Skip to content

Commit f561f32

Browse files
committed
add updated cli
1 parent 9b85408 commit f561f32

File tree

7 files changed

+43
-103
lines changed

7 files changed

+43
-103
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@ dist
77
dist-deno
88
/*.tgz
99
.idea/
10-

package.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,37 @@
2828
"@arethetypeswrong/cli": "^0.17.0",
2929
"@swc/core": "^1.3.102",
3030
"@swc/jest": "^0.2.29",
31+
"@types/archiver": "^6.0.3",
32+
"@types/bun": "^1.2.12",
33+
"@types/fs-extra": "^11.0.4",
34+
"@types/ignore-walk": "^4.0.3",
3135
"@types/jest": "^29.4.0",
3236
"@types/node": "^20.17.6",
33-
"typescript-eslint": "8.31.1",
37+
"@types/tmp": "^0.2.6",
3438
"@typescript-eslint/eslint-plugin": "8.31.1",
3539
"@typescript-eslint/parser": "8.31.1",
40+
"archiver": "^7.0.1",
41+
"chalk": "^5.4.1",
3642
"eslint": "^9.20.1",
3743
"eslint-plugin-prettier": "^5.2.3",
3844
"eslint-plugin-unused-imports": "^4.1.4",
45+
"execa": "^9.5.3",
46+
"fs-extra": "^11.3.0",
47+
"get-port": "^7.1.0",
3948
"iconv-lite": "^0.6.3",
49+
"ignore-walk": "^7.0.0",
4050
"jest": "^29.4.0",
4151
"prettier": "^3.0.0",
4252
"publint": "^0.2.12",
53+
"smol-toml": "^1.3.4",
54+
"tar": "^7.4.3",
55+
"tmp": "^0.2.3",
4356
"ts-jest": "^29.1.0",
4457
"ts-node": "^10.5.0",
4558
"tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz",
4659
"tsconfig-paths": "^4.0.0",
47-
"typescript": "5.8.3"
60+
"typescript": "5.8.3",
61+
"typescript-eslint": "8.31.1"
4862
},
4963
"resolutions": {
5064
"synckit": "0.8.8"
@@ -72,4 +86,4 @@
7286
"engines": {
7387
"node": ">= 20"
7488
}
75-
}
89+
}

src/cli/index.ts

Lines changed: 17 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { KernelJson } from '@onkernel/sdk';
21
import { Command } from 'commander';
32
import fs from 'fs';
43
import getPort from 'get-port';
54
import os from 'os';
65
import path from 'path';
6+
import type { KernelJson } from '../core/app-framework';
77
import { packageApp } from './lib/package';
88
import { getPackageVersion, isPnpmInstalled, isUvInstalled } from './lib/util';
99

@@ -13,29 +13,23 @@ const program = new Command();
1313
// This is useful for local dev.
1414
// KERNEL_NODE_SDK_OVERRIDE=/Users/rafaelgarcia/code/onkernel/kernel/packages/sdk-node
1515
// KERNEL_NODE_SDK_OVERRIDE_VERSION=0.0.1alpha.1
16-
const KERNEL_NODE_SDK_OVERRIDE = process.env.KERNEL_NODE_SDK_OVERRIDE || undefined;
16+
const KERNEL_NODE_SDK_OVERRIDE = process.env['KERNEL_NODE_SDK_OVERRIDE'] || undefined;
1717
// Same for python...
1818
// KERNEL_PYTHON_SDK_OVERRIDE=/Users/rafaelgarcia/code/onkernel/kernel/packages/sdk-python
1919
// KERNEL_PYTHON_SDK_OVERRIDE_VERSION=0.0.1alpha.1
20-
const KERNEL_PYTHON_SDK_OVERRIDE = process.env.KERNEL_PYTHON_SDK_OVERRIDE || undefined;
20+
const KERNEL_PYTHON_SDK_OVERRIDE = process.env['KERNEL_PYTHON_SDK_OVERRIDE'] || undefined;
2121

2222
// Point to a local version of the boot loader or a specific version
23-
const KERNEL_NODE_BOOT_LOADER_OVERRIDE = process.env.KERNEL_NODE_BOOT_LOADER_OVERRIDE;
24-
const KERNEL_PYTHON_BOOT_LOADER_OVERRIDE = process.env.KERNEL_PYTHON_BOOT_LOADER_OVERRIDE;
23+
const KERNEL_NODE_BOOT_LOADER_OVERRIDE = process.env['KERNEL_NODE_BOOT_LOADER_OVERRIDE'] || undefined;
24+
const KERNEL_PYTHON_BOOT_LOADER_OVERRIDE = process.env['KERNEL_PYTHON_BOOT_LOADER_OVERRIDE'] || undefined;
2525

26-
program
27-
.name('kernel')
28-
.description('CLI for Kernel deployment and invocation')
29-
.version(getPackageVersion());
26+
program.name('kernel').description('CLI for Kernel deployment and invocation').version(getPackageVersion());
3027

3128
program
3229
.command('deploy')
3330
.description('Deploy a Kernel application')
3431
.argument('<entrypoint>', 'Path to entrypoint file (TypeScript or Python)')
35-
.option(
36-
'--local',
37-
'Does not publish the app to Kernel, but installs it on disk for invoking locally',
38-
)
32+
.option('--local', 'Does not publish the app to Kernel, but installs it on disk for invoking locally')
3933
.action(async (entrypoint, options) => {
4034
const resolvedEntrypoint = path.resolve(entrypoint);
4135
if (!fs.existsSync(resolvedEntrypoint)) {
@@ -48,12 +42,12 @@ program
4842
sourceDir: path.dirname(resolvedEntrypoint), // TODO: handle nested entrypoint, i.e. ./src/entrypoint.ts
4943
entrypoint: resolvedEntrypoint,
5044
sdkOverrides: {
51-
node: KERNEL_NODE_SDK_OVERRIDE,
52-
python: KERNEL_PYTHON_SDK_OVERRIDE,
45+
...(KERNEL_NODE_SDK_OVERRIDE && { node: KERNEL_NODE_SDK_OVERRIDE }),
46+
...(KERNEL_PYTHON_SDK_OVERRIDE && { python: KERNEL_PYTHON_SDK_OVERRIDE }),
5347
},
5448
bootLoaderOverrides: {
55-
node: KERNEL_NODE_BOOT_LOADER_OVERRIDE,
56-
python: KERNEL_PYTHON_BOOT_LOADER_OVERRIDE,
49+
...(KERNEL_NODE_BOOT_LOADER_OVERRIDE && { node: KERNEL_NODE_BOOT_LOADER_OVERRIDE }),
50+
...(KERNEL_PYTHON_BOOT_LOADER_OVERRIDE && { python: KERNEL_PYTHON_BOOT_LOADER_OVERRIDE }),
5751
},
5852
});
5953

@@ -109,25 +103,15 @@ program
109103
console.log(JSON.stringify(parsedPayload, null, 2));
110104

111105
// Get the app directory
112-
const cacheFile = path.join(
113-
os.homedir(),
114-
'.local',
115-
'state',
116-
'kernel',
117-
'deploy',
118-
'local',
119-
appName,
120-
);
106+
const cacheFile = path.join(os.homedir(), '.local', 'state', 'kernel', 'deploy', 'local', appName);
121107
if (!fs.existsSync(cacheFile)) {
122108
console.error(`Error: App "${appName}" local deployment not found. `);
123109
console.error('Did you `kernel deploy --local <entrypoint>`?');
124110
process.exit(1);
125111
}
126112
const kernelLocalDir = fs.readFileSync(cacheFile, 'utf8').trim();
127113
if (!fs.existsSync(kernelLocalDir)) {
128-
console.error(
129-
`Error: App "${appName}" local deployment has been corrupted, please re-deploy.`,
130-
);
114+
console.error(`Error: App "${appName}" local deployment has been corrupted, please re-deploy.`);
131115
process.exit(1);
132116
}
133117

@@ -176,10 +160,7 @@ async function waitForStartupMessage(
176160
const text = decoder.decode(value);
177161
process.stderr.write(text);
178162

179-
if (
180-
text.includes('Application startup complete.') ||
181-
text.includes('Kernel application running')
182-
) {
163+
if (text.includes('Application startup complete.') || text.includes('Kernel application running')) {
183164
clearTimeout(timeout);
184165
resolve();
185166
break;
@@ -201,12 +182,7 @@ type InvokeLocalOptions = {
201182
/**
202183
* Invokes a locally deployed Python app action
203184
*/
204-
async function invokeLocalPython({
205-
kernelLocalDir,
206-
appName,
207-
actionName,
208-
parsedPayload,
209-
}: InvokeLocalOptions) {
185+
async function invokeLocalPython({ kernelLocalDir, appName, actionName, parsedPayload }: InvokeLocalOptions) {
210186
const uvInstalled = await isUvInstalled();
211187
if (!uvInstalled) {
212188
console.error('Error: uv is not installed. Please install it with:');
@@ -254,12 +230,7 @@ async function invokeLocalPython({
254230
/**
255231
* Invokes a locally deployed TypeScript app action
256232
*/
257-
async function invokeLocalNode({
258-
kernelLocalDir,
259-
appName,
260-
actionName,
261-
parsedPayload,
262-
}: InvokeLocalOptions) {
233+
async function invokeLocalNode({ kernelLocalDir, appName, actionName, parsedPayload }: InvokeLocalOptions) {
263234
const pnpmInstalled = await isPnpmInstalled();
264235
if (!pnpmInstalled) {
265236
console.error('Error: pnpm is not installed. Please install it with:');
@@ -279,15 +250,7 @@ async function invokeLocalNode({
279250
// Find an available port and start the boot loader
280251
const port = await getPort();
281252
const tsProcess = Bun.spawn(
282-
[
283-
'pnpm',
284-
'exec',
285-
'tsx',
286-
'index.ts',
287-
'--port',
288-
port.toString(),
289-
path.join(kernelLocalDir, 'app'),
290-
],
253+
['pnpm', 'exec', 'tsx', 'index.ts', '--port', port.toString(), path.join(kernelLocalDir, 'app')],
291254
{
292255
cwd: kernelLocalDir,
293256
stdio: ['inherit', 'inherit', 'pipe'],

src/cli/lib/package.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { KernelJson } from '@onkernel/sdk';
21
import fs from 'fs';
32
import fsExtra from 'fs-extra';
43
import os from 'os';
54
import path from 'path';
65
import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
6+
import type { KernelJson } from '../../core/app-framework';
77
import { NODE_PACKAGE_NAME, PYTHON_PACKAGE_NAME } from './constants';
88
import { runInDirectory } from './util';
99

@@ -74,10 +74,7 @@ export async function packageApp(config: PackageConfig): Promise<string> {
7474
// 1. Update kernel SDK dependency if override is provided
7575
if (sdkOverrides.python) {
7676
if (fs.existsSync(path.join(kernelAppDir, 'pyproject.toml'))) {
77-
overwriteKernelDependencyInPyproject(
78-
path.join(kernelAppDir, 'pyproject.toml'),
79-
sdkOverrides.python,
80-
);
77+
overwriteKernelDependencyInPyproject(path.join(kernelAppDir, 'pyproject.toml'), sdkOverrides.python);
8178
}
8279
if (fs.existsSync(path.join(kernelAppDir, 'requirements.txt'))) {
8380
overwriteKernelDependencyInRequirementsTxt(
@@ -196,32 +193,18 @@ export async function packageApp(config: PackageConfig): Promise<string> {
196193
/**
197194
* Copy all files from source directory to target directory
198195
*/
199-
export function copyDirectoryContents(
200-
sourceDir: string,
201-
targetDir: string,
202-
excludeDirs: string[] = [],
203-
) {
196+
export function copyDirectoryContents(sourceDir: string, targetDir: string, excludeDirs: string[] = []) {
204197
fsExtra.copySync(sourceDir, targetDir, {
205198
filter: (src: string) => {
206199
const basename = path.basename(src);
207-
const standardExcludes = [
208-
'.build',
209-
'node_modules',
210-
'.git',
211-
'.mypy_cache',
212-
'.venv',
213-
'__pycache__',
214-
];
200+
const standardExcludes = ['.build', 'node_modules', '.git', '.mypy_cache', '.venv', '__pycache__'];
215201
return ![...standardExcludes, ...excludeDirs].includes(basename);
216202
},
217203
overwrite: true,
218204
});
219205
}
220206

221-
function overwriteKernelDependencyInPyproject(
222-
pyprojectPath: string,
223-
kernelDependencyOverride: string,
224-
) {
207+
function overwriteKernelDependencyInPyproject(pyprojectPath: string, kernelDependencyOverride: string) {
225208
const pyproject = parseToml(fs.readFileSync(pyprojectPath, 'utf8')) as any;
226209
if (!pyproject.project) {
227210
pyproject.project = { dependencies: [] };

src/cli/lib/util.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ import { fileURLToPath } from 'url';
1111
* @param options Optional options including working directory
1212
* @returns Promise resolving to just the exit code
1313
*/
14-
export async function runForExitCode(
15-
command: string,
16-
options: { cwd?: string } = {},
17-
): Promise<number> {
14+
export async function runForExitCode(command: string, options: { cwd?: string } = {}): Promise<number> {
1815
try {
1916
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
2017

src/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import { Browser, BrowserCreateSessionResponse } from './resources/browser';
3232
import { readEnv } from './internal/utils/env';
3333
import { formatRequestDetails, loggerFor } from './internal/utils/log';
3434
import { isEmptyObj } from './internal/utils/values';
35-
import { KernelApp, appRegistry, browsers as appBrowsers } from './core/app-framework';
35+
import { KernelApp, appRegistry, KernelAction, KernelContext, KernelJson } from './core/app-framework';
36+
export type { KernelAction, KernelContext, KernelJson };
3637

3738
export interface ClientOptions {
3839
/**
@@ -710,7 +711,6 @@ export class Kernel {
710711

711712
apps: API.Apps = new API.Apps(this);
712713
browser: API.Browser = new API.Browser(this);
713-
appFrameworkBrowsers = appBrowsers;
714714
}
715715
Kernel.Apps = Apps;
716716
Kernel.Browser = Browser;

src/core/app-framework.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@ export class KernelApp {
4343
* Define an action
4444
*/
4545
action<T, R>(
46-
nameOrHandler:
47-
| string
48-
| ((input: T) => Promise<R>)
49-
| ((context: KernelContext, input: T) => Promise<R>),
46+
nameOrHandler: string | ((input: T) => Promise<R>) | ((context: KernelContext, input: T) => Promise<R>),
5047
handler?: ((input: T) => Promise<R>) | ((context: KernelContext, input: T) => Promise<R>),
5148
) {
5249
let actionName: string;
@@ -145,16 +142,3 @@ class KernelAppRegistry {
145142

146143
// Create a singleton registry for apps
147144
export const appRegistry = new KernelAppRegistry();
148-
149-
// Browser management module
150-
export const browsers = {
151-
/**
152-
* Create a new browser instance
153-
*/
154-
create: async ({ invocationId }: { invocationId: string }): Promise<Browser> => {
155-
return {
156-
cdp_ws_url:
157-
'wss://floral-morning-3s0r8t7x.iad-prod-apiukp-0.onkernel.app:9222/devtools/browser/4dc1cbf6-be0e-4612-bba3-8e399d9638c7',
158-
};
159-
},
160-
};

0 commit comments

Comments
 (0)