@thxgg/steward 0.1.16 → 0.1.18
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{CxHOXVf6.js → -z_Gr0GN.js} +1 -1
- package/.output/public/_nuxt/5LlyHjkF.js +60 -0
- package/.output/public/_nuxt/BA0u_CRT.js +1 -0
- package/.output/public/_nuxt/{Bibm_IDv.js → BA4e9-N5.js} +2 -2
- package/.output/public/_nuxt/{BSA0RJ-H.js → BO8EM227.js} +1 -1
- package/.output/public/_nuxt/C0XT5P3Q.js +1 -0
- package/.output/public/_nuxt/{BWVTacYj.js → CGzrvVc6.js} +1 -1
- package/.output/public/_nuxt/{Dum5qplW.js → CJlXUkTg.js} +1 -1
- package/.output/public/_nuxt/CZsXZugv.js +1 -0
- package/.output/public/_nuxt/{ynmyrfyT.js → C_HVaH3B.js} +1 -1
- package/.output/public/_nuxt/{wbjFvimm.js → DAnnHVQP.js} +1 -1
- package/.output/public/_nuxt/DEr8q68O.js +141 -0
- package/.output/public/_nuxt/Detail.DSyVQNdr.css +1 -0
- package/.output/public/_nuxt/DrXxYwWw.js +30 -0
- package/.output/public/_nuxt/QAzsKGuP.js +1 -0
- package/.output/public/_nuxt/TSsR_oCL.js +1 -0
- package/.output/public/_nuxt/WUF6Thhn.js +13 -0
- package/.output/public/_nuxt/_prd_.BkpxMFSV.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/19e0e040-a531-4c25-b46d-a6ca54a1ae3e.json +1 -0
- package/.output/public/_nuxt/i9wn3hS7.js +1 -0
- package/.output/server/chunks/_/git-api.mjs +101 -8
- package/.output/server/chunks/_/git-api.mjs.map +1 -1
- package/.output/server/chunks/_/git.mjs +3 -10
- package/.output/server/chunks/_/git.mjs.map +1 -1
- package/.output/server/chunks/_/prd-service.mjs +366 -0
- package/.output/server/chunks/_/prd-service.mjs.map +1 -0
- package/.output/server/chunks/_/repos.mjs +448 -0
- package/.output/server/chunks/_/repos.mjs.map +1 -0
- package/.output/server/chunks/_/task-graph.mjs +13 -14
- package/.output/server/chunks/_/task-graph.mjs.map +1 -1
- package/.output/server/chunks/_/watcher.mjs +54 -68
- package/.output/server/chunks/_/watcher.mjs.map +1 -1
- package/.output/server/chunks/build/{Detail-CUfU85GY.mjs → Detail-BQSkP9Zm.mjs} +170 -74
- package/.output/server/chunks/build/Detail-BQSkP9Zm.mjs.map +1 -0
- package/.output/server/chunks/build/DiffViewer-styles-1.mjs-BFsE2PCW.mjs +4 -0
- package/.output/server/chunks/build/DiffViewer-styles-1.mjs-BFsE2PCW.mjs.map +1 -0
- package/.output/server/chunks/build/DiffViewer-styles.D2bqX3nK.mjs +8 -0
- package/.output/server/chunks/build/DiffViewer-styles.D2bqX3nK.mjs.map +1 -0
- package/.output/server/chunks/build/DiffViewer-styles.FoV36wuV.mjs +10 -0
- package/.output/server/chunks/build/DiffViewer-styles.FoV36wuV.mjs.map +1 -0
- package/.output/server/chunks/build/Viewer-styles.D6wYWFb1.mjs +8 -0
- package/.output/server/chunks/build/Viewer-styles.D6wYWFb1.mjs.map +1 -0
- package/.output/server/chunks/build/{_prd_-CeVnQzOV.mjs → _prd_-CBR_wm9i.mjs} +73 -33
- package/.output/server/chunks/build/_prd_-CBR_wm9i.mjs.map +1 -0
- package/.output/server/chunks/build/client.precomputed.mjs +1 -1
- package/.output/server/chunks/build/{default-DWCOHHTE.mjs → default-Cao5eO80.mjs} +2 -3
- package/.output/server/chunks/build/default-Cao5eO80.mjs.map +1 -0
- package/.output/server/chunks/build/error-404-Bf6kdO80.mjs +0 -1
- package/.output/server/chunks/build/error-500-D_bcARXN.mjs +0 -1
- package/.output/server/chunks/build/{index-CckL_NBD.mjs → index-ByZO4Bvq.mjs} +2 -2
- package/.output/server/chunks/build/index-ByZO4Bvq.mjs.map +1 -0
- package/.output/server/chunks/build/{index-QVeSHT3L.mjs → index-ljj9uTXI.mjs} +6 -5
- package/.output/server/chunks/build/index-ljj9uTXI.mjs.map +1 -0
- package/.output/server/chunks/build/nuxt-link-SvT1nf8Z.mjs +1 -1
- package/.output/server/chunks/build/{repo-graph-CHNl58mY.mjs → repo-graph-CVnkmn8i.mjs} +23 -10
- package/.output/server/chunks/build/repo-graph-CVnkmn8i.mjs.map +1 -0
- package/.output/server/chunks/build/server.mjs +5 -6
- package/.output/server/chunks/build/styles.mjs +4 -4
- package/.output/server/chunks/build/{usePrd-SqcxGyFU.mjs → usePrd-f7ylhIqs.mjs} +10 -34
- package/.output/server/chunks/build/usePrd-f7ylhIqs.mjs.map +1 -0
- package/.output/server/chunks/nitro/nitro.mjs +614 -1211
- package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
- package/.output/server/chunks/routes/api/browse.get.mjs +34 -10
- package/.output/server/chunks/routes/api/browse.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.get.mjs +3 -2
- package/.output/server/chunks/routes/api/index.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.post.mjs +22 -7
- package/.output/server/chunks/routes/api/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/git/commits.get.mjs +29 -23
- package/.output/server/chunks/routes/api/repos/_repoId/git/commits.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/git/diff.get.mjs +12 -7
- package/.output/server/chunks/routes/api/repos/_repoId/git/diff.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/git/file-content.get.mjs +32 -13
- package/.output/server/chunks/routes/api/repos/_repoId/git/file-content.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/git/file-diff.get.mjs +14 -14
- package/.output/server/chunks/routes/api/repos/_repoId/git/file-diff.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/graph.get.mjs +7 -2
- package/.output/server/chunks/routes/api/repos/_repoId/graph.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/graph.get.mjs +16 -2
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/graph.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/progress.get.mjs +21 -9
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/progress.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/tasks/_taskId/commits.get.mjs +21 -85
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/tasks/_taskId/commits.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/tasks.get.mjs +21 -9
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/tasks.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug_.get.mjs +31 -50
- package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug_.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/prds.get.mjs +20 -49
- package/.output/server/chunks/routes/api/repos/_repoId/prds.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId/refresh-git-repos.post.mjs +6 -13
- package/.output/server/chunks/routes/api/repos/_repoId/refresh-git-repos.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/repos/_repoId_.delete.mjs +3 -2
- package/.output/server/chunks/routes/api/repos/_repoId_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/runtime.get.mjs +1 -2
- package/.output/server/chunks/routes/api/runtime.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/watch.get.mjs +5 -4
- package/.output/server/chunks/routes/api/watch.get.mjs.map +1 -1
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/index.mjs +1 -2
- package/.output/server/index.mjs.map +1 -1
- package/.output/server/node_modules/zod/index.js +4 -0
- package/.output/server/node_modules/zod/package.json +118 -0
- package/.output/server/node_modules/zod/v3/ZodError.js +133 -0
- package/.output/server/node_modules/zod/v3/errors.js +9 -0
- package/.output/server/node_modules/zod/v3/external.js +6 -0
- package/.output/server/node_modules/zod/v3/helpers/errorUtil.js +6 -0
- package/.output/server/node_modules/zod/v3/helpers/parseUtil.js +109 -0
- package/.output/server/node_modules/zod/v3/helpers/typeAliases.js +1 -0
- package/.output/server/node_modules/zod/v3/helpers/util.js +133 -0
- package/.output/server/node_modules/zod/v3/locales/en.js +109 -0
- package/.output/server/node_modules/zod/v3/types.js +3693 -0
- package/.output/server/package.json +2 -1
- package/README.md +2 -2
- package/dist/host/src/api/prds.js +6 -172
- package/dist/host/src/api/repos.js +3 -18
- package/dist/host/src/api/state.js +8 -9
- package/dist/host/src/executor-runner.js +368 -0
- package/dist/host/src/executor.js +138 -260
- package/dist/host/src/mcp.js +27 -1
- package/dist/host/src/ui.js +14 -4
- package/dist/server/utils/change-events.js +33 -0
- package/dist/server/utils/git.js +11 -16
- package/dist/server/utils/prd-service.js +231 -0
- package/dist/server/utils/prd-state.js +54 -162
- package/dist/server/utils/repos.js +72 -17
- package/dist/server/utils/state-schema.js +61 -0
- package/dist/server/utils/task-graph.js +13 -13
- package/package.json +2 -1
- package/.output/public/_nuxt/CVJh28bx.js +0 -1
- package/.output/public/_nuxt/CyZuidLG.js +0 -60
- package/.output/public/_nuxt/D0op9E2g.js +0 -1
- package/.output/public/_nuxt/DX8awZaa.js +0 -1
- package/.output/public/_nuxt/Detail.z33AHKev.css +0 -1
- package/.output/public/_nuxt/DiTJUZOC.js +0 -1
- package/.output/public/_nuxt/T_3JE9C-.js +0 -1
- package/.output/public/_nuxt/WOI2tLsR.js +0 -42
- package/.output/public/_nuxt/_prd_.KTotLoF_.css +0 -1
- package/.output/public/_nuxt/builds/meta/029070b0-b8e2-4988-84f4-d0c9ff55c998.json +0 -1
- package/.output/public/_nuxt/odRGDGwj.js +0 -1
- package/.output/server/chunks/build/Detail-CUfU85GY.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles-1.mjs-CS8FTppg.mjs +0 -4
- package/.output/server/chunks/build/DiffViewer-styles-1.mjs-CS8FTppg.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles.AUfwwelI.mjs +0 -10
- package/.output/server/chunks/build/DiffViewer-styles.AUfwwelI.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles.D_it8zfk.mjs +0 -8
- package/.output/server/chunks/build/DiffViewer-styles.D_it8zfk.mjs.map +0 -1
- package/.output/server/chunks/build/Viewer-styles.CshnetGw.mjs +0 -8
- package/.output/server/chunks/build/Viewer-styles.CshnetGw.mjs.map +0 -1
- package/.output/server/chunks/build/_prd_-CeVnQzOV.mjs.map +0 -1
- package/.output/server/chunks/build/default-DWCOHHTE.mjs.map +0 -1
- package/.output/server/chunks/build/index-CckL_NBD.mjs.map +0 -1
- package/.output/server/chunks/build/index-QVeSHT3L.mjs.map +0 -1
- package/.output/server/chunks/build/repo-graph-CHNl58mY.mjs.map +0 -1
- package/.output/server/chunks/build/usePrd-SqcxGyFU.mjs.map +0 -1
|
@@ -1,103 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { getStewardHelp } from './help.js';
|
|
4
|
-
const MAX_OUTPUT_SIZE = 50_000;
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
5
3
|
const EXECUTION_TIMEOUT_MS = 30_000;
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
function safeJsonStringify(value) {
|
|
19
|
-
const seen = new WeakSet();
|
|
20
|
-
try {
|
|
21
|
-
return JSON.stringify(value, (_key, currentValue) => {
|
|
22
|
-
if (typeof currentValue === 'bigint') {
|
|
23
|
-
return `${currentValue}n`;
|
|
24
|
-
}
|
|
25
|
-
if (typeof currentValue === 'function') {
|
|
26
|
-
const functionName = currentValue.name ? ` ${currentValue.name}` : '';
|
|
27
|
-
return `[Function${functionName}]`;
|
|
28
|
-
}
|
|
29
|
-
if (typeof currentValue === 'symbol') {
|
|
30
|
-
return currentValue.toString();
|
|
31
|
-
}
|
|
32
|
-
if (typeof currentValue === 'object' && currentValue !== null) {
|
|
33
|
-
if (seen.has(currentValue)) {
|
|
34
|
-
return '[Circular]';
|
|
35
|
-
}
|
|
36
|
-
seen.add(currentValue);
|
|
37
|
-
}
|
|
38
|
-
return currentValue;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function formatLogValue(value) {
|
|
46
|
-
if (typeof value === 'string') {
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
|
-
const json = safeJsonStringify(value);
|
|
50
|
-
if (json !== undefined) {
|
|
51
|
-
return json;
|
|
52
|
-
}
|
|
53
|
-
return String(value);
|
|
54
|
-
}
|
|
55
|
-
function truncateResult(result) {
|
|
56
|
-
if (result === undefined) {
|
|
57
|
-
return {
|
|
58
|
-
result: null,
|
|
59
|
-
truncatedResult: false,
|
|
60
|
-
resultWasUndefined: true
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
const json = safeJsonStringify(result);
|
|
64
|
-
if (json === undefined) {
|
|
65
|
-
return {
|
|
66
|
-
result: {
|
|
67
|
-
_unserializable: true,
|
|
68
|
-
preview: String(result)
|
|
69
|
-
},
|
|
70
|
-
truncatedResult: false,
|
|
71
|
-
resultWasUndefined: false
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
if (json.length <= MAX_OUTPUT_SIZE) {
|
|
75
|
-
return {
|
|
76
|
-
result,
|
|
77
|
-
truncatedResult: false,
|
|
78
|
-
resultWasUndefined: false
|
|
79
|
-
};
|
|
4
|
+
const MAX_STDIO_CAPTURE = 200_000;
|
|
5
|
+
function getForwardedNodeFlags() {
|
|
6
|
+
const forwarded = [];
|
|
7
|
+
for (const arg of process.execArgv) {
|
|
8
|
+
if (arg === '--experimental-sqlite' || arg === '--no-experimental-sqlite') {
|
|
9
|
+
forwarded.push(arg);
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (arg.startsWith('--experimental-sqlite=')) {
|
|
13
|
+
forwarded.push(arg);
|
|
14
|
+
}
|
|
80
15
|
}
|
|
81
|
-
return
|
|
82
|
-
result: {
|
|
83
|
-
_truncated: true,
|
|
84
|
-
size: json.length,
|
|
85
|
-
preview: json.slice(0, MAX_OUTPUT_SIZE),
|
|
86
|
-
message: `Output truncated (${json.length} chars, showing first ${MAX_OUTPUT_SIZE})`
|
|
87
|
-
},
|
|
88
|
-
truncatedResult: true,
|
|
89
|
-
resultWasUndefined: false
|
|
90
|
-
};
|
|
16
|
+
return forwarded;
|
|
91
17
|
}
|
|
92
18
|
function normalizeFailure(error) {
|
|
93
|
-
if (error instanceof ExecutionError) {
|
|
94
|
-
return {
|
|
95
|
-
code: error.options?.code || 'EXECUTION_ERROR',
|
|
96
|
-
message: error.message,
|
|
97
|
-
...(error.options?.stackTrace && { stack: error.options.stackTrace }),
|
|
98
|
-
...(error.options?.details !== undefined && { details: error.options.details })
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
19
|
if (error instanceof Error) {
|
|
102
20
|
const { code, details } = error;
|
|
103
21
|
return {
|
|
@@ -112,184 +30,144 @@ function normalizeFailure(error) {
|
|
|
112
30
|
message: String(error)
|
|
113
31
|
};
|
|
114
32
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
logsTruncated = true;
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
let message = args.map(formatLogValue).join(' ');
|
|
126
|
-
if (message.length > MAX_LOG_ENTRY_SIZE) {
|
|
127
|
-
message = `${message.slice(0, MAX_LOG_ENTRY_SIZE)}...`;
|
|
128
|
-
logsTruncated = true;
|
|
129
|
-
}
|
|
130
|
-
if (totalLogChars + message.length > MAX_LOG_OUTPUT_SIZE) {
|
|
131
|
-
logsTruncated = true;
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
totalLogChars += message.length;
|
|
135
|
-
logs.push({
|
|
136
|
-
level,
|
|
33
|
+
function buildFailureEnvelope(startedAt, code, message, details) {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
result: null,
|
|
37
|
+
logs: [],
|
|
38
|
+
error: {
|
|
39
|
+
code,
|
|
137
40
|
message,
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
const buildEnvelope = (params) => ({
|
|
142
|
-
ok: params.ok,
|
|
143
|
-
result: params.result,
|
|
144
|
-
logs,
|
|
145
|
-
error: params.error,
|
|
41
|
+
...(details !== undefined && { details })
|
|
42
|
+
},
|
|
146
43
|
meta: {
|
|
147
44
|
timeoutMs: EXECUTION_TIMEOUT_MS,
|
|
148
45
|
durationMs: Date.now() - startedAt,
|
|
149
|
-
truncatedResult: params.truncatedResult,
|
|
150
|
-
truncatedLogs: logsTruncated,
|
|
151
|
-
resultWasUndefined: params.resultWasUndefined
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
if (!code || !code.trim()) {
|
|
155
|
-
const error = normalizeFailure(new ExecutionError('Code cannot be empty', { code: 'EMPTY_CODE' }));
|
|
156
|
-
return buildEnvelope({
|
|
157
|
-
ok: false,
|
|
158
|
-
result: null,
|
|
159
|
-
error,
|
|
160
46
|
truncatedResult: false,
|
|
47
|
+
truncatedLogs: false,
|
|
161
48
|
resultWasUndefined: false
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
const timers = new Set();
|
|
165
|
-
let executionTimeout = null;
|
|
166
|
-
let asyncCallbackError = null;
|
|
167
|
-
const wrapTimerHandler = (handler) => {
|
|
168
|
-
return () => {
|
|
169
|
-
try {
|
|
170
|
-
handler();
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
const normalizedError = error instanceof Error
|
|
174
|
-
? error
|
|
175
|
-
: new Error(String(error));
|
|
176
|
-
asyncCallbackError = normalizedError;
|
|
177
|
-
appendLog('error', ['Timer callback error:', normalizedError.message]);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
49
|
+
}
|
|
180
50
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
51
|
+
}
|
|
52
|
+
function withDurationFallback(envelope, startedAt) {
|
|
53
|
+
const durationMs = Number.isFinite(envelope.meta.durationMs) && envelope.meta.durationMs >= 0
|
|
54
|
+
? envelope.meta.durationMs
|
|
55
|
+
: Date.now() - startedAt;
|
|
56
|
+
return {
|
|
57
|
+
...envelope,
|
|
58
|
+
meta: {
|
|
59
|
+
...envelope.meta,
|
|
60
|
+
timeoutMs: EXECUTION_TIMEOUT_MS,
|
|
61
|
+
durationMs
|
|
186
62
|
}
|
|
187
|
-
return wrapTimerHandler(handler);
|
|
188
63
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
64
|
+
}
|
|
65
|
+
function looksLikeExecutionEnvelope(value) {
|
|
66
|
+
if (!value || typeof value !== 'object') {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const candidate = value;
|
|
70
|
+
return typeof candidate.ok === 'boolean'
|
|
71
|
+
&& Array.isArray(candidate.logs)
|
|
72
|
+
&& candidate.meta !== undefined;
|
|
73
|
+
}
|
|
74
|
+
export async function execute(code) {
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
if (!code || !code.trim()) {
|
|
77
|
+
return buildFailureEnvelope(startedAt, 'EMPTY_CODE', 'Code cannot be empty');
|
|
78
|
+
}
|
|
79
|
+
const runnerPath = fileURLToPath(new URL('./executor-runner.js', import.meta.url));
|
|
80
|
+
const childArgs = [
|
|
81
|
+
...getForwardedNodeFlags(),
|
|
82
|
+
'--max-old-space-size=256',
|
|
83
|
+
runnerPath
|
|
84
|
+
];
|
|
85
|
+
return await new Promise((resolveEnvelope) => {
|
|
86
|
+
const child = spawn(process.execPath, childArgs, {
|
|
87
|
+
cwd: process.cwd(),
|
|
88
|
+
env: {
|
|
89
|
+
...process.env,
|
|
90
|
+
STEWARD_EXECUTION_TIMEOUT_MS: String(EXECUTION_TIMEOUT_MS)
|
|
91
|
+
},
|
|
92
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
93
|
+
});
|
|
94
|
+
let settled = false;
|
|
95
|
+
let stdout = '';
|
|
96
|
+
let stderr = '';
|
|
97
|
+
const finish = (envelope) => {
|
|
98
|
+
if (settled) {
|
|
99
|
+
return;
|
|
208
100
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
},
|
|
217
|
-
clearTimeout: (timer) => {
|
|
218
|
-
timers.delete(timer);
|
|
219
|
-
clearTimeout(timer);
|
|
220
|
-
},
|
|
221
|
-
setInterval: (handler, timeout) => {
|
|
222
|
-
if (timers.size >= MAX_TIMERS) {
|
|
223
|
-
throw new ExecutionError(`Timer limit exceeded (max ${MAX_TIMERS})`, {
|
|
224
|
-
code: 'TIMER_LIMIT'
|
|
225
|
-
});
|
|
101
|
+
settled = true;
|
|
102
|
+
clearTimeout(killTimer);
|
|
103
|
+
resolveEnvelope(withDurationFallback(envelope, startedAt));
|
|
104
|
+
};
|
|
105
|
+
const captureOutput = (current, chunk) => {
|
|
106
|
+
if (current.length >= MAX_STDIO_CAPTURE) {
|
|
107
|
+
return current;
|
|
226
108
|
}
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
Promise
|
|
237
|
-
};
|
|
238
|
-
const wrappedCode = `
|
|
239
|
-
(async () => {
|
|
240
|
-
${code}
|
|
241
|
-
})()
|
|
242
|
-
`;
|
|
243
|
-
try {
|
|
244
|
-
const script = new vm.Script(wrappedCode, {
|
|
245
|
-
filename: 'codemode.js'
|
|
109
|
+
const remaining = MAX_STDIO_CAPTURE - current.length;
|
|
110
|
+
return current + chunk.toString('utf-8', 0, remaining);
|
|
111
|
+
};
|
|
112
|
+
const killTimer = setTimeout(() => {
|
|
113
|
+
child.kill('SIGKILL');
|
|
114
|
+
finish(buildFailureEnvelope(startedAt, 'TIMEOUT', `Execution timed out after ${EXECUTION_TIMEOUT_MS}ms`));
|
|
115
|
+
}, EXECUTION_TIMEOUT_MS);
|
|
116
|
+
child.stdout.on('data', (chunk) => {
|
|
117
|
+
stdout = captureOutput(stdout, chunk);
|
|
246
118
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
timeout: EXECUTION_TIMEOUT_MS
|
|
250
|
-
}));
|
|
251
|
-
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
252
|
-
executionTimeout = setTimeout(() => {
|
|
253
|
-
reject(new ExecutionError(`Execution timed out after ${EXECUTION_TIMEOUT_MS}ms`, {
|
|
254
|
-
code: 'TIMEOUT'
|
|
255
|
-
}));
|
|
256
|
-
}, EXECUTION_TIMEOUT_MS);
|
|
119
|
+
child.stderr.on('data', (chunk) => {
|
|
120
|
+
stderr = captureOutput(stderr, chunk);
|
|
257
121
|
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
code: 'ASYNC_CALLBACK_ERROR',
|
|
262
|
-
stackTrace: asyncCallbackError.stack
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
const truncated = truncateResult(rawResult);
|
|
266
|
-
return buildEnvelope({
|
|
267
|
-
ok: true,
|
|
268
|
-
result: truncated.result,
|
|
269
|
-
error: null,
|
|
270
|
-
truncatedResult: truncated.truncatedResult,
|
|
271
|
-
resultWasUndefined: truncated.resultWasUndefined
|
|
122
|
+
child.on('error', (error) => {
|
|
123
|
+
const failure = normalizeFailure(error);
|
|
124
|
+
finish(buildFailureEnvelope(startedAt, failure.code, failure.message, failure.details));
|
|
272
125
|
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
126
|
+
child.on('close', (exitCode, signal) => {
|
|
127
|
+
if (settled) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const trimmedStdout = stdout.trim();
|
|
131
|
+
if (!trimmedStdout) {
|
|
132
|
+
const message = signal
|
|
133
|
+
? `Execution process terminated by signal ${signal}`
|
|
134
|
+
: `Execution process exited with code ${exitCode ?? 0}`;
|
|
135
|
+
finish(buildFailureEnvelope(startedAt, 'EXECUTION_PROCESS_FAILURE', message, {
|
|
136
|
+
exitCode,
|
|
137
|
+
signal,
|
|
138
|
+
stderr: stderr.trim() || undefined
|
|
139
|
+
}));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const parsed = JSON.parse(trimmedStdout);
|
|
144
|
+
if (looksLikeExecutionEnvelope(parsed)) {
|
|
145
|
+
finish(parsed);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
finish(buildFailureEnvelope(startedAt, 'INVALID_ENVELOPE', 'Execution process returned an invalid envelope', {
|
|
149
|
+
outputPreview: trimmedStdout.slice(0, 2000),
|
|
150
|
+
stderr: stderr.trim() || undefined,
|
|
151
|
+
exitCode,
|
|
152
|
+
signal
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const failure = normalizeFailure(error);
|
|
157
|
+
finish(buildFailureEnvelope(startedAt, 'INVALID_JSON', failure.message, {
|
|
158
|
+
outputPreview: trimmedStdout.slice(0, 2000),
|
|
159
|
+
stderr: stderr.trim() || undefined,
|
|
160
|
+
exitCode,
|
|
161
|
+
signal
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
283
164
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (executionTimeout) {
|
|
287
|
-
clearTimeout(executionTimeout);
|
|
165
|
+
try {
|
|
166
|
+
child.stdin.end(JSON.stringify({ code }));
|
|
288
167
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
const failure = normalizeFailure(error);
|
|
170
|
+
finish(buildFailureEnvelope(startedAt, 'EXECUTION_PIPE_FAILURE', failure.message));
|
|
171
|
+
}
|
|
172
|
+
});
|
|
295
173
|
}
|
package/dist/host/src/mcp.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
6
|
import { z } from 'zod';
|
|
@@ -50,11 +53,34 @@ function buildUnexpectedErrorEnvelope(error) {
|
|
|
50
53
|
}
|
|
51
54
|
};
|
|
52
55
|
}
|
|
56
|
+
function resolvePackageVersion() {
|
|
57
|
+
let currentDir = dirname(fileURLToPath(import.meta.url));
|
|
58
|
+
while (true) {
|
|
59
|
+
const packageJsonPath = join(currentDir, 'package.json');
|
|
60
|
+
if (existsSync(packageJsonPath)) {
|
|
61
|
+
try {
|
|
62
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
63
|
+
if (typeof packageJson.version === 'string' && packageJson.version.trim().length > 0) {
|
|
64
|
+
return packageJson.version;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const parentDir = dirname(currentDir);
|
|
72
|
+
if (parentDir === currentDir) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
currentDir = parentDir;
|
|
76
|
+
}
|
|
77
|
+
return '0.0.0';
|
|
78
|
+
}
|
|
53
79
|
export async function runMcpServer() {
|
|
54
80
|
await assertSqliteRuntimeSupport();
|
|
55
81
|
const server = new McpServer({
|
|
56
82
|
name: 'steward',
|
|
57
|
-
version:
|
|
83
|
+
version: resolvePackageVersion()
|
|
58
84
|
});
|
|
59
85
|
registerStewardPrompts(server);
|
|
60
86
|
server.tool('execute', getExecuteToolDescription(), {
|
package/dist/host/src/ui.js
CHANGED
|
@@ -2,6 +2,13 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const DEFAULT_UI_HOST = '127.0.0.1';
|
|
6
|
+
function isLoopbackHost(host) {
|
|
7
|
+
const normalizedHost = host.trim().toLowerCase();
|
|
8
|
+
return normalizedHost === '127.0.0.1'
|
|
9
|
+
|| normalizedHost === 'localhost'
|
|
10
|
+
|| normalizedHost === '::1';
|
|
11
|
+
}
|
|
5
12
|
function findPackageRoot(startDir) {
|
|
6
13
|
let currentDir = startDir;
|
|
7
14
|
while (true) {
|
|
@@ -26,16 +33,19 @@ export async function runUi(options) {
|
|
|
26
33
|
}
|
|
27
34
|
const args = [serverEntrypoint];
|
|
28
35
|
const env = { ...process.env };
|
|
36
|
+
const hostFromEnv = env.NITRO_HOST || env.HOST;
|
|
37
|
+
const requestedHost = (options.host || hostFromEnv || DEFAULT_UI_HOST).trim();
|
|
38
|
+
if (!isLoopbackHost(requestedHost)) {
|
|
39
|
+
throw new Error(`Refusing to bind UI to non-loopback host "${requestedHost}". Steward only supports loopback hosts.`);
|
|
40
|
+
}
|
|
29
41
|
env.NODE_ENV = env.NODE_ENV || 'production';
|
|
30
42
|
if (options.port !== undefined) {
|
|
31
43
|
const port = String(options.port);
|
|
32
44
|
env.PORT = port;
|
|
33
45
|
env.NITRO_PORT = port;
|
|
34
46
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
env.NITRO_HOST = options.host;
|
|
38
|
-
}
|
|
47
|
+
env.HOST = requestedHost;
|
|
48
|
+
env.NITRO_HOST = requestedHost;
|
|
39
49
|
const child = spawn(process.execPath, args, {
|
|
40
50
|
cwd: packageRoot,
|
|
41
51
|
stdio: 'inherit',
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const listeners = new Set();
|
|
2
|
+
const pendingEvents = [];
|
|
3
|
+
let debounceTimer = null;
|
|
4
|
+
const DEBOUNCE_MS = 300;
|
|
5
|
+
function flushPendingEvents() {
|
|
6
|
+
const dedupedEvents = new Map();
|
|
7
|
+
for (const event of pendingEvents) {
|
|
8
|
+
dedupedEvents.set(`${event.repoId}:${event.category}:${event.path}`, event);
|
|
9
|
+
}
|
|
10
|
+
pendingEvents.length = 0;
|
|
11
|
+
debounceTimer = null;
|
|
12
|
+
for (const event of dedupedEvents.values()) {
|
|
13
|
+
for (const listener of listeners) {
|
|
14
|
+
listener(event);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function emitChange(event) {
|
|
19
|
+
pendingEvents.push(event);
|
|
20
|
+
if (debounceTimer) {
|
|
21
|
+
clearTimeout(debounceTimer);
|
|
22
|
+
}
|
|
23
|
+
debounceTimer = setTimeout(flushPendingEvents, DEBOUNCE_MS);
|
|
24
|
+
}
|
|
25
|
+
export function addChangeListener(listener) {
|
|
26
|
+
listeners.add(listener);
|
|
27
|
+
return () => {
|
|
28
|
+
listeners.delete(listener);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function getChangeListenerCount() {
|
|
32
|
+
return listeners.size;
|
|
33
|
+
}
|
package/dist/server/utils/git.js
CHANGED
|
@@ -208,15 +208,6 @@ export async function getCommitDiff(repoPath, sha) {
|
|
|
208
208
|
if (!/^[0-9a-f]{4,40}$/i.test(sha)) {
|
|
209
209
|
throw new Error(`Invalid commit SHA: ${sha}`);
|
|
210
210
|
}
|
|
211
|
-
// Get file status and stats
|
|
212
|
-
const output = await execGit(repoPath, [
|
|
213
|
-
'show', sha,
|
|
214
|
-
'--format=',
|
|
215
|
-
'--name-status',
|
|
216
|
-
'--numstat',
|
|
217
|
-
]);
|
|
218
|
-
// Parse the output - first part is numstat, then name-status
|
|
219
|
-
const lines = output.trim().split('\n').filter(l => l.trim());
|
|
220
211
|
// We need to get both numstat and name-status info
|
|
221
212
|
const numstatOutput = await execGit(repoPath, ['show', sha, '--format=', '--numstat']);
|
|
222
213
|
const nameStatusOutput = await execGit(repoPath, ['show', sha, '--format=', '--name-status']);
|
|
@@ -344,12 +335,13 @@ function parseDiffHunks(diffOutput) {
|
|
|
344
335
|
newLineNum = newStart;
|
|
345
336
|
continue;
|
|
346
337
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
line
|
|
352
|
-
|
|
338
|
+
const isDiffHeader = line.startsWith('diff --git')
|
|
339
|
+
|| line.startsWith('index ')
|
|
340
|
+
|| /^--- (?:a\/.*|\/dev\/null)$/.test(line)
|
|
341
|
+
|| /^\+\+\+ (?:b\/.*|\/dev\/null)$/.test(line)
|
|
342
|
+
|| line === '\';
|
|
343
|
+
// Skip diff metadata lines
|
|
344
|
+
if (isDiffHeader) {
|
|
353
345
|
continue;
|
|
354
346
|
}
|
|
355
347
|
// Parse diff lines
|
|
@@ -370,7 +362,7 @@ function parseDiffHunks(diffOutput) {
|
|
|
370
362
|
};
|
|
371
363
|
currentHunk.lines.push(diffLine);
|
|
372
364
|
}
|
|
373
|
-
else if (line.startsWith(' ')
|
|
365
|
+
else if (line.startsWith(' ')) {
|
|
374
366
|
const diffLine = {
|
|
375
367
|
type: 'context',
|
|
376
368
|
content: line.substring(1),
|
|
@@ -386,6 +378,9 @@ function parseDiffHunks(diffOutput) {
|
|
|
386
378
|
}
|
|
387
379
|
return hunks;
|
|
388
380
|
}
|
|
381
|
+
export function parseDiffHunksForTest(diffOutput) {
|
|
382
|
+
return parseDiffHunks(diffOutput);
|
|
383
|
+
}
|
|
389
384
|
/**
|
|
390
385
|
* Check if a file is binary by attempting to get its diff
|
|
391
386
|
*/
|