@onkernel/cli 0.0.1-alpha.1 → 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -2
- package/dist/index.js +112 -715
- package/package.json +11 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { }
|
|
1
|
+
#!/usr/bin/env bun
|
package/dist/index.js
CHANGED
|
@@ -1,504 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Kernel } from '@onkernel/sdk';
|
|
3
|
+
import chalk from 'chalk';
|
|
1
4
|
import { Command } from 'commander';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { parse, stringify } from 'smol-toml';
|
|
5
|
+
import fs, { createReadStream } from 'fs';
|
|
6
|
+
import path2 from 'path';
|
|
7
|
+
import * as tmp from 'tmp';
|
|
8
|
+
import archiver from 'archiver';
|
|
7
9
|
import fsExtra from 'fs-extra';
|
|
8
|
-
import
|
|
9
|
-
import Python from 'tree-sitter-python';
|
|
10
|
-
import TypeScript from 'tree-sitter-typescript';
|
|
11
|
-
import { z } from 'zod';
|
|
12
|
-
import { execa } from 'execa';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
10
|
+
import walk from 'ignore-walk';
|
|
14
11
|
|
|
15
|
-
// index.ts
|
|
16
|
-
|
|
17
|
-
// lib/constants.ts
|
|
18
|
-
var PYTHON_PACKAGE_NAME = "kernel";
|
|
19
|
-
var NODE_PACKAGE_NAME = "@onkernel/sdk";
|
|
20
|
-
async function runForExitCode(command, options = {}) {
|
|
21
|
-
try {
|
|
22
|
-
const cwd = options.cwd ? path3.resolve(options.cwd) : process.cwd();
|
|
23
|
-
const { exitCode } = await execa(command, {
|
|
24
|
-
shell: true,
|
|
25
|
-
cwd,
|
|
26
|
-
stdio: "ignore",
|
|
27
|
-
// Don't show any output
|
|
28
|
-
reject: false
|
|
29
|
-
// Don't throw on non-zero exit code
|
|
30
|
-
});
|
|
31
|
-
return exitCode ?? 1;
|
|
32
|
-
} catch (error) {
|
|
33
|
-
return 1;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async function runInDirectory(command, cwd) {
|
|
37
|
-
const resolvedCwd = path3.resolve(cwd);
|
|
38
|
-
await execa(command, {
|
|
39
|
-
shell: true,
|
|
40
|
-
cwd: resolvedCwd,
|
|
41
|
-
stdio: "inherit"
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
12
|
function getPackageVersion() {
|
|
45
|
-
const
|
|
46
|
-
const __dirname = dirname(__filename);
|
|
47
|
-
const pkgJsonPath = path3.join(__dirname, "..", "package.json");
|
|
13
|
+
const pkgJsonPath = path2.join(__dirname, "..", "..", "..", "package.json");
|
|
48
14
|
const content = fsExtra.readJSONSync(pkgJsonPath);
|
|
49
15
|
if (!content.version) {
|
|
50
16
|
throw new Error("package.json does not contain a version");
|
|
51
17
|
}
|
|
52
18
|
return content.version;
|
|
53
19
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
cron: z.string(),
|
|
61
|
-
uses_browser: z.boolean().optional()
|
|
62
|
-
}),
|
|
63
|
-
z.object({
|
|
64
|
-
type: z.literal("http"),
|
|
65
|
-
name: z.string(),
|
|
66
|
-
path: z.string(),
|
|
67
|
-
method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]),
|
|
68
|
-
uses_browser: z.boolean().optional()
|
|
69
|
-
}),
|
|
70
|
-
z.object({
|
|
71
|
-
type: z.literal("function"),
|
|
72
|
-
name: z.string(),
|
|
73
|
-
uses_browser: z.boolean().optional()
|
|
74
|
-
})
|
|
75
|
-
]);
|
|
76
|
-
z.object({
|
|
77
|
-
entrypoint: z.string(),
|
|
78
|
-
functions: z.array(KernelFunctionSchema)
|
|
79
|
-
});
|
|
80
|
-
function findPythonDecoratedFunctions(filePath) {
|
|
81
|
-
const parser = new Parser();
|
|
82
|
-
parser.setLanguage(Python);
|
|
83
|
-
const code = fs2.readFileSync(filePath, "utf8");
|
|
84
|
-
const tree = parser.parse(code);
|
|
85
|
-
const rootNode = tree.rootNode;
|
|
86
|
-
const result = [];
|
|
87
|
-
const functionNodes = [];
|
|
88
|
-
traverseTree(rootNode, (node) => {
|
|
89
|
-
if (node.type === "function_definition") {
|
|
90
|
-
functionNodes.push(node);
|
|
91
|
-
}
|
|
20
|
+
async function zipDirectory(inputDir, outputZip) {
|
|
21
|
+
const entries = await walk({
|
|
22
|
+
path: inputDir,
|
|
23
|
+
ignoreFiles: [".gitignore", ".dockerignore"],
|
|
24
|
+
includeEmpty: true,
|
|
25
|
+
follow: false
|
|
92
26
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
let curNode = node.previousSibling;
|
|
99
|
-
while (curNode && curNode.type === "decorator") {
|
|
100
|
-
const decoratorNameNode = curNode.child(1);
|
|
101
|
-
if (decoratorNameNode) {
|
|
102
|
-
const decoratorName = decoratorNameNode.text;
|
|
103
|
-
if (decoratorName === "func") {
|
|
104
|
-
decoratorFound = true;
|
|
105
|
-
result.push({
|
|
106
|
-
type: "function",
|
|
107
|
-
name: functionName,
|
|
108
|
-
uses_browser: code.includes("use_browser") && code.includes(`def ${functionName}`)
|
|
109
|
-
});
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
curNode = curNode.previousSibling;
|
|
114
|
-
}
|
|
115
|
-
if (!decoratorFound) {
|
|
116
|
-
const funcStartLine = node.startPosition.row;
|
|
117
|
-
const codeLines = code.split("\n");
|
|
118
|
-
for (let i = Math.max(0, funcStartLine - 3); i < funcStartLine; i++) {
|
|
119
|
-
const line = codeLines[i]?.trim() || "";
|
|
120
|
-
if (line.startsWith("@func")) {
|
|
121
|
-
result.push({
|
|
122
|
-
type: "function",
|
|
123
|
-
name: functionName,
|
|
124
|
-
uses_browser: code.includes("use_browser") && code.includes(`def ${functionName}`)
|
|
125
|
-
});
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return result;
|
|
132
|
-
}
|
|
133
|
-
function traverseTree(node, callback) {
|
|
134
|
-
callback(node);
|
|
135
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
136
|
-
const child = node.child(i);
|
|
137
|
-
if (child) {
|
|
138
|
-
traverseTree(child, callback);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function findTypeScriptDecoratedFunctions(filePath) {
|
|
143
|
-
const parser = new Parser();
|
|
144
|
-
parser.setLanguage(TypeScript.typescript);
|
|
145
|
-
const code = fs2.readFileSync(filePath, "utf8");
|
|
146
|
-
const tree = parser.parse(code);
|
|
147
|
-
const rootNode = tree.rootNode;
|
|
148
|
-
traverseTreeForDebug(rootNode);
|
|
149
|
-
const results = walkTreeForKernelExports(rootNode, code);
|
|
150
|
-
if (results.length === 0 && code.includes("export default Kernel.func")) {
|
|
151
|
-
results.push({
|
|
152
|
-
type: "function",
|
|
153
|
-
name: "default",
|
|
154
|
-
uses_browser: code.includes("useBrowser")
|
|
155
|
-
});
|
|
156
|
-
} else if (results.length === 0 && code.includes("export default Kernel.schedule")) {
|
|
157
|
-
results.push({
|
|
158
|
-
type: "schedule",
|
|
159
|
-
name: "default",
|
|
160
|
-
cron: "* * * * *",
|
|
161
|
-
// Default
|
|
162
|
-
uses_browser: code.includes("useBrowser")
|
|
163
|
-
});
|
|
164
|
-
} else if (results.length === 0 && code.includes("export default Kernel.endpoint")) {
|
|
165
|
-
results.push({
|
|
166
|
-
type: "http",
|
|
167
|
-
name: "default",
|
|
168
|
-
path: "/",
|
|
169
|
-
method: "GET",
|
|
170
|
-
uses_browser: code.includes("useBrowser")
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
return results;
|
|
174
|
-
}
|
|
175
|
-
function traverseTreeForDebug(node, level) {
|
|
176
|
-
for (let i = 0; i < node.namedChildCount; i++) {
|
|
177
|
-
const child = node.namedChild(i);
|
|
178
|
-
traverseTreeForDebug(child);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
function walkTreeForKernelExports(rootNode, code) {
|
|
182
|
-
const results = [];
|
|
183
|
-
for (let i = 0; i < rootNode.namedChildCount; i++) {
|
|
184
|
-
const node = rootNode.namedChild(i);
|
|
185
|
-
if (!node) continue;
|
|
186
|
-
if (node.type === "export_statement") {
|
|
187
|
-
if (node.text.startsWith("export default")) {
|
|
188
|
-
const declaration = node.childForFieldName("declaration");
|
|
189
|
-
if (!declaration) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (declaration.type === "call_expression") {
|
|
193
|
-
const callExpr = declaration;
|
|
194
|
-
if (callExpr.childCount > 0 && callExpr.child(0)?.type === "member_expression") {
|
|
195
|
-
const memberExpr = callExpr.child(0);
|
|
196
|
-
const object = memberExpr.childForFieldName("object");
|
|
197
|
-
const property = memberExpr.childForFieldName("property");
|
|
198
|
-
if (object?.text === "Kernel") {
|
|
199
|
-
const method = property?.text;
|
|
200
|
-
const usesBrowser = code.includes("useBrowser");
|
|
201
|
-
if (method === "func") {
|
|
202
|
-
results.push({
|
|
203
|
-
type: "function",
|
|
204
|
-
name: "default",
|
|
205
|
-
uses_browser: usesBrowser
|
|
206
|
-
});
|
|
207
|
-
} else if (method === "schedule") {
|
|
208
|
-
results.push({
|
|
209
|
-
type: "schedule",
|
|
210
|
-
name: "default",
|
|
211
|
-
cron: "* * * * *",
|
|
212
|
-
// Default
|
|
213
|
-
uses_browser: usesBrowser
|
|
214
|
-
});
|
|
215
|
-
} else if (method === "endpoint") {
|
|
216
|
-
results.push({
|
|
217
|
-
type: "http",
|
|
218
|
-
name: "default",
|
|
219
|
-
path: "/",
|
|
220
|
-
method: "GET",
|
|
221
|
-
uses_browser: usesBrowser
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
} else if (node.type === "lexical_declaration") {
|
|
229
|
-
const decl = node.childForFieldName("declaration");
|
|
230
|
-
if (!decl) continue;
|
|
231
|
-
for (let j = 0; j < decl.namedChildCount; j++) {
|
|
232
|
-
const varDecl = decl.namedChild(j);
|
|
233
|
-
if (!varDecl) continue;
|
|
234
|
-
const varNameNode = varDecl.childForFieldName("name");
|
|
235
|
-
const valueNode = varDecl.childForFieldName("value");
|
|
236
|
-
if (!varNameNode || !valueNode) continue;
|
|
237
|
-
if (valueNode.type === "call_expression" && valueNode.child(0)?.type === "member_expression") {
|
|
238
|
-
const memberExpr = valueNode.child(0);
|
|
239
|
-
const object = memberExpr.childForFieldName("object");
|
|
240
|
-
const property = memberExpr.childForFieldName("property");
|
|
241
|
-
if (object?.text === "Kernel") {
|
|
242
|
-
const method = property?.text;
|
|
243
|
-
const name = varNameNode.text;
|
|
244
|
-
const usesBrowser = code.includes("useBrowser");
|
|
245
|
-
if (method === "func") {
|
|
246
|
-
results.push({
|
|
247
|
-
type: "function",
|
|
248
|
-
name,
|
|
249
|
-
uses_browser: usesBrowser
|
|
250
|
-
});
|
|
251
|
-
} else if (method === "schedule") {
|
|
252
|
-
let cronExpression = "* * * * *";
|
|
253
|
-
results.push({
|
|
254
|
-
type: "schedule",
|
|
255
|
-
name,
|
|
256
|
-
cron: cronExpression,
|
|
257
|
-
uses_browser: usesBrowser
|
|
258
|
-
});
|
|
259
|
-
} else if (method === "endpoint") {
|
|
260
|
-
let path5 = "/";
|
|
261
|
-
let method2 = "GET";
|
|
262
|
-
results.push({
|
|
263
|
-
type: "http",
|
|
264
|
-
name,
|
|
265
|
-
path: path5,
|
|
266
|
-
method: method2,
|
|
267
|
-
uses_browser: usesBrowser
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return results;
|
|
276
|
-
}
|
|
277
|
-
async function packageApp(config) {
|
|
278
|
-
const { sourceDir, entrypoint, targetDir } = config;
|
|
279
|
-
if (fs2.existsSync(targetDir)) {
|
|
280
|
-
fs2.rmSync(targetDir, { recursive: true, force: true });
|
|
281
|
-
}
|
|
282
|
-
fs2.mkdirSync(targetDir, { recursive: true });
|
|
283
|
-
const entrypointRelative = path3.relative(sourceDir, entrypoint);
|
|
284
|
-
const extension = path3.extname(entrypoint);
|
|
285
|
-
let functions = [];
|
|
286
|
-
if (extension === ".py") {
|
|
287
|
-
functions = findPythonDecoratedFunctions(entrypoint);
|
|
288
|
-
} else if (extension === ".ts") {
|
|
289
|
-
functions = findTypeScriptDecoratedFunctions(entrypoint);
|
|
290
|
-
} else {
|
|
291
|
-
throw new Error(`Unsupported file extension: ${extension}`);
|
|
292
|
-
}
|
|
293
|
-
const kernelJson = {
|
|
294
|
-
entrypoint: entrypointRelative,
|
|
295
|
-
functions
|
|
296
|
-
};
|
|
297
|
-
const kernelJsonPath = path3.join(targetDir, "kernel.json");
|
|
298
|
-
fs2.writeFileSync(kernelJsonPath, JSON.stringify(kernelJson, null, 2));
|
|
299
|
-
copyDirectoryContents(sourceDir, targetDir);
|
|
300
|
-
if (extension === ".ts") {
|
|
301
|
-
if (config.tsOptions?.kernelDependencyOverride) {
|
|
302
|
-
const packageJsonPath = path3.join(targetDir, "package.json");
|
|
303
|
-
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
|
|
304
|
-
packageJson.dependencies[NODE_PACKAGE_NAME] = config.tsOptions.kernelDependencyOverride;
|
|
305
|
-
fs2.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
306
|
-
}
|
|
307
|
-
await runInDirectory(`pnpm i`, targetDir);
|
|
308
|
-
}
|
|
309
|
-
return kernelJsonPath;
|
|
310
|
-
}
|
|
311
|
-
function copyDirectoryContents(sourceDir, targetDir) {
|
|
312
|
-
fsExtra.copySync(sourceDir, targetDir, {
|
|
313
|
-
filter: (src) => {
|
|
314
|
-
const basename = path3.basename(src);
|
|
315
|
-
return ![".build", "node_modules", ".git", ".mypy_cache", ".venv", "__pycache__"].includes(
|
|
316
|
-
basename
|
|
317
|
-
);
|
|
318
|
-
},
|
|
319
|
-
overwrite: true
|
|
27
|
+
const output = fs.createWriteStream(outputZip);
|
|
28
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
29
|
+
const finalizePromise = new Promise((resolve, reject) => {
|
|
30
|
+
output.on("close", resolve);
|
|
31
|
+
archive.on("error", reject);
|
|
320
32
|
});
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
"invoke"
|
|
331
|
-
);
|
|
332
|
-
function overwriteKernelDendencyInPyproject(pyprojectPath, kernelDependencyOverride) {
|
|
333
|
-
const pyproject = parse(fs2.readFileSync(pyprojectPath, "utf8"));
|
|
334
|
-
if (!pyproject.project) {
|
|
335
|
-
pyproject.project = { dependencies: [] };
|
|
336
|
-
} else if (!pyproject.project.dependencies) {
|
|
337
|
-
pyproject.project.dependencies = [];
|
|
338
|
-
}
|
|
339
|
-
pyproject.project.dependencies = pyproject.project.dependencies.filter((dep) => {
|
|
340
|
-
return !dep.startsWith(PYTHON_PACKAGE_NAME);
|
|
341
|
-
});
|
|
342
|
-
if (!kernelDependencyOverride.startsWith("/") && !kernelDependencyOverride.startsWith("./") && !kernelDependencyOverride.startsWith("../")) {
|
|
343
|
-
pyproject.project.dependencies.push(kernelDependencyOverride);
|
|
344
|
-
} else {
|
|
345
|
-
pyproject.project.dependencies.push(PYTHON_PACKAGE_NAME);
|
|
346
|
-
if (!pyproject.tool) {
|
|
347
|
-
pyproject.tool = {};
|
|
348
|
-
}
|
|
349
|
-
if (!pyproject.tool.uv) {
|
|
350
|
-
pyproject.tool.uv = {};
|
|
351
|
-
}
|
|
352
|
-
if (!pyproject.tool.uv.sources) {
|
|
353
|
-
pyproject.tool.uv.sources = {};
|
|
354
|
-
}
|
|
355
|
-
pyproject.tool.uv.sources.kernel = { path: kernelDependencyOverride };
|
|
356
|
-
}
|
|
357
|
-
fs2.writeFileSync(pyprojectPath, stringify(pyproject));
|
|
358
|
-
}
|
|
359
|
-
function overwriteKernelDendencyInRequirementsTxt(requirementsTxtPath, kernelDependencyOverride) {
|
|
360
|
-
const requirementsTxt = fs2.readFileSync(requirementsTxtPath, "utf8");
|
|
361
|
-
const requirementsTxtLines = requirementsTxt.split("\n");
|
|
362
|
-
const newRequirementsTxtLines = requirementsTxtLines.filter((line) => {
|
|
363
|
-
return !line.startsWith(PYTHON_PACKAGE_NAME);
|
|
364
|
-
});
|
|
365
|
-
if (!kernelDependencyOverride.startsWith("/") && !kernelDependencyOverride.startsWith("./") && !kernelDependencyOverride.startsWith("../")) {
|
|
366
|
-
newRequirementsTxtLines.push(`${PYTHON_PACKAGE_NAME} @ file:${kernelDependencyOverride}`);
|
|
367
|
-
} else {
|
|
368
|
-
newRequirementsTxtLines.push(kernelDependencyOverride);
|
|
369
|
-
}
|
|
370
|
-
fs2.writeFileSync(requirementsTxtPath, newRequirementsTxtLines.join("\n"));
|
|
371
|
-
}
|
|
372
|
-
async function preparePythonInvokeEnvironment(options) {
|
|
373
|
-
const { appName, appDir, bootLoaderDir} = options;
|
|
374
|
-
const invokeDir = path3.join(INVOKE_BASE_DIR, appName);
|
|
375
|
-
if (fs2.existsSync(invokeDir)) {
|
|
376
|
-
fs2.rmSync(invokeDir, { recursive: true, force: true });
|
|
377
|
-
}
|
|
378
|
-
fs2.mkdirSync(invokeDir, { recursive: true });
|
|
379
|
-
copyDirectoryContents(bootLoaderDir, invokeDir);
|
|
380
|
-
if (options.kernelDependencyOverride) {
|
|
381
|
-
if (fs2.existsSync(path3.join(invokeDir, "pyproject.toml"))) {
|
|
382
|
-
overwriteKernelDendencyInPyproject(
|
|
383
|
-
path3.join(invokeDir, "pyproject.toml"),
|
|
384
|
-
options.kernelDependencyOverride
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
if (fs2.existsSync(path3.join(appDir, "pyproject.toml"))) {
|
|
388
|
-
overwriteKernelDendencyInPyproject(
|
|
389
|
-
path3.join(appDir, "pyproject.toml"),
|
|
390
|
-
options.kernelDependencyOverride
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
if (fs2.existsSync(path3.join(appDir, "requirements.txt"))) {
|
|
394
|
-
overwriteKernelDendencyInRequirementsTxt(
|
|
395
|
-
path3.join(appDir, "requirements.txt"),
|
|
396
|
-
options.kernelDependencyOverride
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
if (!fs2.existsSync(path3.join(appDir, "requirements.txt"))) {
|
|
401
|
-
await runInDirectory(
|
|
402
|
-
`/bin/bash -c 'uv venv && source .venv/bin/activate && uv sync --no-cache && uv pip freeze > requirements.txt'`,
|
|
403
|
-
appDir
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
await runInDirectory(
|
|
407
|
-
`/bin/bash -c 'uv venv && source .venv/bin/activate && uv add -r ${path3.join(appDir, "requirements.txt")}'`,
|
|
408
|
-
invokeDir
|
|
409
|
-
);
|
|
410
|
-
return invokeDir;
|
|
411
|
-
}
|
|
412
|
-
async function prepareTypeScriptInvokeEnvironment(options) {
|
|
413
|
-
const { appName, appDir, bootLoaderDir } = options;
|
|
414
|
-
const invokeDir = path3.join(INVOKE_BASE_DIR, appName);
|
|
415
|
-
if (fs2.existsSync(invokeDir)) {
|
|
416
|
-
fs2.rmSync(invokeDir, { recursive: true, force: true });
|
|
417
|
-
}
|
|
418
|
-
fs2.mkdirSync(invokeDir, { recursive: true });
|
|
419
|
-
copyDirectoryContents(bootLoaderDir, invokeDir);
|
|
420
|
-
if (options.kernelDependencyOverride) {
|
|
421
|
-
const packageJsonPath = path3.join(invokeDir, "package.json");
|
|
422
|
-
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
|
|
423
|
-
packageJson.dependencies[NODE_PACKAGE_NAME] = options.kernelDependencyOverride;
|
|
424
|
-
fs2.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
425
|
-
}
|
|
426
|
-
const appPackageJsonPath = path3.join(appDir, "package.json");
|
|
427
|
-
const invokePackageJsonPath = path3.join(invokeDir, "package.json");
|
|
428
|
-
if (fs2.existsSync(appPackageJsonPath) && fs2.existsSync(invokePackageJsonPath)) {
|
|
429
|
-
const appPackageJson = JSON.parse(fs2.readFileSync(appPackageJsonPath, "utf8"));
|
|
430
|
-
const invokePackageJson = JSON.parse(fs2.readFileSync(invokePackageJsonPath, "utf8"));
|
|
431
|
-
if (appPackageJson.dependencies) {
|
|
432
|
-
if (!invokePackageJson.dependencies) {
|
|
433
|
-
invokePackageJson.dependencies = {};
|
|
434
|
-
}
|
|
435
|
-
for (const [depName, depVersion] of Object.entries(appPackageJson.dependencies)) {
|
|
436
|
-
if (!invokePackageJson.dependencies[depName]) {
|
|
437
|
-
invokePackageJson.dependencies[depName] = depVersion;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
if (appPackageJson.devDependencies) {
|
|
442
|
-
if (!invokePackageJson.devDependencies) {
|
|
443
|
-
invokePackageJson.devDependencies = {};
|
|
444
|
-
}
|
|
445
|
-
for (const [depName, depVersion] of Object.entries(appPackageJson.devDependencies)) {
|
|
446
|
-
if (!invokePackageJson.devDependencies[depName]) {
|
|
447
|
-
invokePackageJson.devDependencies[depName] = depVersion;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
33
|
+
archive.pipe(output);
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
const fullPath = path2.join(inputDir, entry);
|
|
36
|
+
const stat = fs.statSync(fullPath);
|
|
37
|
+
const archivePath = entry.split(path2.sep).join("/");
|
|
38
|
+
if (stat.isFile()) {
|
|
39
|
+
archive.file(fullPath, { name: archivePath });
|
|
40
|
+
} else if (stat.isDirectory()) {
|
|
41
|
+
archive.append("", { name: archivePath.endsWith("/") ? archivePath : archivePath + "/" });
|
|
450
42
|
}
|
|
451
|
-
fs2.writeFileSync(invokePackageJsonPath, JSON.stringify(invokePackageJson, null, 2));
|
|
452
|
-
console.log("Merged dependencies in package.json");
|
|
453
|
-
await runInDirectory("pnpm i", invokeDir);
|
|
454
43
|
}
|
|
455
|
-
|
|
44
|
+
await archive.finalize();
|
|
45
|
+
await finalizePromise;
|
|
456
46
|
}
|
|
457
47
|
|
|
458
48
|
// index.ts
|
|
459
49
|
var program = new Command();
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
program.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
50
|
+
if (process.argv.length === 3 && ["-v", "--version"].includes(process.argv[2])) {
|
|
51
|
+
console.log(getPackageVersion());
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
program.name("kernel").description("CLI for Kernel deployment and invocation");
|
|
55
|
+
program.command("deploy").description("Deploy a Kernel application").argument("<entrypoint>", "Path to entrypoint file (TypeScript or Python)").option("--version <version>", "Specify a version for the app (default: latest)").option("--force", "Allow overwrite of an existing version with the same name").action(async (entrypoint, options) => {
|
|
56
|
+
let { version: versionArg, force } = options;
|
|
57
|
+
if (!versionArg) {
|
|
58
|
+
versionArg = "latest";
|
|
59
|
+
if (force && force !== "true") {
|
|
60
|
+
console.error("Error: --force must be used when version is latest");
|
|
61
|
+
process.exit(1);
|
|
62
|
+
} else if (!force) {
|
|
63
|
+
force = "true";
|
|
64
|
+
}
|
|
473
65
|
}
|
|
474
|
-
const resolvedEntrypoint =
|
|
475
|
-
if (!
|
|
66
|
+
const resolvedEntrypoint = path2.resolve(entrypoint);
|
|
67
|
+
if (!fs.existsSync(resolvedEntrypoint)) {
|
|
476
68
|
console.error(`Error: Entrypoint ${resolvedEntrypoint} doesn't exist`);
|
|
477
69
|
process.exit(1);
|
|
478
70
|
}
|
|
479
|
-
const sourceDir =
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
71
|
+
const sourceDir = path2.dirname(resolvedEntrypoint);
|
|
72
|
+
if (!process.env["KERNEL_API_KEY"]) {
|
|
73
|
+
console.error("Error: KERNEL_API_KEY environment variable is not set");
|
|
74
|
+
console.error("Please set your Kernel API key using: export KERNEL_API_KEY=your_api_key");
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const client = new Kernel({
|
|
78
|
+
apiKey: process.env["KERNEL_API_KEY"],
|
|
79
|
+
baseURL: process.env["KERNEL_BASE_URL"] || "http://localhost:3001"
|
|
488
80
|
});
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
console.
|
|
81
|
+
console.log(chalk.green(`Compressing files...`));
|
|
82
|
+
const tmpZipFile = tmp.fileSync({ postfix: ".zip" });
|
|
83
|
+
try {
|
|
84
|
+
await zipDirectory(path2.join(sourceDir), tmpZipFile.name);
|
|
85
|
+
console.log(chalk.green(`Deploying app (version: ${versionArg})...`));
|
|
86
|
+
const response = await client.apps.deploy(
|
|
87
|
+
{
|
|
88
|
+
file: createReadStream(tmpZipFile.name),
|
|
89
|
+
version: versionArg,
|
|
90
|
+
force,
|
|
91
|
+
entrypointRelPath: path2.relative(sourceDir, resolvedEntrypoint)
|
|
92
|
+
},
|
|
93
|
+
{ maxRetries: 0 }
|
|
94
|
+
);
|
|
95
|
+
if (!response.success) {
|
|
96
|
+
console.error("Error deploying to Kernel:", response.message);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
for (const app of response.apps) {
|
|
100
|
+
console.log(
|
|
101
|
+
chalk.green(
|
|
102
|
+
`App "${app.name}" successfully deployed to Kernel with action${app.actions.length > 1 ? "s" : ""}: ${app.actions.map((a) => a.name).join(", ")}`
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
console.log(
|
|
106
|
+
`You can invoke it with: kernel invoke${versionArg !== "latest" ? ` --version ${versionArg}` : ""} ${quoteIfNeeded(app.name)} ${quoteIfNeeded(app.actions[0].name)} '{ ... JSON payload ... }'`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error("Error deploying to Kernel:", error);
|
|
494
111
|
process.exit(1);
|
|
112
|
+
} finally {
|
|
113
|
+
tmpZipFile.removeCallback();
|
|
495
114
|
}
|
|
496
115
|
});
|
|
497
|
-
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
process.exit(1);
|
|
116
|
+
function quoteIfNeeded(str) {
|
|
117
|
+
if (str.includes(" ")) {
|
|
118
|
+
return `"${str}"`;
|
|
501
119
|
}
|
|
120
|
+
return str;
|
|
121
|
+
}
|
|
122
|
+
program.command("invoke").description("Invoke a deployed Kernel application").option("--version <version>", "Specify a version of the app to invoke").argument("<app_name>", "Name of the application to invoke").argument("<action_name>", "Name of the action to invoke").argument("<payload>", "JSON payload to send to the application").action(async (appName, actionName, payload, options) => {
|
|
502
123
|
let parsedPayload;
|
|
503
124
|
try {
|
|
504
125
|
parsedPayload = JSON.parse(payload);
|
|
@@ -506,254 +127,30 @@ program.command("invoke").description("Invoke a deployed Kernel application").op
|
|
|
506
127
|
console.error("Error: Invalid JSON payload");
|
|
507
128
|
process.exit(1);
|
|
508
129
|
}
|
|
509
|
-
if (!
|
|
510
|
-
console.
|
|
130
|
+
if (!process.env["KERNEL_API_KEY"]) {
|
|
131
|
+
console.error("Error: KERNEL_API_KEY environment variable is not set");
|
|
132
|
+
console.error("Please set your Kernel API key using: export KERNEL_API_KEY=your_api_key");
|
|
511
133
|
process.exit(1);
|
|
512
134
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
135
|
+
const client = new Kernel({
|
|
136
|
+
apiKey: process.env["KERNEL_API_KEY"],
|
|
137
|
+
baseURL: process.env["KERNEL_BASE_URL"] || "http://localhost:3001"
|
|
138
|
+
});
|
|
139
|
+
console.log(`Invoking "${appName}" with action "${actionName}" and payload:`);
|
|
516
140
|
console.log(JSON.stringify(parsedPayload, null, 2));
|
|
517
|
-
const localAppsDir = path3.join(LOCAL_APPS_BASE_DIR, options.name);
|
|
518
|
-
if (!fs2.existsSync(localAppsDir)) {
|
|
519
|
-
console.error(`Error: App "${options.name}" not found in ${localAppsDir}`);
|
|
520
|
-
console.error("Did you deploy it with --local first?");
|
|
521
|
-
process.exit(1);
|
|
522
|
-
}
|
|
523
|
-
const kernelJsonPath = path3.join(localAppsDir, "kernel.json");
|
|
524
|
-
if (!fs2.existsSync(kernelJsonPath)) {
|
|
525
|
-
console.error(`Error: kernel.json not found in ${localAppsDir}`);
|
|
526
|
-
process.exit(1);
|
|
527
|
-
}
|
|
528
|
-
const kernelJson = JSON.parse(fs2.readFileSync(kernelJsonPath, "utf8"));
|
|
529
|
-
const entrypointExt = path3.extname(kernelJson.entrypoint);
|
|
530
|
-
const isPythonApp = entrypointExt.toLowerCase() === ".py";
|
|
531
|
-
const isTypeScriptApp = entrypointExt.toLowerCase() === ".ts";
|
|
532
141
|
try {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
142
|
+
const response = await client.apps.invoke({
|
|
143
|
+
appName,
|
|
144
|
+
actionName,
|
|
145
|
+
payload,
|
|
146
|
+
...options.version && { version: options.version }
|
|
147
|
+
});
|
|
148
|
+
console.log("Result:");
|
|
149
|
+
console.log(JSON.stringify(JSON.parse(response.output || "{}"), null, 2));
|
|
540
150
|
} catch (error) {
|
|
541
151
|
console.error("Error invoking application:", error);
|
|
542
152
|
process.exit(1);
|
|
543
153
|
}
|
|
154
|
+
return;
|
|
544
155
|
});
|
|
545
|
-
async function waitForStartupMessage(childProcess, timeoutMs = 3e4) {
|
|
546
|
-
return new Promise(async (resolve, reject) => {
|
|
547
|
-
const timeout = setTimeout(() => {
|
|
548
|
-
reject(new Error("Timeout waiting for application startup."));
|
|
549
|
-
}, timeoutMs);
|
|
550
|
-
const reader = childProcess.stderr.getReader();
|
|
551
|
-
const decoder = new TextDecoder();
|
|
552
|
-
try {
|
|
553
|
-
while (true) {
|
|
554
|
-
const { done, value } = await reader.read();
|
|
555
|
-
if (done) break;
|
|
556
|
-
const text = decoder.decode(value);
|
|
557
|
-
process.stderr.write(text);
|
|
558
|
-
if (text.includes("Application startup complete.") || text.includes("Kernel application running")) {
|
|
559
|
-
clearTimeout(timeout);
|
|
560
|
-
resolve();
|
|
561
|
-
break;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
} finally {
|
|
565
|
-
reader.releaseLock();
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
async function getBootPyPath() {
|
|
570
|
-
const bootPyDir = BOOT_PY_PATH;
|
|
571
|
-
if (!fs2.existsSync(bootPyDir)) {
|
|
572
|
-
console.error(`Error: Python boot loader not found at ${bootPyDir}`);
|
|
573
|
-
console.error("Please ensure the boot-py package is installed");
|
|
574
|
-
process.exit(1);
|
|
575
|
-
}
|
|
576
|
-
return bootPyDir;
|
|
577
|
-
}
|
|
578
|
-
async function getBootTsPath() {
|
|
579
|
-
const bootTsDir = BOOT_TS_PATH;
|
|
580
|
-
if (!fs2.existsSync(bootTsDir)) {
|
|
581
|
-
console.error(`Error: TypeScript boot loader not found at ${bootTsDir}`);
|
|
582
|
-
console.error("Please ensure the boot-ts package is installed");
|
|
583
|
-
process.exit(1);
|
|
584
|
-
}
|
|
585
|
-
return bootTsDir;
|
|
586
|
-
}
|
|
587
|
-
async function isUvInstalled() {
|
|
588
|
-
const exitCode = await runForExitCode("uv --version");
|
|
589
|
-
return exitCode === 0;
|
|
590
|
-
}
|
|
591
|
-
async function invokePythonApp(appDir, kernelJson, payload, options) {
|
|
592
|
-
const uvInstalled = await isUvInstalled();
|
|
593
|
-
if (!uvInstalled) {
|
|
594
|
-
console.error("Error: uv is not installed. Please install it with:");
|
|
595
|
-
console.error(" curl -LsSf https://astral.sh/uv/install.sh | sh");
|
|
596
|
-
process.exit(1);
|
|
597
|
-
}
|
|
598
|
-
const bootPyDir = await getBootPyPath();
|
|
599
|
-
const invokeDir = await preparePythonInvokeEnvironment({
|
|
600
|
-
appName: options.name,
|
|
601
|
-
appDir,
|
|
602
|
-
bootLoaderDir: bootPyDir,
|
|
603
|
-
kernelDependencyOverride: KERNEL_PYTHON_SDK_OVERRIDE
|
|
604
|
-
});
|
|
605
|
-
const port = await getPort();
|
|
606
|
-
console.log(`Starting Python boot server for ${appDir} on port ${port}...`);
|
|
607
|
-
const pythonProcess = Bun.spawn(
|
|
608
|
-
["uv", "run", "--no-cache", "python", "main.py", appDir, "--port", port.toString()],
|
|
609
|
-
{
|
|
610
|
-
cwd: invokeDir,
|
|
611
|
-
stdio: ["inherit", "inherit", "pipe"],
|
|
612
|
-
env: {
|
|
613
|
-
...process.env,
|
|
614
|
-
// Pass through any browser-related environment variables
|
|
615
|
-
...options.withBrowser ? { KERNEL_WITH_BROWSER: "true" } : {}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
);
|
|
619
|
-
console.log(`Waiting for Python application to start...`);
|
|
620
|
-
try {
|
|
621
|
-
await waitForStartupMessage(pythonProcess);
|
|
622
|
-
} catch (error) {
|
|
623
|
-
console.error("Error while waiting for application to start:", error);
|
|
624
|
-
process.exit(1);
|
|
625
|
-
}
|
|
626
|
-
let serverReached = false;
|
|
627
|
-
try {
|
|
628
|
-
try {
|
|
629
|
-
const healthCheck = await fetch(`http://localhost:${port}/`, {
|
|
630
|
-
method: "GET"
|
|
631
|
-
}).catch(() => null);
|
|
632
|
-
if (!healthCheck) {
|
|
633
|
-
throw new Error(`Could not connect to boot server at http://localhost:${port}/`);
|
|
634
|
-
}
|
|
635
|
-
serverReached = true;
|
|
636
|
-
} catch (error) {
|
|
637
|
-
console.error("Error connecting to boot server:", error);
|
|
638
|
-
console.error("The boot server might not have started correctly.");
|
|
639
|
-
process.exit(1);
|
|
640
|
-
}
|
|
641
|
-
let funcName = options.func;
|
|
642
|
-
if (!funcName) {
|
|
643
|
-
const firstFunction = kernelJson.functions.find((f) => f.type === "function");
|
|
644
|
-
if (firstFunction) {
|
|
645
|
-
funcName = firstFunction.name;
|
|
646
|
-
} else {
|
|
647
|
-
throw new Error("No functions found in the application");
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
console.log(`Invoking function "${funcName}"...`);
|
|
651
|
-
const response = await fetch(`http://localhost:${port}/function/${funcName}`, {
|
|
652
|
-
method: "POST",
|
|
653
|
-
headers: {
|
|
654
|
-
"Content-Type": "application/json"
|
|
655
|
-
},
|
|
656
|
-
body: JSON.stringify(payload)
|
|
657
|
-
}).catch((error) => {
|
|
658
|
-
console.error(`Failed to connect to function endpoint: ${error.message}`);
|
|
659
|
-
throw new Error(
|
|
660
|
-
`Could not connect to function endpoint at http://localhost:${port}/function/${funcName}`
|
|
661
|
-
);
|
|
662
|
-
});
|
|
663
|
-
if (!response.ok) {
|
|
664
|
-
const errorText = await response.text().catch(() => "Unknown error");
|
|
665
|
-
throw new Error(`HTTP error ${response.status}: ${errorText}`);
|
|
666
|
-
}
|
|
667
|
-
const result = await response.json();
|
|
668
|
-
console.log("Result:", JSON.stringify(result, null, 2));
|
|
669
|
-
return result;
|
|
670
|
-
} finally {
|
|
671
|
-
if (serverReached) {
|
|
672
|
-
console.log("Shutting down boot server...");
|
|
673
|
-
}
|
|
674
|
-
pythonProcess.kill();
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
async function invokeTypeScriptApp(appDir, kernelJson, payload, options) {
|
|
678
|
-
const bootTsDir = await getBootTsPath();
|
|
679
|
-
const invokeDir = await prepareTypeScriptInvokeEnvironment({
|
|
680
|
-
appName: options.name,
|
|
681
|
-
appDir,
|
|
682
|
-
bootLoaderDir: bootTsDir,
|
|
683
|
-
kernelDependencyOverride: KERNEL_TS_SDK_OVERRIDE
|
|
684
|
-
});
|
|
685
|
-
const port = await getPort();
|
|
686
|
-
console.log(`Starting TypeScript boot server for ${appDir} on port ${port}...`);
|
|
687
|
-
const tsProcess = Bun.spawn(["./node_modules/.bin/tsx", "index.ts", appDir, port.toString()], {
|
|
688
|
-
cwd: invokeDir,
|
|
689
|
-
stdio: ["inherit", "inherit", "pipe"],
|
|
690
|
-
env: {
|
|
691
|
-
...process.env,
|
|
692
|
-
// Pass through any browser-related environment variables
|
|
693
|
-
...options.withBrowser ? { KERNEL_WITH_BROWSER: "true" } : {}
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
tsProcess.exited.then((exitCode) => {
|
|
697
|
-
if (exitCode !== 0) {
|
|
698
|
-
console.error(`Boot server exited with code ${exitCode}`);
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
|
-
console.log(`Waiting for TypeScript application to start...`);
|
|
702
|
-
try {
|
|
703
|
-
await waitForStartupMessage(tsProcess);
|
|
704
|
-
} catch (error) {
|
|
705
|
-
console.error("Error while waiting for application to start:", error);
|
|
706
|
-
process.exit(1);
|
|
707
|
-
}
|
|
708
|
-
let serverReached = false;
|
|
709
|
-
try {
|
|
710
|
-
try {
|
|
711
|
-
const healthCheck = await fetch(`http://localhost:${port}/`, {
|
|
712
|
-
method: "GET"
|
|
713
|
-
}).catch(() => null);
|
|
714
|
-
if (!healthCheck) {
|
|
715
|
-
throw new Error(`Could not connect to boot server at http://localhost:${port}/`);
|
|
716
|
-
}
|
|
717
|
-
serverReached = true;
|
|
718
|
-
} catch (error) {
|
|
719
|
-
console.error("Error connecting to boot server:", error);
|
|
720
|
-
console.error("The boot server might not have started correctly.");
|
|
721
|
-
process.exit(1);
|
|
722
|
-
}
|
|
723
|
-
let funcName = options.func;
|
|
724
|
-
if (!funcName) {
|
|
725
|
-
const firstFunction = kernelJson.functions.find((f) => f.type === "function");
|
|
726
|
-
if (firstFunction) {
|
|
727
|
-
funcName = firstFunction.name;
|
|
728
|
-
} else {
|
|
729
|
-
throw new Error("No functions found in the application");
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
console.log(`Invoking function "${funcName}"...`);
|
|
733
|
-
const response = await fetch(`http://localhost:${port}/function/${funcName}`, {
|
|
734
|
-
method: "POST",
|
|
735
|
-
headers: {
|
|
736
|
-
"Content-Type": "application/json"
|
|
737
|
-
},
|
|
738
|
-
body: JSON.stringify(payload)
|
|
739
|
-
}).catch((error) => {
|
|
740
|
-
console.error(`Failed to connect to function endpoint: ${error.message}`);
|
|
741
|
-
throw new Error(
|
|
742
|
-
`Could not connect to function endpoint at http://localhost:${port}/function/${funcName}`
|
|
743
|
-
);
|
|
744
|
-
});
|
|
745
|
-
if (!response.ok) {
|
|
746
|
-
const errorText = await response.text().catch(() => "Unknown error");
|
|
747
|
-
throw new Error(`HTTP error ${response.status}: ${errorText}`);
|
|
748
|
-
}
|
|
749
|
-
const result = await response.json();
|
|
750
|
-
console.log("Result:", JSON.stringify(result, null, 2));
|
|
751
|
-
return result;
|
|
752
|
-
} finally {
|
|
753
|
-
if (serverReached) {
|
|
754
|
-
console.log("Shutting down boot server...");
|
|
755
|
-
}
|
|
756
|
-
tsProcess.kill();
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
156
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onkernel/cli",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"description": "Kernel CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "index.ts",
|
|
@@ -35,20 +35,20 @@
|
|
|
35
35
|
],
|
|
36
36
|
"author": "",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@
|
|
38
|
+
"@onkernel/sdk": "0.1.0-alpha.9",
|
|
39
|
+
"@types/archiver": "^6.0.3",
|
|
40
|
+
"@types/ignore-walk": "^4.0.3",
|
|
41
|
+
"archiver": "^7.0.1",
|
|
42
|
+
"chalk": "^5.4.1",
|
|
39
43
|
"commander": "^13.1.0",
|
|
40
|
-
"execa": "^9.5.2",
|
|
41
44
|
"fs-extra": "^11.3.0",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"tree-sitter-python": "^0.23.6",
|
|
46
|
-
"tree-sitter-typescript": "^0.23.2",
|
|
47
|
-
"type-fest": "^4.40.1",
|
|
48
|
-
"zod": "^3.24.3"
|
|
45
|
+
"ignore-walk": "^7.0.0",
|
|
46
|
+
"tmp": "^0.2.3",
|
|
47
|
+
"type-fest": "^4.40.1"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
|
-
"@types/
|
|
50
|
+
"@types/fs-extra": "^11.0.4",
|
|
51
|
+
"@types/tmp": "^0.2.6",
|
|
52
52
|
"tsup": "^8.4.0"
|
|
53
53
|
}
|
|
54
54
|
}
|