mstro-app 0.4.21 → 0.4.24
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 +66 -0
- package/dist/server/cli/headless/claude-invoker-process.js +1 -1
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
- package/dist/server/cli/headless/mcp-config.d.ts +1 -1
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +4 -1
- 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 +1 -0
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +4 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/index.js +9 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +9 -1
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/security-analysis.d.ts +6 -0
- package/dist/server/mcp/security-analysis.d.ts.map +1 -1
- package/dist/server/mcp/security-analysis.js +16 -1
- package/dist/server/mcp/security-analysis.js.map +1 -1
- package/dist/server/mcp/security-patterns.d.ts +8 -0
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +47 -2
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/services/deploy/ai-broker.d.ts +63 -0
- package/dist/server/services/deploy/ai-broker.d.ts.map +1 -0
- package/dist/server/services/deploy/ai-broker.js +360 -0
- package/dist/server/services/deploy/ai-broker.js.map +1 -0
- package/dist/server/services/deploy/board-execution-handler.d.ts +114 -0
- package/dist/server/services/deploy/board-execution-handler.d.ts.map +1 -0
- package/dist/server/services/deploy/board-execution-handler.js +621 -0
- package/dist/server/services/deploy/board-execution-handler.js.map +1 -0
- package/dist/server/services/deploy/credentials.d.ts +35 -0
- package/dist/server/services/deploy/credentials.d.ts.map +1 -0
- package/dist/server/services/deploy/credentials.js +177 -0
- package/dist/server/services/deploy/credentials.js.map +1 -0
- package/dist/server/services/deploy/deploy-ai-service.d.ts +107 -0
- package/dist/server/services/deploy/deploy-ai-service.d.ts.map +1 -0
- package/dist/server/services/deploy/deploy-ai-service.js +294 -0
- package/dist/server/services/deploy/deploy-ai-service.js.map +1 -0
- package/dist/server/services/deploy/headless-session-handler.d.ts +94 -0
- package/dist/server/services/deploy/headless-session-handler.d.ts.map +1 -0
- package/dist/server/services/deploy/headless-session-handler.js +274 -0
- package/dist/server/services/deploy/headless-session-handler.js.map +1 -0
- package/dist/server/services/file-explorer-ops.d.ts.map +1 -1
- package/dist/server/services/file-explorer-ops.js +2 -6
- package/dist/server/services/file-explorer-ops.js.map +1 -1
- package/dist/server/services/plan/agent-loader.d.ts +10 -0
- package/dist/server/services/plan/agent-loader.d.ts.map +1 -0
- package/dist/server/services/plan/agent-loader.js +65 -0
- package/dist/server/services/plan/agent-loader.js.map +1 -0
- package/dist/server/services/plan/composer.js +1 -1
- package/dist/server/services/plan/dependency-resolver.d.ts +1 -1
- package/dist/server/services/plan/dependency-resolver.js +2 -2
- package/dist/server/services/plan/dependency-resolver.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts +6 -2
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +21 -6
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/front-matter.d.ts +5 -0
- package/dist/server/services/plan/front-matter.d.ts.map +1 -1
- package/dist/server/services/plan/front-matter.js +19 -0
- package/dist/server/services/plan/front-matter.js.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.d.ts +1 -1
- package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.js +1 -1
- package/dist/server/services/plan/issue-retry.d.ts +2 -0
- package/dist/server/services/plan/issue-retry.d.ts.map +1 -1
- package/dist/server/services/plan/issue-retry.js +1 -0
- package/dist/server/services/plan/issue-retry.js.map +1 -1
- package/dist/server/services/plan/output-manager.d.ts +2 -2
- package/dist/server/services/plan/output-manager.js +2 -2
- package/dist/server/services/plan/parser-core.d.ts +1 -1
- package/dist/server/services/plan/parser-core.js +1 -1
- package/dist/server/services/plan/parser-core.js.map +1 -1
- package/dist/server/services/plan/parser-migration.d.ts +2 -2
- package/dist/server/services/plan/parser-migration.d.ts.map +1 -1
- package/dist/server/services/plan/parser-migration.js +5 -5
- package/dist/server/services/plan/parser-migration.js.map +1 -1
- package/dist/server/services/plan/parser.d.ts.map +1 -1
- package/dist/server/services/plan/parser.js +4 -7
- package/dist/server/services/plan/parser.js.map +1 -1
- package/dist/server/services/plan/prompt-builder.d.ts +1 -1
- package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.d.ts +4 -0
- package/dist/server/services/plan/review-gate.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.js +89 -34
- package/dist/server/services/plan/review-gate.js.map +1 -1
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
- package/dist/server/services/plan/state-reconciler.js +21 -11
- package/dist/server/services/plan/state-reconciler.js.map +1 -1
- package/dist/server/services/plan/types.d.ts +2 -2
- package/dist/server/services/plan/types.d.ts.map +1 -1
- package/dist/server/services/plan/watcher.js +1 -1
- package/dist/server/services/sentry.d.ts.map +1 -1
- package/dist/server/services/sentry.js +8 -4
- package/dist/server/services/sentry.js.map +1 -1
- package/dist/server/services/websocket/deploy-handlers.d.ts +14 -0
- package/dist/server/services/websocket/deploy-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/deploy-handlers.js +409 -0
- package/dist/server/services/websocket/deploy-handlers.js.map +1 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +4 -0
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/handlers/deploy-handlers.d.ts +11 -0
- package/dist/server/services/websocket/handlers/deploy-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/handlers/deploy-handlers.js +181 -0
- package/dist/server/services/websocket/handlers/deploy-handlers.js.map +1 -0
- package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.js +54 -1
- package/dist/server/services/websocket/plan-board-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 +3 -4
- package/dist/server/services/websocket/plan-helpers.js.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.js +5 -1
- package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js +3 -11
- package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +264 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/server/cli/headless/claude-invoker-process.ts +1 -1
- package/server/cli/headless/mcp-config.ts +4 -1
- package/server/cli/headless/runner.ts +1 -0
- package/server/cli/headless/types.ts +4 -1
- package/server/index.ts +9 -1
- package/server/mcp/bouncer-integration.ts +9 -1
- package/server/mcp/security-analysis.ts +19 -0
- package/server/mcp/security-patterns.ts +53 -2
- package/server/services/deploy/ai-broker.ts +512 -0
- package/server/services/deploy/board-execution-handler.ts +847 -0
- package/server/services/deploy/credentials.ts +200 -0
- package/server/services/deploy/deploy-ai-service.ts +401 -0
- package/server/services/deploy/headless-session-handler.ts +415 -0
- package/server/services/file-explorer-ops.ts +2 -6
- package/server/services/plan/agent-loader.ts +73 -0
- package/server/services/plan/agents/review-code.md +28 -0
- package/server/services/plan/agents/review-custom.md +27 -0
- package/server/services/plan/agents/review-quality.md +42 -0
- package/server/services/plan/composer.ts +1 -1
- package/server/services/plan/dependency-resolver.ts +2 -2
- package/server/services/plan/executor.ts +21 -6
- package/server/services/plan/front-matter.ts +23 -0
- package/server/services/plan/issue-prompt-builder.ts +2 -2
- package/server/services/plan/issue-retry.ts +3 -0
- package/server/services/plan/output-manager.ts +2 -2
- package/server/services/plan/parser-core.ts +2 -2
- package/server/services/plan/parser-migration.ts +5 -5
- package/server/services/plan/parser.ts +4 -5
- package/server/services/plan/prompt-builder.ts +1 -1
- package/server/services/plan/review-gate.ts +104 -33
- package/server/services/plan/state-reconciler.ts +21 -11
- package/server/services/plan/types.ts +3 -3
- package/server/services/plan/watcher.ts +1 -1
- package/server/services/sentry.ts +8 -4
- package/server/services/websocket/deploy-handlers.ts +544 -0
- package/server/services/websocket/handler.ts +5 -1
- package/server/services/websocket/handlers/deploy-handlers.ts +231 -0
- package/server/services/websocket/plan-board-handlers.ts +53 -1
- package/server/services/websocket/plan-helpers.ts +3 -4
- package/server/services/websocket/plan-issue-handlers.ts +6 -1
- package/server/services/websocket/plan-sprint-handlers.ts +3 -9
- package/server/services/websocket/types.ts +333 -2
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* - Sensitive operations (system paths, credentials) get AI review with context
|
|
13
13
|
* - The question is: "Does this operation make sense given user intent?"
|
|
14
14
|
*
|
|
15
|
+
* SECURITY NOTE: These patterns are public (open-source repo). Pattern
|
|
16
|
+
* visibility is intentional and follows industry practice (OWASP CRS,
|
|
17
|
+
* Snort, ModSecurity all publish rules publicly). The Haiku AI layer
|
|
18
|
+
* in bouncer-integration.ts is the actual security boundary — these
|
|
19
|
+
* patterns are a performance optimization for the fast path only.
|
|
20
|
+
*
|
|
15
21
|
* Analysis logic (requiresAIReview, classifyRisk) lives in security-analysis.ts
|
|
16
22
|
* and is re-exported here for backward compatibility.
|
|
17
23
|
*/
|
|
@@ -25,7 +31,7 @@ export { classifyRisk, isSensitivePath, requiresAIReview } from './security-anal
|
|
|
25
31
|
export const SENSITIVE_PATHS = [
|
|
26
32
|
// System directories - might be legitimate (e.g., user asked to configure something)
|
|
27
33
|
{ pattern: /^(Write|Edit):\s*\/etc\//i, reason: 'System configuration - verify user intent' },
|
|
28
|
-
{ pattern: /^(Write|Edit):\s*\/(bin|sbin|usr\/bin|usr\/sbin)\//i, reason: 'System binaries - verify user intent' },
|
|
34
|
+
{ pattern: /^(Write|Edit):\s*\/(bin|sbin|usr\/bin|usr\/sbin|usr\/local\/bin|usr\/local\/sbin)\//i, reason: 'System binaries - verify user intent' },
|
|
29
35
|
{ pattern: /^(Write|Edit):\s*\/boot\//i, reason: 'Boot directory - verify user intent' },
|
|
30
36
|
{ pattern: /^(Write|Edit):\s*\/root\//i, reason: 'Root home - verify user intent' },
|
|
31
37
|
{ pattern: /^(Write|Edit):\s*\/System\//i, reason: 'macOS system - verify user intent' },
|
|
@@ -51,7 +57,7 @@ export const SENSITIVE_PATHS = [
|
|
|
51
57
|
*/
|
|
52
58
|
export const CRITICAL_THREATS = [
|
|
53
59
|
{
|
|
54
|
-
pattern: /rm\s+-rf\s+(\/|~)($|\s)/i,
|
|
60
|
+
pattern: /rm\s+-rf\s+(\/|~)($|\s|;|&&|\|\|)/i,
|
|
55
61
|
reason: 'Deleting root (/) or home (~) directory is never a legitimate dev task'
|
|
56
62
|
},
|
|
57
63
|
{
|
|
@@ -205,6 +211,45 @@ export const NEEDS_AI_REVIEW = [
|
|
|
205
211
|
// MCP server manipulation
|
|
206
212
|
{ pattern: /\bclaude\b.*\bmcp\b.*\badd\b/i, reason: 'Adding MCP server - verify source is trusted' },
|
|
207
213
|
];
|
|
214
|
+
/**
|
|
215
|
+
* Deploy-specific patterns — additional restrictions for board/headless executions
|
|
216
|
+
* triggered by end-user prompts (untrusted input from deployed app users).
|
|
217
|
+
*
|
|
218
|
+
* These catch operations that are unusual in an automated board execution context
|
|
219
|
+
* and should be routed to AI review. They supplement NEEDS_AI_REVIEW above.
|
|
220
|
+
*/
|
|
221
|
+
export const DEPLOY_PATTERNS = [
|
|
222
|
+
// Credential access from deploy context is suspicious
|
|
223
|
+
{
|
|
224
|
+
pattern: /\b(cat|head|tail|less|more)\b.*\.(env|pem|key|crt|p12|pfx|jks)\b/i,
|
|
225
|
+
reason: 'Deploy execution reading credential/certificate files — verify intent'
|
|
226
|
+
},
|
|
227
|
+
// Git push from deploy context — end-user code should not push upstream
|
|
228
|
+
{
|
|
229
|
+
pattern: /\bgit\b.*\bpush\b/i,
|
|
230
|
+
reason: 'Deploy execution attempting git push — verify this is intended'
|
|
231
|
+
},
|
|
232
|
+
// Network listeners — deploy executions should not open ports
|
|
233
|
+
{
|
|
234
|
+
pattern: /\b(nc|netcat|ncat|socat)\b.*(\blisten\b|\s-l\b)/i,
|
|
235
|
+
reason: 'Deploy execution opening network listener — potential backdoor'
|
|
236
|
+
},
|
|
237
|
+
// Process manipulation from deploy context
|
|
238
|
+
{
|
|
239
|
+
pattern: /\b(kill|killall|pkill)\b/i,
|
|
240
|
+
reason: 'Deploy execution attempting process termination — verify intent'
|
|
241
|
+
},
|
|
242
|
+
// SSH/SCP from deploy context
|
|
243
|
+
{
|
|
244
|
+
pattern: /\b(ssh|scp|sftp)\b.*@/i,
|
|
245
|
+
reason: 'Deploy execution initiating SSH/SCP connection — verify intent'
|
|
246
|
+
},
|
|
247
|
+
// Cron/systemd manipulation
|
|
248
|
+
{
|
|
249
|
+
pattern: /\b(crontab|systemctl|launchctl)\b/i,
|
|
250
|
+
reason: 'Deploy execution manipulating scheduled tasks — verify intent'
|
|
251
|
+
},
|
|
252
|
+
];
|
|
208
253
|
// ── Utility functions ─────────────────────────────────────────
|
|
209
254
|
/**
|
|
210
255
|
* Check if operation matches any pattern in array
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-patterns.js","sourceRoot":"","sources":["../../../server/mcp/security-patterns.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE
|
|
1
|
+
{"version":3,"file":"security-patterns.js","sourceRoot":"","sources":["../../../server/mcp/security-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { type HealthUpdateData, type UsageReportData } from './headless-session-handler.js';
|
|
3
|
+
export interface AiBrokerInvokeBody {
|
|
4
|
+
capability: 'headless' | 'pm-board';
|
|
5
|
+
deploymentId: string;
|
|
6
|
+
endUserId: string;
|
|
7
|
+
prompt: string;
|
|
8
|
+
boardTemplateId?: string;
|
|
9
|
+
systemPrompt?: string;
|
|
10
|
+
allowedTools?: string[];
|
|
11
|
+
model?: string;
|
|
12
|
+
}
|
|
13
|
+
interface DeployTokenRecord {
|
|
14
|
+
deploymentId: string;
|
|
15
|
+
tokenHash: string;
|
|
16
|
+
capabilities: ('headless' | 'pm-board')[];
|
|
17
|
+
rateLimit: {
|
|
18
|
+
maxRequestsPerMinute: number | null;
|
|
19
|
+
maxConcurrentSessions: number;
|
|
20
|
+
};
|
|
21
|
+
aiConfig: {
|
|
22
|
+
aiEnabled: boolean;
|
|
23
|
+
defaultSystemPrompt: string | null;
|
|
24
|
+
defaultModel: string;
|
|
25
|
+
maxTokensPerRequest: number | null;
|
|
26
|
+
workingDir: string;
|
|
27
|
+
allowedBoardTemplateIds: string[];
|
|
28
|
+
maxConcurrentBoardExecutions: number;
|
|
29
|
+
maxBoardExecutionsPerMinute: number | null;
|
|
30
|
+
};
|
|
31
|
+
/** When set, the deployment requires payment and this URL is returned on 402 */
|
|
32
|
+
paymentUrl?: string;
|
|
33
|
+
/** Whether the deployment is currently active */
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function registerDeployToken(record: DeployTokenRecord): void;
|
|
37
|
+
export declare function unregisterDeployToken(deploymentId: string): void;
|
|
38
|
+
export declare function getDeployTokenRecord(deploymentId: string): DeployTokenRecord | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Update rate limit and AI config on an existing deploy token record.
|
|
41
|
+
* Called when the server syncs updated deployment config to the CLI.
|
|
42
|
+
*/
|
|
43
|
+
export declare function updateDeployTokenConfig(deploymentId: string, updates: {
|
|
44
|
+
maxRequestsPerMinute?: number | null;
|
|
45
|
+
maxConcurrentSessions?: number;
|
|
46
|
+
maxTokensPerRequest?: number | null;
|
|
47
|
+
aiEnabled?: boolean;
|
|
48
|
+
}): boolean;
|
|
49
|
+
type UsageReportListener = (report: UsageReportData) => void;
|
|
50
|
+
type HealthUpdateListener = (update: HealthUpdateData) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Register a listener for deploy usage reports.
|
|
53
|
+
* Called from the server setup to wire usage reports to the platform connection.
|
|
54
|
+
*/
|
|
55
|
+
export declare function setDeployUsageReportListener(listener: UsageReportListener): void;
|
|
56
|
+
/**
|
|
57
|
+
* Register a listener for deploy AI health updates.
|
|
58
|
+
* Called from the server setup to wire health updates to the platform connection.
|
|
59
|
+
*/
|
|
60
|
+
export declare function setDeployHealthUpdateListener(listener: HealthUpdateListener): void;
|
|
61
|
+
export declare function createAiBrokerRoutes(): Hono;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=ai-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-broker.d.ts","sourceRoot":"","sources":["../../../../server/services/deploy/ai-broker.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAgB,IAAI,EAAE,MAAM,MAAM,CAAC;AAO1C,OAAO,EAGL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAIvC,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,UAAU,GAAG,UAAU,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,iBAAiB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,CAAC,UAAU,GAAG,UAAU,CAAC,EAAE,CAAC;IAC1C,SAAS,EAAE;QACT,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,qBAAqB,EAAE,MAAM,CAAC;KAC/B,CAAC;IACF,QAAQ,EAAE;QACR,SAAS,EAAE,OAAO,CAAC;QACnB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,YAAY,EAAE,MAAM,CAAC;QACrB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,UAAU,EAAE,MAAM,CAAC;QACnB,uBAAuB,EAAE,MAAM,EAAE,CAAC;QAClC,4BAA4B,EAAE,MAAM,CAAC;QACrC,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5C,CAAC;IACF,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;CAClB;AAWD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAEnE;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAEhE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAExF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE;IACP,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GACA,OAAO,CAkBT;AAID,KAAK,mBAAmB,GAAG,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;AAC7D,KAAK,oBAAoB,GAAG,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;AAK/D;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAEhF;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAElF;AA8ID,wBAAgB,oBAAoB,IAAI,IAAI,CAoD3C"}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* AI Broker — HTTP endpoint for developer backends to invoke AI execution.
|
|
5
|
+
*
|
|
6
|
+
* POST /api/deploy/ai/invoke
|
|
7
|
+
* Accepts { capability, deploymentId, endUserId, prompt, ... }
|
|
8
|
+
* Authorization: Bearer <deploy-token>
|
|
9
|
+
*
|
|
10
|
+
* GET /api/deploy/ai/jobs/:jobId
|
|
11
|
+
* Poll board execution status.
|
|
12
|
+
*
|
|
13
|
+
* Deploy tokens are per-deployment. The CLI stores the SHA-256 hash; the
|
|
14
|
+
* developer's backend sends the raw token. We hash the incoming token and
|
|
15
|
+
* compare against the stored hash.
|
|
16
|
+
*
|
|
17
|
+
* Headless sessions return SSE (text/event-stream).
|
|
18
|
+
* Board executions return { jobId, statusUrl } immediately.
|
|
19
|
+
*/
|
|
20
|
+
import { createHash } from 'node:crypto';
|
|
21
|
+
import { Hono } from 'hono';
|
|
22
|
+
import { streamSSE } from 'hono/streaming';
|
|
23
|
+
import { getBoardExecutionStatus, startBoardExecution, } from './board-execution-handler.js';
|
|
24
|
+
import { handleHeadlessSession, } from './headless-session-handler.js';
|
|
25
|
+
// ========== Token Store ==========
|
|
26
|
+
/**
|
|
27
|
+
* In-memory store for deploy tokens. Populated when deployments are created
|
|
28
|
+
* via the WebSocket handlers. Each entry maps a deployment ID to its
|
|
29
|
+
* hashed token and configuration.
|
|
30
|
+
*/
|
|
31
|
+
const tokenStore = new Map();
|
|
32
|
+
export function registerDeployToken(record) {
|
|
33
|
+
tokenStore.set(record.deploymentId, record);
|
|
34
|
+
}
|
|
35
|
+
export function unregisterDeployToken(deploymentId) {
|
|
36
|
+
tokenStore.delete(deploymentId);
|
|
37
|
+
}
|
|
38
|
+
export function getDeployTokenRecord(deploymentId) {
|
|
39
|
+
return tokenStore.get(deploymentId);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Update rate limit and AI config on an existing deploy token record.
|
|
43
|
+
* Called when the server syncs updated deployment config to the CLI.
|
|
44
|
+
*/
|
|
45
|
+
export function updateDeployTokenConfig(deploymentId, updates) {
|
|
46
|
+
const record = tokenStore.get(deploymentId);
|
|
47
|
+
if (!record)
|
|
48
|
+
return false;
|
|
49
|
+
if (updates.maxRequestsPerMinute !== undefined) {
|
|
50
|
+
record.rateLimit.maxRequestsPerMinute = updates.maxRequestsPerMinute;
|
|
51
|
+
}
|
|
52
|
+
if (updates.maxConcurrentSessions !== undefined) {
|
|
53
|
+
record.rateLimit.maxConcurrentSessions = updates.maxConcurrentSessions;
|
|
54
|
+
}
|
|
55
|
+
if (updates.maxTokensPerRequest !== undefined) {
|
|
56
|
+
record.aiConfig.maxTokensPerRequest = updates.maxTokensPerRequest;
|
|
57
|
+
}
|
|
58
|
+
if (updates.aiEnabled !== undefined) {
|
|
59
|
+
record.aiConfig.aiEnabled = updates.aiEnabled;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
let usageReportListener = null;
|
|
64
|
+
let healthUpdateListener = null;
|
|
65
|
+
/**
|
|
66
|
+
* Register a listener for deploy usage reports.
|
|
67
|
+
* Called from the server setup to wire usage reports to the platform connection.
|
|
68
|
+
*/
|
|
69
|
+
export function setDeployUsageReportListener(listener) {
|
|
70
|
+
usageReportListener = listener;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Register a listener for deploy AI health updates.
|
|
74
|
+
* Called from the server setup to wire health updates to the platform connection.
|
|
75
|
+
*/
|
|
76
|
+
export function setDeployHealthUpdateListener(listener) {
|
|
77
|
+
healthUpdateListener = listener;
|
|
78
|
+
}
|
|
79
|
+
// ========== Token Validation ==========
|
|
80
|
+
function hashToken(token) {
|
|
81
|
+
return createHash('sha256').update(token).digest('hex');
|
|
82
|
+
}
|
|
83
|
+
function extractBearerToken(authHeader) {
|
|
84
|
+
if (!authHeader)
|
|
85
|
+
return null;
|
|
86
|
+
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
87
|
+
return match ? match[1] : null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Validate a deploy token against the stored hash.
|
|
91
|
+
* Returns the token record if valid, null otherwise.
|
|
92
|
+
*/
|
|
93
|
+
function validateDeployToken(rawToken, deploymentId) {
|
|
94
|
+
const record = tokenStore.get(deploymentId);
|
|
95
|
+
if (!record)
|
|
96
|
+
return null;
|
|
97
|
+
const incomingHash = hashToken(rawToken);
|
|
98
|
+
if (incomingHash !== record.tokenHash)
|
|
99
|
+
return null;
|
|
100
|
+
return record;
|
|
101
|
+
}
|
|
102
|
+
const brokerRateBuckets = new Map();
|
|
103
|
+
function getBucket(key) {
|
|
104
|
+
let bucket = brokerRateBuckets.get(key);
|
|
105
|
+
if (!bucket) {
|
|
106
|
+
bucket = { timestamps: [], activeSessions: 0 };
|
|
107
|
+
brokerRateBuckets.set(key, bucket);
|
|
108
|
+
}
|
|
109
|
+
return bucket;
|
|
110
|
+
}
|
|
111
|
+
function pruneTimestamps(bucket) {
|
|
112
|
+
const oneMinuteAgo = Date.now() - 60_000;
|
|
113
|
+
while (bucket.timestamps.length > 0 && bucket.timestamps[0] < oneMinuteAgo) {
|
|
114
|
+
bucket.timestamps.shift();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function checkBrokerRateLimit(record) {
|
|
118
|
+
const bucket = getBucket(record.deploymentId);
|
|
119
|
+
if (bucket.activeSessions >= record.rateLimit.maxConcurrentSessions) {
|
|
120
|
+
return { limited: true, retryAfterMs: 5_000 };
|
|
121
|
+
}
|
|
122
|
+
if (record.rateLimit.maxRequestsPerMinute !== null) {
|
|
123
|
+
pruneTimestamps(bucket);
|
|
124
|
+
if (bucket.timestamps.length >= record.rateLimit.maxRequestsPerMinute) {
|
|
125
|
+
// Calculate retry-after based on oldest timestamp expiry
|
|
126
|
+
const oldestTs = bucket.timestamps[0];
|
|
127
|
+
const retryAfterMs = oldestTs + 60_000 - Date.now();
|
|
128
|
+
return { limited: true, retryAfterMs: Math.max(1_000, retryAfterMs) };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { limited: false };
|
|
132
|
+
}
|
|
133
|
+
function recordBrokerRequestStart(deploymentId) {
|
|
134
|
+
const bucket = getBucket(deploymentId);
|
|
135
|
+
bucket.timestamps.push(Date.now());
|
|
136
|
+
bucket.activeSessions++;
|
|
137
|
+
}
|
|
138
|
+
function recordBrokerRequestEnd(deploymentId) {
|
|
139
|
+
const bucket = getBucket(deploymentId);
|
|
140
|
+
bucket.activeSessions = Math.max(0, bucket.activeSessions - 1);
|
|
141
|
+
}
|
|
142
|
+
function validateBody(body) {
|
|
143
|
+
if (!body.capability || !body.deploymentId || !body.endUserId || !body.prompt) {
|
|
144
|
+
return 'Missing required fields: capability, deploymentId, endUserId, prompt';
|
|
145
|
+
}
|
|
146
|
+
if (body.capability !== 'headless' && body.capability !== 'pm-board') {
|
|
147
|
+
return "Invalid capability. Must be 'headless' or 'pm-board'";
|
|
148
|
+
}
|
|
149
|
+
if (body.capability === 'pm-board' && !body.boardTemplateId) {
|
|
150
|
+
return "boardTemplateId is required when capability is 'pm-board'";
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function validateTokenAndConfig(rawToken, body) {
|
|
155
|
+
const record = validateDeployToken(rawToken, body.deploymentId);
|
|
156
|
+
if (!record) {
|
|
157
|
+
return { ok: false, error: 'Invalid deploy token', status: 401 };
|
|
158
|
+
}
|
|
159
|
+
if (!record.enabled) {
|
|
160
|
+
return { ok: false, error: 'Deployment is disabled', status: 403 };
|
|
161
|
+
}
|
|
162
|
+
if (!record.aiConfig.aiEnabled) {
|
|
163
|
+
return { ok: false, error: 'AI features are not enabled for this deployment', status: 403 };
|
|
164
|
+
}
|
|
165
|
+
if (!record.capabilities.includes(body.capability)) {
|
|
166
|
+
return { ok: false, error: `Capability '${body.capability}' is not enabled for this deployment`, status: 403 };
|
|
167
|
+
}
|
|
168
|
+
const rateCheck = checkBrokerRateLimit(record);
|
|
169
|
+
if (rateCheck.limited) {
|
|
170
|
+
const retryAfterSec = Math.ceil((rateCheck.retryAfterMs ?? 5_000) / 1_000);
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
error: 'Rate limit exceeded. Try again later.',
|
|
174
|
+
status: 429,
|
|
175
|
+
headers: { 'Retry-After': String(retryAfterSec) },
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return { ok: true, body, record };
|
|
179
|
+
}
|
|
180
|
+
// ========== Route Factory ==========
|
|
181
|
+
export function createAiBrokerRoutes() {
|
|
182
|
+
const routes = new Hono();
|
|
183
|
+
// ── POST /invoke — trigger AI execution ────────────────────
|
|
184
|
+
routes.post('/invoke', async (c) => {
|
|
185
|
+
const rawToken = extractBearerToken(c.req.header('Authorization'));
|
|
186
|
+
if (!rawToken) {
|
|
187
|
+
return c.json({ error: 'Missing or malformed Authorization header. Expected: Bearer <deploy-token>' }, 401);
|
|
188
|
+
}
|
|
189
|
+
let body;
|
|
190
|
+
try {
|
|
191
|
+
body = await c.req.json();
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return c.json({ error: 'Invalid JSON body' }, 400);
|
|
195
|
+
}
|
|
196
|
+
const bodyError = validateBody(body);
|
|
197
|
+
if (bodyError) {
|
|
198
|
+
return c.json({ error: bodyError }, 400);
|
|
199
|
+
}
|
|
200
|
+
const validation = validateTokenAndConfig(rawToken, body);
|
|
201
|
+
if (!validation.ok) {
|
|
202
|
+
return c.json({ error: validation.error }, { status: validation.status, headers: validation.headers });
|
|
203
|
+
}
|
|
204
|
+
if (body.capability === 'headless') {
|
|
205
|
+
return handleHeadlessInvoke(c, body, validation.record);
|
|
206
|
+
}
|
|
207
|
+
return handleBoardInvoke(c, body, validation.record);
|
|
208
|
+
});
|
|
209
|
+
// ── GET /jobs/:jobId — poll board execution status ─────────
|
|
210
|
+
routes.get('/jobs/:jobId', (c) => {
|
|
211
|
+
const { jobId } = c.req.param();
|
|
212
|
+
const endUserId = c.req.query('endUserId');
|
|
213
|
+
const status = getBoardExecutionStatus(jobId, endUserId ?? undefined);
|
|
214
|
+
if (!status) {
|
|
215
|
+
return c.json({ error: 'Job not found' }, 404);
|
|
216
|
+
}
|
|
217
|
+
return c.json(status);
|
|
218
|
+
});
|
|
219
|
+
return routes;
|
|
220
|
+
}
|
|
221
|
+
// ========== Headless Dispatch ==========
|
|
222
|
+
async function handleHeadlessInvoke(c, body, record) {
|
|
223
|
+
const config = {
|
|
224
|
+
deploymentId: record.deploymentId,
|
|
225
|
+
aiEnabled: record.aiConfig.aiEnabled,
|
|
226
|
+
allowedAiCapabilities: record.capabilities,
|
|
227
|
+
maxTokensPerRequest: record.aiConfig.maxTokensPerRequest,
|
|
228
|
+
maxRequestsPerMinute: record.rateLimit.maxRequestsPerMinute,
|
|
229
|
+
maxConcurrentSessions: record.rateLimit.maxConcurrentSessions,
|
|
230
|
+
defaultSystemPrompt: record.aiConfig.defaultSystemPrompt,
|
|
231
|
+
defaultModel: record.aiConfig.defaultModel,
|
|
232
|
+
workingDir: record.aiConfig.workingDir,
|
|
233
|
+
};
|
|
234
|
+
recordBrokerRequestStart(record.deploymentId);
|
|
235
|
+
// Stream headless session output as SSE
|
|
236
|
+
return streamSSE(c, async (stream) => {
|
|
237
|
+
let resultSent = false;
|
|
238
|
+
const callbacks = {
|
|
239
|
+
onOutput: (text) => {
|
|
240
|
+
stream.writeSSE({ event: 'output', data: text }).catch(() => { });
|
|
241
|
+
},
|
|
242
|
+
onThinking: (text) => {
|
|
243
|
+
stream.writeSSE({ event: 'thinking', data: text }).catch(() => { });
|
|
244
|
+
},
|
|
245
|
+
onToolUse: (event) => {
|
|
246
|
+
stream.writeSSE({ event: 'tool_use', data: JSON.stringify(event) }).catch(() => { });
|
|
247
|
+
},
|
|
248
|
+
onUsageReport: (report) => {
|
|
249
|
+
usageReportListener?.(report);
|
|
250
|
+
},
|
|
251
|
+
onHealthUpdate: (update) => {
|
|
252
|
+
healthUpdateListener?.(update);
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
const result = await handleHeadlessSession({
|
|
257
|
+
prompt: body.prompt,
|
|
258
|
+
systemPrompt: body.systemPrompt,
|
|
259
|
+
allowedTools: body.allowedTools,
|
|
260
|
+
model: body.model,
|
|
261
|
+
endUserId: body.endUserId,
|
|
262
|
+
}, config, callbacks);
|
|
263
|
+
if (result.ok) {
|
|
264
|
+
await stream.writeSSE({
|
|
265
|
+
event: 'done',
|
|
266
|
+
data: JSON.stringify({
|
|
267
|
+
sessionId: result.result.sessionId,
|
|
268
|
+
completed: result.result.completed,
|
|
269
|
+
totalTokens: result.result.totalTokens,
|
|
270
|
+
durationMs: result.result.durationMs,
|
|
271
|
+
}),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// Map error codes to appropriate SSE error events
|
|
276
|
+
const statusCode = mapErrorCodeToStatus(result.error.code);
|
|
277
|
+
const errorData = {
|
|
278
|
+
code: result.error.code,
|
|
279
|
+
message: result.error.message,
|
|
280
|
+
statusCode,
|
|
281
|
+
};
|
|
282
|
+
if (statusCode === 402 && record.paymentUrl) {
|
|
283
|
+
errorData.paymentUrl = record.paymentUrl;
|
|
284
|
+
}
|
|
285
|
+
await stream.writeSSE({
|
|
286
|
+
event: 'error',
|
|
287
|
+
data: JSON.stringify(errorData),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
resultSent = true;
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (!resultSent) {
|
|
294
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
295
|
+
await stream.writeSSE({
|
|
296
|
+
event: 'error',
|
|
297
|
+
data: JSON.stringify({ code: 'EXECUTION_FAILED', message }),
|
|
298
|
+
}).catch(() => { });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
recordBrokerRequestEnd(record.deploymentId);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
// ========== Board Dispatch ==========
|
|
307
|
+
function handleBoardInvoke(c, body, record) {
|
|
308
|
+
const config = {
|
|
309
|
+
deploymentId: record.deploymentId,
|
|
310
|
+
aiEnabled: record.aiConfig.aiEnabled,
|
|
311
|
+
allowedAiCapabilities: record.capabilities.map((cap) => cap === 'pm-board' ? 'board-execution' : cap),
|
|
312
|
+
allowedBoardTemplateIds: record.aiConfig.allowedBoardTemplateIds,
|
|
313
|
+
maxConcurrentBoardExecutions: record.aiConfig.maxConcurrentBoardExecutions,
|
|
314
|
+
maxBoardExecutionsPerMinute: record.aiConfig.maxBoardExecutionsPerMinute,
|
|
315
|
+
defaultModel: record.aiConfig.defaultModel,
|
|
316
|
+
workingDir: record.aiConfig.workingDir,
|
|
317
|
+
};
|
|
318
|
+
const result = startBoardExecution({
|
|
319
|
+
boardTemplateId: body.boardTemplateId,
|
|
320
|
+
endUserPrompt: body.prompt,
|
|
321
|
+
endUserId: body.endUserId,
|
|
322
|
+
deploymentId: body.deploymentId,
|
|
323
|
+
}, config);
|
|
324
|
+
if (!result.ok) {
|
|
325
|
+
const statusCode = mapErrorCodeToStatus(result.error.code);
|
|
326
|
+
const body = { error: result.error.message, code: result.error.code };
|
|
327
|
+
if (statusCode === 402 && record.paymentUrl) {
|
|
328
|
+
body.paymentUrl = record.paymentUrl;
|
|
329
|
+
}
|
|
330
|
+
return c.json(body, statusCode);
|
|
331
|
+
}
|
|
332
|
+
// Construct polling URL relative to the request
|
|
333
|
+
const host = c.req.header('Host') || 'localhost';
|
|
334
|
+
const protocol = c.req.header('X-Forwarded-Proto') || 'http';
|
|
335
|
+
const statusUrl = `${protocol}://${host}/api/deploy/ai/jobs/${result.jobId}`;
|
|
336
|
+
return c.json({
|
|
337
|
+
jobId: result.jobId,
|
|
338
|
+
statusUrl,
|
|
339
|
+
}, 202);
|
|
340
|
+
}
|
|
341
|
+
// ========== Error Mapping ==========
|
|
342
|
+
function mapErrorCodeToStatus(code) {
|
|
343
|
+
switch (code) {
|
|
344
|
+
case 'CAPABILITY_DENIED':
|
|
345
|
+
case 'AI_DISABLED':
|
|
346
|
+
return 403;
|
|
347
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
348
|
+
case 'CONCURRENT_LIMIT_EXCEEDED':
|
|
349
|
+
return 429;
|
|
350
|
+
case 'INVALID_REQUEST':
|
|
351
|
+
case 'INVALID_BOARD_TEMPLATE':
|
|
352
|
+
case 'BOARD_TEMPLATE_NOT_FOUND':
|
|
353
|
+
return 400;
|
|
354
|
+
case 'PAYMENT_REQUIRED':
|
|
355
|
+
return 402;
|
|
356
|
+
default:
|
|
357
|
+
return 500;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
//# sourceMappingURL=ai-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-broker.js","sourceRoot":"","sources":["../../../../server/services/deploy/ai-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize an end-user prompt before passing it to the AI.
|
|
3
|
+
*
|
|
4
|
+
* SECURITY: End-user prompts are untrusted. This function:
|
|
5
|
+
* 1. Strips system instruction XML delimiters to prevent prompt escape
|
|
6
|
+
* 2. Removes null bytes and zero-width characters used for evasion
|
|
7
|
+
* 3. Truncates to MAX_END_USER_PROMPT_LENGTH
|
|
8
|
+
*
|
|
9
|
+
* Note: This does NOT strip tool-use instructions or path traversal text —
|
|
10
|
+
* those are handled by the Security Bouncer (tool execution level) and
|
|
11
|
+
* the isolated working directory (filesystem level).
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeEndUserPrompt(prompt: string): string;
|
|
14
|
+
export interface BoardExecutionRequest {
|
|
15
|
+
/** Board template to execute (must be in deployment's allowedBoardTemplateIds) */
|
|
16
|
+
boardTemplateId: string;
|
|
17
|
+
/** The end user's prompt (untrusted input) */
|
|
18
|
+
endUserPrompt: string;
|
|
19
|
+
/** Unique identifier for the end user (for isolation + rate tracking) */
|
|
20
|
+
endUserId: string;
|
|
21
|
+
/** Deployment that owns this execution */
|
|
22
|
+
deploymentId: string;
|
|
23
|
+
}
|
|
24
|
+
export interface BoardExecutionConfig {
|
|
25
|
+
deploymentId: string;
|
|
26
|
+
aiEnabled: boolean;
|
|
27
|
+
allowedAiCapabilities: string[];
|
|
28
|
+
/** Board template IDs this deployment is allowed to execute */
|
|
29
|
+
allowedBoardTemplateIds: string[];
|
|
30
|
+
/** Max concurrent board executions per deployment */
|
|
31
|
+
maxConcurrentBoardExecutions: number;
|
|
32
|
+
/** Max board executions per minute (null = unlimited) */
|
|
33
|
+
maxBoardExecutionsPerMinute: number | null;
|
|
34
|
+
defaultModel: string;
|
|
35
|
+
workingDir: string;
|
|
36
|
+
}
|
|
37
|
+
export type BoardExecutionErrorCode = 'CAPABILITY_DENIED' | 'AI_DISABLED' | 'INVALID_BOARD_TEMPLATE' | 'BOARD_TEMPLATE_NOT_FOUND' | 'RATE_LIMIT_EXCEEDED' | 'CONCURRENT_LIMIT_EXCEEDED' | 'INVALID_REQUEST' | 'EXECUTION_FAILED';
|
|
38
|
+
export interface BoardExecutionError {
|
|
39
|
+
code: BoardExecutionErrorCode;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
export type BoardExecutionJobStatus = 'customizing' | 'executing' | 'completed' | 'failed' | 'cancelled';
|
|
43
|
+
export interface BoardExecutionProgress {
|
|
44
|
+
phase: 'isolating' | 'customizing' | 'executing' | 'collecting' | 'done';
|
|
45
|
+
issuesTotal: number;
|
|
46
|
+
issuesCompleted: number;
|
|
47
|
+
currentWaveIds: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface BoardExecutionJobResult {
|
|
50
|
+
completed: boolean;
|
|
51
|
+
issuesTotal: number;
|
|
52
|
+
issuesCompleted: number;
|
|
53
|
+
issuesFailed: number;
|
|
54
|
+
/** Output artifact contents keyed by filename */
|
|
55
|
+
outputs: Record<string, string>;
|
|
56
|
+
durationMs: number;
|
|
57
|
+
}
|
|
58
|
+
export interface BoardExecutionStatusResult {
|
|
59
|
+
jobId: string;
|
|
60
|
+
status: BoardExecutionJobStatus;
|
|
61
|
+
progress: BoardExecutionProgress;
|
|
62
|
+
result: BoardExecutionJobResult | null;
|
|
63
|
+
error: string | null;
|
|
64
|
+
}
|
|
65
|
+
export type StartBoardExecutionResult = {
|
|
66
|
+
ok: true;
|
|
67
|
+
jobId: string;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
error: BoardExecutionError;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Start a board execution for an end user. Returns a job ID immediately.
|
|
74
|
+
* The execution runs asynchronously — poll with getBoardExecutionStatus().
|
|
75
|
+
*
|
|
76
|
+
* Validates the deployment config, checks rate limits, verifies the board
|
|
77
|
+
* template exists and is allowed, then launches the background execution.
|
|
78
|
+
*
|
|
79
|
+
* @returns Structured result with either the job ID or an error.
|
|
80
|
+
*/
|
|
81
|
+
export declare function startBoardExecution(request: BoardExecutionRequest, config: BoardExecutionConfig): StartBoardExecutionResult;
|
|
82
|
+
/**
|
|
83
|
+
* Get the current status of a board execution job.
|
|
84
|
+
*
|
|
85
|
+
* Optionally pass endUserId to enforce isolation — returns null if the
|
|
86
|
+
* job belongs to a different end user.
|
|
87
|
+
*
|
|
88
|
+
* @returns Job status or null if not found / access denied.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getBoardExecutionStatus(jobId: string, endUserId?: string): BoardExecutionStatusResult | null;
|
|
91
|
+
/**
|
|
92
|
+
* Get the current rate limit state for a deployment's board executions.
|
|
93
|
+
* Useful for status/monitoring endpoints.
|
|
94
|
+
*/
|
|
95
|
+
export declare function getDeploymentBoardExecutionState(deploymentId: string): {
|
|
96
|
+
executionsInLastMinute: number;
|
|
97
|
+
activeExecutions: number;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Reset rate limit state for a deployment's board executions.
|
|
101
|
+
* Call when a deployment is deleted.
|
|
102
|
+
*/
|
|
103
|
+
export declare function resetDeploymentBoardExecutionRateLimit(deploymentId: string): void;
|
|
104
|
+
/**
|
|
105
|
+
* Sweep stale isolated directories left behind by crashed executions.
|
|
106
|
+
*
|
|
107
|
+
* Board execution creates temp dirs prefixed with 'mstro-board-exec-'.
|
|
108
|
+
* If the process crashes before cleanup, these dirs leak. This function
|
|
109
|
+
* removes any that are older than the retention window + buffer.
|
|
110
|
+
*
|
|
111
|
+
* Safe to call on startup or periodically.
|
|
112
|
+
*/
|
|
113
|
+
export declare function sweepStaleIsolatedDirs(): number;
|
|
114
|
+
//# sourceMappingURL=board-execution-handler.d.ts.map
|