@onkernel/cli 0.0.2 → 0.1.3

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 CHANGED
@@ -1,15 +1,112 @@
1
- # cli
1
+ # Kernel CLI
2
2
 
3
- To install dependencies:
3
+ A command-line tool for deploying and invoking Kernel applications.
4
+
5
+ ## Installation
4
6
 
5
7
  ```bash
6
- bun install
8
+ brew tap onkernel/homebrew-tap
9
+ brew install kernel
7
10
  ```
8
11
 
9
- To run:
12
+ ## Development Prerequisites
13
+
14
+ Install the following tools:
15
+
16
+ - Go 1.22+ ( https://go.dev/doc/install )
17
+ - [Goreleaser](https://goreleaser.com/install/)
18
+ - [chglog](https://github.com/goreleaser/chglog)
19
+
20
+ Compile the CLI:
21
+
22
+ ```bash
23
+ make build # compiles the binary to ./bin/kernel
24
+ ```
25
+
26
+ Run the CLI:
27
+
28
+ ```bash
29
+ ./bin/kernel --help
30
+ ```
31
+
32
+ ## Development workflow
33
+
34
+ Useful make targets:
35
+
36
+ - `make build` – compile the project to `./bin/kernel`
37
+ - `make test` – execute unit tests
38
+ - `make lint` – run the linter (requires `golangci-lint`)
39
+ - `make changelog` – generate/update the `CHANGELOG.md` file using **chglog**
40
+ - `make release` – create a release using **goreleaser** (builds archives, homebrew formula, etc. See below)
41
+
42
+ ### Releasing a new version
43
+
44
+ Prerequisites:
45
+
46
+ - Make sure you have `goreleaser` _pro_ installed via `brew install goreleaser/tap/goreleaser-pro`. You will need a license key (in 1pw), and then `export GORELEASER_KEY=<the key>`.
47
+
48
+ - Grab the NPM token for our org (in 1pw) and run `npm config set '//registry.npmjs.org/:_authToken'=<the token>`
49
+
50
+ - export a `GITHUB_TOKEN` with repo and write:packages permissions: https://github.com/settings/tokens/new?scopes=repo,write:packages.
51
+
52
+ - Make sure you are logged in to the prod AWS account with `aws sso login --sso-session=kernel` + `export AWS_PROFILE=kernel-prod`. This is necessary to publish releases to S3.
53
+
54
+ On main, run:
10
55
 
11
56
  ```bash
12
- bun run index.ts
57
+ make release-dry-run
13
58
  ```
14
59
 
15
- This project was created using `bun init` in bun v1.2.9. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
60
+ This will check that everything is working, but not actually release anything.
61
+ You should see one error about there not being a git tag, and that's fine.
62
+
63
+ To actually release, run:
64
+
65
+ ```bash
66
+ export VERSION=0.1.1
67
+ git tag -a cli/v$VERSION -m "Bugfixes"
68
+ git push origin cli/v$VERSION
69
+ make release
70
+ ```
71
+
72
+ ### Environment variables
73
+
74
+ The CLI requires a Kernel API key to interact with the platform:
75
+
76
+ ```bash
77
+ export KERNEL_API_KEY=your_api_key
78
+ ```
79
+
80
+ ### Releasing a new version
81
+
82
+ 1. Update the changelog:
83
+
84
+ ```bash
85
+ make changelog
86
+ ```
87
+
88
+ 2. Tag the release (e.g. `v1.0.0`) and push the tag:
89
+
90
+ ```bash
91
+ git tag cli/v0.1.0
92
+ git push origin cli/v0.1.0
93
+ ```
94
+
95
+ 3. Run `make release` – **goreleaser** will:
96
+
97
+ - Build binaries for macOS (arm64/amd64), Linux and Windows
98
+ - Create a GitHub release upload archives
99
+ - Publish/commit the Homebrew formula to the `onkernel/homebrew-tap` repository
100
+
101
+ ## Directory structure
102
+
103
+ ```
104
+ packages/cli
105
+ ├── cmd/ # cobra commands (root, deploy, invoke, …)
106
+ │ └── kernel/
107
+ │ └── main.go
108
+ ├── pkg/ # reusable helpers (zip util, etc.)
109
+ ├── .goreleaser.yaml
110
+ ├── Makefile
111
+ └── README.md
112
+ ```
package/install.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This file was generated by GoReleaser. DO NOT EDIT.
4
+
5
+ const { install } = require("./lib.js");
6
+ install();
package/lib.js ADDED
@@ -0,0 +1,175 @@
1
+ // This file was generated by GoReleaser. DO NOT EDIT.
2
+ const { archives } = require("./package.json");
3
+ const fs = require("fs");
4
+ const crypto = require("crypto");
5
+ const path = require("path");
6
+ const JSZip = require("jszip");
7
+ const tar = require("tar");
8
+ const axios = require("axios");
9
+ const { spawnSync } = require("child_process");
10
+
11
+ const getArchive = () => {
12
+ let target = `${process.platform}-${process.arch}`;
13
+ const archive = archives[target];
14
+ if (!archive) {
15
+ throw new Error(`No archive available for ${target}`);
16
+ }
17
+ return archive;
18
+ };
19
+
20
+ const binDir = path.join(__dirname, "bin");
21
+
22
+ async function extractTar(tarPath, binaries, dir) {
23
+ try {
24
+ await tar.x({
25
+ file: tarPath,
26
+ cwd: dir,
27
+ filter: (path) => binaries.includes(path),
28
+ });
29
+ console.log(`Successfully extracted binaries to "${dir}"`);
30
+ } catch (err) {
31
+ throw new Error(`Extraction failed: ${err.message}`);
32
+ }
33
+ }
34
+
35
+ async function extractZip(zipPath, binaries, dir) {
36
+ try {
37
+ const zipData = fs.readFileSync(zipPath);
38
+ const zip = await JSZip.loadAsync(zipData);
39
+ for (const binary of binaries) {
40
+ if (!zip.files[binary]) {
41
+ throw new Error(
42
+ `Error: ${binary} does not exist in ${zipPath}`,
43
+ );
44
+ }
45
+
46
+ const content = await zip.files[binary].async("nodebuffer");
47
+ if (!fs.existsSync(dir)) {
48
+ fs.mkdirSync(dir, { recursive: true });
49
+ }
50
+ const file = path.join(dir, binary);
51
+ fs.writeFileSync(file, content);
52
+ fs.chmodSync(file, "755");
53
+ console.log(`Successfully extracted "${binary}" to "${dir}"`);
54
+ }
55
+ } catch (err) {
56
+ throw new Error(`Extraction failed: ${err.message}`);
57
+ }
58
+ }
59
+
60
+ const run = async (bin) => {
61
+ await install();
62
+ if (process.platform === "win32") {
63
+ bin += ".exe";
64
+ }
65
+ const [, , ...args] = process.argv;
66
+ let result = spawnSync(path.join(binDir, bin), args, {
67
+ cwd: process.cwd(),
68
+ stdio: "inherit",
69
+ });
70
+ if (result.error) {
71
+ console.error(result.error);
72
+ }
73
+ return result.status;
74
+ };
75
+
76
+ const install = async () => {
77
+ try {
78
+ let archive = getArchive();
79
+ if (await exists(archive)) {
80
+ return;
81
+ }
82
+ let tmp = fs.mkdtempSync("archive-");
83
+ let archivePath = path.join(tmp, archive.name);
84
+ await download(archive.url, archivePath);
85
+ verify(archivePath, archive.checksum);
86
+
87
+ if (!fs.existsSync(binDir)) {
88
+ fs.mkdirSync(binDir);
89
+ }
90
+ switch (archive.format) {
91
+ case "binary":
92
+ const bin = path.join(binDir, archive.bins[0]);
93
+ fs.copyFileSync(archivePath, bin);
94
+ fs.chmodSync(bin, 0o755);
95
+ return;
96
+ case "zip":
97
+ return extractZip(archivePath, archive.bins, binDir);
98
+ case "tar":
99
+ case "tar.gz":
100
+ case "tgz":
101
+ return extractTar(archivePath, archive.bins, binDir);
102
+ case "tar.zst":
103
+ case "tzst":
104
+ case "tar.xz":
105
+ case "txz":
106
+ default:
107
+ throw new Error(`unsupported format: ${archive.format}`);
108
+ }
109
+ } catch (err) {
110
+ throw new Error(`Installation failed: ${err.message}`);
111
+ }
112
+ };
113
+
114
+ const verify = (filename, checksum) => {
115
+ if (checksum.algorithm == "" || checksum.digest == "") {
116
+ console.warn("Warning: No checksum provided for verification");
117
+ return;
118
+ }
119
+ let digest = crypto
120
+ .createHash(checksum.algorithm)
121
+ .update(fs.readFileSync(filename))
122
+ .digest("hex");
123
+ if (digest != checksum.digest) {
124
+ throw new Error(
125
+ `${filename}: ${checksum.algorithm} does not match, expected ${checksum.digest}, got ${digest}`,
126
+ );
127
+ }
128
+ };
129
+
130
+ const download = async (url, filename) => {
131
+ try {
132
+ console.log(`Downloading ${url} to ${filename}...`);
133
+ const dir = path.dirname(filename);
134
+ if (!fs.existsSync(dir)) {
135
+ fs.mkdirSync(dir, { recursive: true });
136
+ }
137
+
138
+ const response = await axios({
139
+ method: "GET",
140
+ url: url,
141
+ responseType: "stream",
142
+ timeout: 300000, // 5min
143
+ });
144
+
145
+ const writer = fs.createWriteStream(filename);
146
+ response.data.pipe(writer);
147
+
148
+ return new Promise((resolve, reject) => {
149
+ writer.on("finish", () => {
150
+ console.log(`Download complete: ${filename}`);
151
+ resolve(dir);
152
+ });
153
+
154
+ writer.on("error", (err) => {
155
+ console.error(`Error writing file: ${err.message}`);
156
+ reject(err);
157
+ });
158
+ });
159
+ } catch (err) {
160
+ throw new Error(`Download failed: ${err.message}`);
161
+ }
162
+ };
163
+
164
+ function exists(archive) {
165
+ if (!fs.existsSync(binDir)) {
166
+ return false;
167
+ }
168
+ return archive.bins.every((bin) => fs.existsSync(path.join(binDir, bin)));
169
+ }
170
+
171
+ module.exports = {
172
+ install,
173
+ run,
174
+ getArchive,
175
+ };
package/package.json CHANGED
@@ -1,54 +1,108 @@
1
1
  {
2
2
  "name": "@onkernel/cli",
3
- "version": "0.0.2",
4
- "description": "Kernel CLI",
5
- "type": "module",
6
- "module": "index.ts",
7
- "main": "dist/index.js",
8
- "types": "dist/index.d.ts",
9
- "bin": {
10
- "kernel": "./dist/index.js"
11
- },
12
- "files": [
13
- "dist"
14
- ],
3
+ "version": "0.1.3",
4
+ "description": "CLI for Kernel",
15
5
  "scripts": {
16
- "build": "tsup --config tsup.config.js"
6
+ "postinstall": "node install.js",
7
+ "run": "node run-kernel.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://onkernel-public.s3.us-east-1.amazonaws.com/"
17
12
  },
18
13
  "keywords": [
19
14
  "kernel",
20
15
  "cli",
21
- "ai",
22
- "llm",
23
- "llms",
24
- "agent",
25
- "agents",
26
- "dev",
27
- "development",
28
- "deploy",
29
- "deployment",
30
- "build",
31
- "workflow",
32
- "typescript",
33
- "command-line",
34
- "devtools"
16
+ "browsers",
17
+ "automation",
18
+ "ai"
35
19
  ],
36
- "author": "",
20
+ "author": "Rafael Garcia \u003craf@onkernel.com\u003e",
21
+ "license": "MIT",
22
+ "bugs": {
23
+ "url": "https://github.com/onkernel/kernel/issues"
24
+ },
25
+ "homepage": "https://docs.onkernel.com",
26
+ "bin": {
27
+ "kernel": "run-kernel.js"
28
+ },
37
29
  "dependencies": {
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",
43
- "commander": "^13.1.0",
44
- "fs-extra": "^11.3.0",
45
- "ignore-walk": "^7.0.0",
46
- "tmp": "^0.2.3",
47
- "type-fest": "^4.40.1"
30
+ "axios": "^1.8.2",
31
+ "jszip": "^3.10.1",
32
+ "tar": "^7.4.3"
48
33
  },
49
- "devDependencies": {
50
- "@types/fs-extra": "^11.0.4",
51
- "@types/tmp": "^0.2.6",
52
- "tsup": "^8.4.0"
34
+ "archives": {
35
+ "darwin-arm64": {
36
+ "name": "kernel_0.1.3_darwin_arm64.tar.gz",
37
+ "url": "https://github.com/onkernel/kernel/releases/download/cli%2Fv0.1.3/kernel_0.1.3_darwin_arm64.tar.gz",
38
+ "bins": [
39
+ "kernel"
40
+ ],
41
+ "format": "tar.gz",
42
+ "checksum": {
43
+ "algorithm": "sha256",
44
+ "digest": "d703b23c7e536c43dbc18b32eb0f87d61b0586bf4bbabb509cee0c7899bfc29e"
45
+ }
46
+ },
47
+ "darwin-x64": {
48
+ "name": "kernel_0.1.3_darwin_amd64.tar.gz",
49
+ "url": "https://github.com/onkernel/kernel/releases/download/cli%2Fv0.1.3/kernel_0.1.3_darwin_amd64.tar.gz",
50
+ "bins": [
51
+ "kernel"
52
+ ],
53
+ "format": "tar.gz",
54
+ "checksum": {
55
+ "algorithm": "sha256",
56
+ "digest": "0af126117f591cedbfcd60dae8ff4a12dd2c3dcbb12e54594059d448d351a906"
57
+ }
58
+ },
59
+ "linux-arm64": {
60
+ "name": "kernel_0.1.3_linux_arm64.tar.gz",
61
+ "url": "https://github.com/onkernel/kernel/releases/download/cli%2Fv0.1.3/kernel_0.1.3_linux_arm64.tar.gz",
62
+ "bins": [
63
+ "kernel"
64
+ ],
65
+ "format": "tar.gz",
66
+ "checksum": {
67
+ "algorithm": "sha256",
68
+ "digest": "8b50e00fd9f98ef3333081c916f392d3646b1ab214858daf6d0faa08e1223c03"
69
+ }
70
+ },
71
+ "linux-x64": {
72
+ "name": "kernel_0.1.3_linux_amd64.tar.gz",
73
+ "url": "https://github.com/onkernel/kernel/releases/download/cli%2Fv0.1.3/kernel_0.1.3_linux_amd64.tar.gz",
74
+ "bins": [
75
+ "kernel"
76
+ ],
77
+ "format": "tar.gz",
78
+ "checksum": {
79
+ "algorithm": "sha256",
80
+ "digest": "a23d3c552729de53d7321ecccc73d8108c1c7af850a917f4976ba81a59368b06"
81
+ }
82
+ },
83
+ "win32-arm64": {
84
+ "name": "kernel_0.1.3_windows_arm64.tar.gz",
85
+ "url": "https://github.com/onkernel/kernel/releases/download/cli%2Fv0.1.3/kernel_0.1.3_windows_arm64.tar.gz",
86
+ "bins": [
87
+ "kernel.exe"
88
+ ],
89
+ "format": "tar.gz",
90
+ "checksum": {
91
+ "algorithm": "sha256",
92
+ "digest": "1ab255f78eae35d0e2a368ca04c99695933e7eacd19208ab53f4f1dbfa3323dd"
93
+ }
94
+ },
95
+ "win32-x64": {
96
+ "name": "kernel_0.1.3_windows_amd64.tar.gz",
97
+ "url": "https://github.com/onkernel/kernel/releases/download/cli%2Fv0.1.3/kernel_0.1.3_windows_amd64.tar.gz",
98
+ "bins": [
99
+ "kernel.exe"
100
+ ],
101
+ "format": "tar.gz",
102
+ "checksum": {
103
+ "algorithm": "sha256",
104
+ "digest": "298beb9c14eacb53567debb23ea52e54f2098e1dd676f58c127e77a66869d78c"
105
+ }
106
+ }
53
107
  }
54
- }
108
+ }
package/run-kernel.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This file was generated by GoReleaser. DO NOT EDIT.
4
+
5
+ const { run } = require("./lib.js");
6
+ run("kernel");
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env bun
package/dist/index.js DELETED
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { Kernel } from '@onkernel/sdk';
3
- import chalk from 'chalk';
4
- import { Command } from 'commander';
5
- import fs, { createReadStream } from 'fs';
6
- import path2 from 'path';
7
- import * as tmp from 'tmp';
8
- import archiver from 'archiver';
9
- import fsExtra from 'fs-extra';
10
- import walk from 'ignore-walk';
11
-
12
- function getPackageVersion() {
13
- const pkgJsonPath = path2.join(__dirname, "..", "..", "..", "package.json");
14
- const content = fsExtra.readJSONSync(pkgJsonPath);
15
- if (!content.version) {
16
- throw new Error("package.json does not contain a version");
17
- }
18
- return content.version;
19
- }
20
- async function zipDirectory(inputDir, outputZip) {
21
- const entries = await walk({
22
- path: inputDir,
23
- ignoreFiles: [".gitignore", ".dockerignore"],
24
- includeEmpty: true,
25
- follow: false
26
- });
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);
32
- });
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 + "/" });
42
- }
43
- }
44
- await archive.finalize();
45
- await finalizePromise;
46
- }
47
-
48
- // index.ts
49
- var program = new Command();
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
- }
65
- }
66
- const resolvedEntrypoint = path2.resolve(entrypoint);
67
- if (!fs.existsSync(resolvedEntrypoint)) {
68
- console.error(`Error: Entrypoint ${resolvedEntrypoint} doesn't exist`);
69
- process.exit(1);
70
- }
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
- console.log(chalk.green(`Compressing files...`));
79
- const tmpZipFile = tmp.fileSync({ postfix: ".zip" });
80
- try {
81
- await zipDirectory(path2.join(sourceDir), tmpZipFile.name);
82
- console.log(chalk.green(`Deploying app (version: ${versionArg})...`));
83
- const response = await client.apps.deploy(
84
- {
85
- file: createReadStream(tmpZipFile.name),
86
- version: versionArg,
87
- force,
88
- entrypointRelPath: path2.relative(sourceDir, resolvedEntrypoint)
89
- },
90
- { maxRetries: 0 }
91
- );
92
- if (!response.success) {
93
- console.error("Error deploying to Kernel:", response.message);
94
- process.exit(1);
95
- }
96
- for (const app of response.apps) {
97
- console.log(
98
- chalk.green(
99
- `App "${app.name}" successfully deployed to Kernel with action${app.actions.length > 1 ? "s" : ""}: ${app.actions.map((a) => a.name).join(", ")}`
100
- )
101
- );
102
- console.log(
103
- `You can invoke it with: kernel invoke${versionArg !== "latest" ? ` --version ${versionArg}` : ""} ${quoteIfNeeded(app.name)} ${quoteIfNeeded(app.actions[0].name)} '{ ... JSON payload ... }'`
104
- );
105
- }
106
- } catch (error) {
107
- console.error("Error deploying to Kernel:", error);
108
- process.exit(1);
109
- } finally {
110
- tmpZipFile.removeCallback();
111
- }
112
- });
113
- function quoteIfNeeded(str) {
114
- if (str.includes(" ")) {
115
- return `"${str}"`;
116
- }
117
- return str;
118
- }
119
- 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) => {
120
- let parsedPayload;
121
- try {
122
- parsedPayload = JSON.parse(payload);
123
- } catch (error) {
124
- console.error("Error: Invalid JSON payload");
125
- process.exit(1);
126
- }
127
- if (!process.env["KERNEL_API_KEY"]) {
128
- console.error("Error: KERNEL_API_KEY environment variable is not set");
129
- console.error("Please set your Kernel API key using: export KERNEL_API_KEY=your_api_key");
130
- process.exit(1);
131
- }
132
- const client = new Kernel();
133
- console.log(`Invoking "${appName}" with action "${actionName}" and payload:`);
134
- console.log(JSON.stringify(parsedPayload, null, 2));
135
- try {
136
- const response = await client.apps.invoke({
137
- appName,
138
- actionName,
139
- payload,
140
- ...options.version && { version: options.version }
141
- });
142
- console.log("Result:");
143
- console.log(JSON.stringify(JSON.parse(response.output || "{}"), null, 2));
144
- } catch (error) {
145
- console.error("Error invoking application:", error);
146
- process.exit(1);
147
- }
148
- return;
149
- });
150
- program.parse();