lynkr 0.1.0
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/.eslintrc.cjs +12 -0
- package/CLAUDE.md +39 -0
- package/LICENSE +21 -0
- package/README.md +417 -0
- package/bin/cli.js +3 -0
- package/index.js +3 -0
- package/package.json +54 -0
- package/src/api/middleware/logging.js +37 -0
- package/src/api/middleware/session.js +55 -0
- package/src/api/router.js +80 -0
- package/src/cache/prompt.js +183 -0
- package/src/clients/databricks.js +72 -0
- package/src/config/index.js +301 -0
- package/src/db/index.js +192 -0
- package/src/diff/comments.js +153 -0
- package/src/edits/index.js +171 -0
- package/src/indexer/index.js +1610 -0
- package/src/indexer/navigation/index.js +32 -0
- package/src/indexer/navigation/providers/treeSitter.js +36 -0
- package/src/indexer/parser.js +324 -0
- package/src/logger/index.js +27 -0
- package/src/mcp/client.js +194 -0
- package/src/mcp/index.js +34 -0
- package/src/mcp/permissions.js +69 -0
- package/src/mcp/registry.js +225 -0
- package/src/mcp/sandbox.js +238 -0
- package/src/metrics/index.js +38 -0
- package/src/orchestrator/index.js +1492 -0
- package/src/policy/index.js +212 -0
- package/src/policy/web-fallback.js +33 -0
- package/src/server.js +73 -0
- package/src/sessions/index.js +15 -0
- package/src/sessions/record.js +31 -0
- package/src/sessions/store.js +179 -0
- package/src/tasks/store.js +349 -0
- package/src/tests/coverage.js +173 -0
- package/src/tests/index.js +171 -0
- package/src/tests/store.js +213 -0
- package/src/tools/edits.js +94 -0
- package/src/tools/execution.js +169 -0
- package/src/tools/git.js +1346 -0
- package/src/tools/index.js +258 -0
- package/src/tools/indexer.js +360 -0
- package/src/tools/mcp-remote.js +81 -0
- package/src/tools/mcp.js +116 -0
- package/src/tools/process.js +151 -0
- package/src/tools/stubs.js +55 -0
- package/src/tools/tasks.js +260 -0
- package/src/tools/tests.js +132 -0
- package/src/tools/web.js +286 -0
- package/src/tools/workspace.js +173 -0
- package/src/workspace/index.js +95 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const db = require("../db");
|
|
2
|
+
const logger = require("../logger");
|
|
3
|
+
|
|
4
|
+
const OUTPUT_SNIPPET_LIMIT = 2000;
|
|
5
|
+
|
|
6
|
+
const insertTestRunStmt = db.prepare(
|
|
7
|
+
`INSERT INTO test_runs (
|
|
8
|
+
profile,
|
|
9
|
+
status,
|
|
10
|
+
command,
|
|
11
|
+
args,
|
|
12
|
+
cwd,
|
|
13
|
+
exit_code,
|
|
14
|
+
timed_out,
|
|
15
|
+
duration_ms,
|
|
16
|
+
sandbox,
|
|
17
|
+
stdout,
|
|
18
|
+
stderr,
|
|
19
|
+
coverage,
|
|
20
|
+
created_at
|
|
21
|
+
) VALUES (
|
|
22
|
+
@profile,
|
|
23
|
+
@status,
|
|
24
|
+
@command,
|
|
25
|
+
@args,
|
|
26
|
+
@cwd,
|
|
27
|
+
@exit_code,
|
|
28
|
+
@timed_out,
|
|
29
|
+
@duration_ms,
|
|
30
|
+
@sandbox,
|
|
31
|
+
@stdout,
|
|
32
|
+
@stderr,
|
|
33
|
+
@coverage,
|
|
34
|
+
@created_at
|
|
35
|
+
)`,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const listRecentTestRunsStmt = db.prepare(
|
|
39
|
+
`SELECT
|
|
40
|
+
id,
|
|
41
|
+
profile,
|
|
42
|
+
status,
|
|
43
|
+
command,
|
|
44
|
+
args,
|
|
45
|
+
cwd,
|
|
46
|
+
exit_code,
|
|
47
|
+
timed_out,
|
|
48
|
+
duration_ms,
|
|
49
|
+
sandbox,
|
|
50
|
+
stdout,
|
|
51
|
+
stderr,
|
|
52
|
+
coverage,
|
|
53
|
+
created_at
|
|
54
|
+
FROM test_runs
|
|
55
|
+
ORDER BY created_at DESC
|
|
56
|
+
LIMIT ?`,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const countAllTestRunsStmt = db.prepare(`SELECT COUNT(1) AS total FROM test_runs`);
|
|
60
|
+
const countPassedTestRunsStmt = db.prepare(
|
|
61
|
+
`SELECT COUNT(1) AS total FROM test_runs WHERE status = 'passed'`,
|
|
62
|
+
);
|
|
63
|
+
const selectLatestTestRunStmt = db.prepare(
|
|
64
|
+
`SELECT
|
|
65
|
+
id,
|
|
66
|
+
profile,
|
|
67
|
+
status,
|
|
68
|
+
command,
|
|
69
|
+
args,
|
|
70
|
+
cwd,
|
|
71
|
+
exit_code,
|
|
72
|
+
timed_out,
|
|
73
|
+
duration_ms,
|
|
74
|
+
sandbox,
|
|
75
|
+
stdout,
|
|
76
|
+
stderr,
|
|
77
|
+
coverage,
|
|
78
|
+
created_at
|
|
79
|
+
FROM test_runs
|
|
80
|
+
ORDER BY created_at DESC
|
|
81
|
+
LIMIT 1`,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
function truncateOutput(output, limit = OUTPUT_SNIPPET_LIMIT) {
|
|
85
|
+
if (typeof output !== "string" || output.length === 0) {
|
|
86
|
+
return { text: output ?? "", truncated: false };
|
|
87
|
+
}
|
|
88
|
+
if (output.length <= limit) {
|
|
89
|
+
return { text: output, truncated: false };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
text: output.slice(output.length - limit),
|
|
93
|
+
truncated: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function parseJson(value, fallback = null) {
|
|
98
|
+
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(value);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
logger.debug({ err }, "Failed to parse JSON payload in test_runs");
|
|
103
|
+
return fallback;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normaliseArgs(value) {
|
|
108
|
+
const parsed = parseJson(value, []);
|
|
109
|
+
if (!Array.isArray(parsed)) return [];
|
|
110
|
+
return parsed.map((item) => String(item));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normaliseCoverage(value) {
|
|
114
|
+
const parsed = parseJson(value, null);
|
|
115
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
116
|
+
return parsed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normaliseTestRunRow(row, { includeLogs = false } = {}) {
|
|
120
|
+
if (!row) return null;
|
|
121
|
+
const stdoutResult = includeLogs ? { text: row.stdout ?? "", truncated: false } : truncateOutput(row.stdout);
|
|
122
|
+
const stderrResult = includeLogs ? { text: row.stderr ?? "", truncated: false } : truncateOutput(row.stderr);
|
|
123
|
+
return {
|
|
124
|
+
id: row.id,
|
|
125
|
+
profile: row.profile ?? null,
|
|
126
|
+
status: row.status ?? null,
|
|
127
|
+
command: row.command ?? null,
|
|
128
|
+
args: normaliseArgs(row.args),
|
|
129
|
+
cwd: row.cwd ?? null,
|
|
130
|
+
exitCode: typeof row.exit_code === "number" ? row.exit_code : null,
|
|
131
|
+
timedOut: row.timed_out === 1,
|
|
132
|
+
durationMs: typeof row.duration_ms === "number" ? row.duration_ms : null,
|
|
133
|
+
sandbox: row.sandbox ?? null,
|
|
134
|
+
stdout: stdoutResult.text ?? "",
|
|
135
|
+
stdoutTruncated: stdoutResult.truncated,
|
|
136
|
+
stderr: stderrResult.text ?? "",
|
|
137
|
+
stderrTruncated: stderrResult.truncated,
|
|
138
|
+
coverage: normaliseCoverage(row.coverage),
|
|
139
|
+
createdAt: new Date(Number(row.created_at ?? Date.now())).toISOString(),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function createTestRun({
|
|
144
|
+
profile,
|
|
145
|
+
status,
|
|
146
|
+
command,
|
|
147
|
+
args,
|
|
148
|
+
cwd,
|
|
149
|
+
exitCode,
|
|
150
|
+
timedOut,
|
|
151
|
+
durationMs,
|
|
152
|
+
sandbox,
|
|
153
|
+
stdout,
|
|
154
|
+
stderr,
|
|
155
|
+
coverage,
|
|
156
|
+
createdAt,
|
|
157
|
+
}) {
|
|
158
|
+
const payload = {
|
|
159
|
+
profile: profile ?? null,
|
|
160
|
+
status: status ?? null,
|
|
161
|
+
command: command ?? "",
|
|
162
|
+
args: Array.isArray(args) && args.length ? JSON.stringify(args.map(String)) : null,
|
|
163
|
+
cwd: cwd ?? null,
|
|
164
|
+
exit_code: typeof exitCode === "number" ? exitCode : null,
|
|
165
|
+
timed_out: timedOut ? 1 : 0,
|
|
166
|
+
duration_ms: typeof durationMs === "number" ? durationMs : null,
|
|
167
|
+
sandbox: sandbox ?? null,
|
|
168
|
+
stdout: typeof stdout === "string" ? stdout : stdout ?? "",
|
|
169
|
+
stderr: typeof stderr === "string" ? stderr : stderr ?? "",
|
|
170
|
+
coverage: coverage ? JSON.stringify(coverage) : null,
|
|
171
|
+
created_at: Number.isFinite(createdAt) ? Math.trunc(createdAt) : Date.now(),
|
|
172
|
+
};
|
|
173
|
+
const info = insertTestRunStmt.run(payload);
|
|
174
|
+
return normaliseTestRunRow(
|
|
175
|
+
{
|
|
176
|
+
id: info.lastInsertRowid,
|
|
177
|
+
...payload,
|
|
178
|
+
},
|
|
179
|
+
{ includeLogs: true },
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function listTestRuns({ limit = 5, includeLogs = false } = {}) {
|
|
184
|
+
const clamped = Math.min(Math.max(Number(limit) || 5, 1), 50);
|
|
185
|
+
const rows = listRecentTestRunsStmt.all(clamped);
|
|
186
|
+
return rows.map((row) => normaliseTestRunRow(row, { includeLogs }));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function getTestSummary({ includeRecent = false, recentLimit = 5 } = {}) {
|
|
190
|
+
const totals = countAllTestRunsStmt.get();
|
|
191
|
+
const passed = countPassedTestRunsStmt.get();
|
|
192
|
+
const latest = selectLatestTestRunStmt.get();
|
|
193
|
+
const totalRuns = Number(totals?.total ?? 0);
|
|
194
|
+
const passedRuns = Number(passed?.total ?? 0);
|
|
195
|
+
const summary = {
|
|
196
|
+
totalRuns,
|
|
197
|
+
passRate: totalRuns > 0 ? Number(((passedRuns / totalRuns) * 100).toFixed(2)) : null,
|
|
198
|
+
lastRun: latest ? normaliseTestRunRow(latest, { includeLogs: false }) : null,
|
|
199
|
+
};
|
|
200
|
+
if (includeRecent) {
|
|
201
|
+
summary.recentRuns = listTestRuns({
|
|
202
|
+
limit: recentLimit,
|
|
203
|
+
includeLogs: false,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return summary;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = {
|
|
210
|
+
createTestRun,
|
|
211
|
+
listTestRuns,
|
|
212
|
+
getTestSummary,
|
|
213
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const { getEditHistory, revertEdit } = require("../edits");
|
|
2
|
+
const { registerTool } = require(".");
|
|
3
|
+
const logger = require("../logger");
|
|
4
|
+
|
|
5
|
+
function registerEditHistoryTool() {
|
|
6
|
+
registerTool(
|
|
7
|
+
"workspace_edit_history",
|
|
8
|
+
async ({ args = {} }) => {
|
|
9
|
+
const limit =
|
|
10
|
+
typeof args.limit === "number" && args.limit > 0 ? Math.min(args.limit, 100) : 20;
|
|
11
|
+
const filePath =
|
|
12
|
+
typeof args.path === "string"
|
|
13
|
+
? args.path
|
|
14
|
+
: typeof args.file === "string"
|
|
15
|
+
? args.file
|
|
16
|
+
: undefined;
|
|
17
|
+
const sessionId =
|
|
18
|
+
typeof args.session_id === "string"
|
|
19
|
+
? args.session_id
|
|
20
|
+
: typeof args.sessionId === "string"
|
|
21
|
+
? args.sessionId
|
|
22
|
+
: undefined;
|
|
23
|
+
|
|
24
|
+
const history = getEditHistory({ filePath, sessionId, limit });
|
|
25
|
+
return {
|
|
26
|
+
ok: true,
|
|
27
|
+
status: 200,
|
|
28
|
+
content: JSON.stringify(
|
|
29
|
+
{
|
|
30
|
+
edits: history,
|
|
31
|
+
limit,
|
|
32
|
+
filePath,
|
|
33
|
+
sessionId,
|
|
34
|
+
},
|
|
35
|
+
null,
|
|
36
|
+
2,
|
|
37
|
+
),
|
|
38
|
+
metadata: {
|
|
39
|
+
count: history.length,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
{ category: "workspace" },
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function registerEditRevertTool() {
|
|
48
|
+
registerTool(
|
|
49
|
+
"workspace_edit_revert",
|
|
50
|
+
async ({ args = {} }, context = {}) => {
|
|
51
|
+
const editId = args.id ?? args.edit_id ?? args.editId;
|
|
52
|
+
if (typeof editId !== "number" && typeof editId !== "string") {
|
|
53
|
+
throw new Error("workspace_edit_revert requires an edit id (numeric).");
|
|
54
|
+
}
|
|
55
|
+
const numericId = Number(editId);
|
|
56
|
+
if (Number.isNaN(numericId)) {
|
|
57
|
+
throw new Error("Edit id must be numeric.");
|
|
58
|
+
}
|
|
59
|
+
const sessionId = context.session?.id ?? context.sessionId ?? null;
|
|
60
|
+
try {
|
|
61
|
+
const result = await revertEdit({ editId: numericId, sessionId });
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
status: 200,
|
|
65
|
+
content: JSON.stringify(
|
|
66
|
+
{
|
|
67
|
+
revertedEditId: result.revertedEditId,
|
|
68
|
+
filePath: result.filePath,
|
|
69
|
+
},
|
|
70
|
+
null,
|
|
71
|
+
2,
|
|
72
|
+
),
|
|
73
|
+
metadata: {
|
|
74
|
+
revertedEditId: result.revertedEditId,
|
|
75
|
+
filePath: result.filePath,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
} catch (err) {
|
|
79
|
+
logger.warn({ err, editId: numericId }, "Failed to revert edit");
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{ category: "workspace" },
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function registerEditTools() {
|
|
88
|
+
registerEditHistoryTool();
|
|
89
|
+
registerEditRevertTool();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
registerEditTools,
|
|
94
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const { runProcess, MAX_TIMEOUT_MS, DEFAULT_TIMEOUT_MS } = require("./process");
|
|
3
|
+
const { registerTool } = require(".");
|
|
4
|
+
const { workspaceRoot, resolveWorkspacePath } = require("../workspace");
|
|
5
|
+
|
|
6
|
+
function parseTimeout(value) {
|
|
7
|
+
if (value === undefined || value === null) return DEFAULT_TIMEOUT_MS;
|
|
8
|
+
const parsed = Number.parseInt(value, 10);
|
|
9
|
+
if (Number.isNaN(parsed) || parsed <= 0) return DEFAULT_TIMEOUT_MS;
|
|
10
|
+
return Math.min(parsed, MAX_TIMEOUT_MS);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normaliseCwd(cwd) {
|
|
14
|
+
if (!cwd) return workspaceRoot;
|
|
15
|
+
return resolveWorkspacePath(cwd);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseSandboxMode(value) {
|
|
19
|
+
if (typeof value !== "string") return "auto";
|
|
20
|
+
const mode = value.trim().toLowerCase();
|
|
21
|
+
if (mode === "always" || mode === "never" || mode === "auto") {
|
|
22
|
+
return mode;
|
|
23
|
+
}
|
|
24
|
+
return "auto";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function formatProcessResult(result) {
|
|
28
|
+
return JSON.stringify(
|
|
29
|
+
{
|
|
30
|
+
stdout: result.stdout,
|
|
31
|
+
stderr: result.stderr,
|
|
32
|
+
exit_code: result.exitCode,
|
|
33
|
+
signal: result.signal,
|
|
34
|
+
timed_out: result.timedOut,
|
|
35
|
+
duration_ms: result.durationMs,
|
|
36
|
+
stdout_overflow: result.stdoutOverflow,
|
|
37
|
+
stderr_overflow: result.stderrOverflow,
|
|
38
|
+
},
|
|
39
|
+
null,
|
|
40
|
+
2,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function registerShellTool() {
|
|
45
|
+
registerTool(
|
|
46
|
+
"shell",
|
|
47
|
+
async ({ args = {} }) => {
|
|
48
|
+
const command = args.command ?? args.cmd ?? args.run ?? args.input;
|
|
49
|
+
const commandArgs = Array.isArray(args.args) ? args.args.map(String) : [];
|
|
50
|
+
const cwd = normaliseCwd(args.cwd);
|
|
51
|
+
const timeoutMs = parseTimeout(args.timeout_ms ?? args.timeout);
|
|
52
|
+
|
|
53
|
+
let spawnCommand;
|
|
54
|
+
let spawnArgs;
|
|
55
|
+
let useShell = false;
|
|
56
|
+
|
|
57
|
+
if (typeof command === "string" && command.trim().length > 0) {
|
|
58
|
+
spawnCommand = "bash";
|
|
59
|
+
spawnArgs = ["-lc", command];
|
|
60
|
+
useShell = false;
|
|
61
|
+
} else if (Array.isArray(command) && command.length > 0) {
|
|
62
|
+
spawnCommand = String(command[0]);
|
|
63
|
+
spawnArgs = command.slice(1).map(String).concat(commandArgs);
|
|
64
|
+
} else if (
|
|
65
|
+
typeof args.args === "string" &&
|
|
66
|
+
args.args.trim().length > 0 &&
|
|
67
|
+
!command
|
|
68
|
+
) {
|
|
69
|
+
spawnCommand = "bash";
|
|
70
|
+
spawnArgs = ["-lc", args.args];
|
|
71
|
+
} else {
|
|
72
|
+
throw new Error("shell tool requires a command string or array.");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const sandbox = parseSandboxMode(args.sandbox ?? args.isolation);
|
|
76
|
+
|
|
77
|
+
const result = await runProcess({
|
|
78
|
+
command: spawnCommand,
|
|
79
|
+
args: spawnArgs,
|
|
80
|
+
cwd,
|
|
81
|
+
env: args.env,
|
|
82
|
+
timeoutMs,
|
|
83
|
+
shell: useShell,
|
|
84
|
+
sandbox,
|
|
85
|
+
sessionId: args.session_id ?? args.sessionId ?? null,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const ok = result.exitCode === 0 && !result.timedOut;
|
|
89
|
+
const status = result.timedOut ? 408 : ok ? 200 : 500;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
ok,
|
|
93
|
+
status,
|
|
94
|
+
content: formatProcessResult(result),
|
|
95
|
+
metadata: {
|
|
96
|
+
command: spawnCommand,
|
|
97
|
+
args: spawnArgs,
|
|
98
|
+
cwd,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
{ category: "execution" },
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function registerPythonTool() {
|
|
107
|
+
registerTool(
|
|
108
|
+
"python_exec",
|
|
109
|
+
async ({ args = {} }) => {
|
|
110
|
+
const code =
|
|
111
|
+
typeof args.code === "string"
|
|
112
|
+
? args.code
|
|
113
|
+
: typeof args.script === "string"
|
|
114
|
+
? args.script
|
|
115
|
+
: typeof args.input === "string"
|
|
116
|
+
? args.input
|
|
117
|
+
: null;
|
|
118
|
+
|
|
119
|
+
if (!code) {
|
|
120
|
+
throw new Error("python_exec requires a code string.");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const executable = args.executable ?? args.python ?? "python3";
|
|
124
|
+
const cwd = normaliseCwd(args.cwd);
|
|
125
|
+
const timeoutMs = parseTimeout(args.timeout_ms ?? args.timeout);
|
|
126
|
+
const requirements = Array.isArray(args.requirements) ? args.requirements : [];
|
|
127
|
+
|
|
128
|
+
// Basic support: write code to stdin; requirements handling is TODO.
|
|
129
|
+
const sandbox = parseSandboxMode(args.sandbox ?? args.isolation);
|
|
130
|
+
|
|
131
|
+
const result = await runProcess({
|
|
132
|
+
command: executable,
|
|
133
|
+
args: ["-"],
|
|
134
|
+
cwd,
|
|
135
|
+
env: args.env,
|
|
136
|
+
timeoutMs,
|
|
137
|
+
input: code,
|
|
138
|
+
sandbox,
|
|
139
|
+
sessionId: args.session_id ?? args.sessionId ?? null,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const ok = result.exitCode === 0 && !result.timedOut;
|
|
143
|
+
const status = result.timedOut ? 408 : ok ? 200 : 500;
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
ok,
|
|
147
|
+
status,
|
|
148
|
+
content: formatProcessResult(result),
|
|
149
|
+
metadata: {
|
|
150
|
+
executable: path.basename(executable),
|
|
151
|
+
cwd,
|
|
152
|
+
requirements,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
{ category: "execution" },
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function registerExecutionTools() {
|
|
161
|
+
registerShellTool();
|
|
162
|
+
registerPythonTool();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = {
|
|
166
|
+
registerExecutionTools,
|
|
167
|
+
registerShellTool,
|
|
168
|
+
registerPythonTool,
|
|
169
|
+
};
|