@janux/cli 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/README.md +3 -0
- package/bin.ts +7 -0
- package/package.json +54 -0
- package/src/args.test.ts +24 -0
- package/src/args.ts +25 -0
- package/src/commands.ts +84 -0
- package/src/index.ts +13 -0
package/README.md
ADDED
package/bin.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@janux/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Janux CLI: dev (Vite), build (client bundle) and start (production server on Bun).",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/aralroca/Janux.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Aral Roca Gomez",
|
|
12
|
+
"email": "contact@aralroca.com"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"janux": "./bin.ts"
|
|
17
|
+
},
|
|
18
|
+
"main": "./src/index.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./src/index.ts"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"janux": "0.1.0",
|
|
24
|
+
"@janux/server": "0.1.0",
|
|
25
|
+
"@janux/agent": "0.1.0",
|
|
26
|
+
"@janux/vite": "0.1.0",
|
|
27
|
+
"vite": "^7.3.0"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/aralroca/Janux#readme",
|
|
30
|
+
"bugs": "https://github.com/aralroca/Janux/issues",
|
|
31
|
+
"keywords": [
|
|
32
|
+
"janux",
|
|
33
|
+
"agentic-ui",
|
|
34
|
+
"mcp",
|
|
35
|
+
"webmcp",
|
|
36
|
+
"ai-agents",
|
|
37
|
+
"fullstack",
|
|
38
|
+
"framework",
|
|
39
|
+
"bun",
|
|
40
|
+
"vite",
|
|
41
|
+
"resumability",
|
|
42
|
+
"copilot"
|
|
43
|
+
],
|
|
44
|
+
"files": [
|
|
45
|
+
"src",
|
|
46
|
+
"bin.ts"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"bun": ">=1.3.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/args.test.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { HELP_TEXT, parseArgs } from './args';
|
|
3
|
+
|
|
4
|
+
describe('janux CLI args', () => {
|
|
5
|
+
it('parses commands and the port flag', () => {
|
|
6
|
+
expect(parseArgs(['dev', '--port', '4000'], '/app')).toEqual({
|
|
7
|
+
command: 'dev',
|
|
8
|
+
port: 4000,
|
|
9
|
+
root: '/app',
|
|
10
|
+
});
|
|
11
|
+
expect(parseArgs(['build'], '/app').command).toBe('build');
|
|
12
|
+
expect(parseArgs(['start'], '/app').command).toBe('start');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('falls back to help on unknown commands', () => {
|
|
16
|
+
expect(parseArgs(['nope'], '/app').command).toBe('help');
|
|
17
|
+
expect(parseArgs([], '/app').command).toBe('help');
|
|
18
|
+
expect(HELP_TEXT).toContain('janux dev');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('rejects non-numeric ports', () => {
|
|
22
|
+
expect(() => parseArgs(['dev', '--port', 'abc'], '/app')).toThrow(/--port/);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/src/args.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface CliCommand {
|
|
2
|
+
command: 'dev' | 'build' | 'start' | 'help';
|
|
3
|
+
port: number;
|
|
4
|
+
root: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const COMMANDS = new Set(['dev', 'build', 'start', 'help']);
|
|
8
|
+
|
|
9
|
+
export const HELP_TEXT = `janux — the agent-native fullstack UI framework
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
janux dev [--port 3000] Start the dev server (Vite + HMR)
|
|
13
|
+
janux build Bundle client assets for production
|
|
14
|
+
janux start [--port 3000] Run the production server (Bun)
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
export function parseArgs(argv: string[], cwd: string): CliCommand {
|
|
18
|
+
const command = COMMANDS.has(argv[0] ?? '') ? (argv[0] as CliCommand['command']) : 'help';
|
|
19
|
+
const portFlag = argv.indexOf('--port');
|
|
20
|
+
const port = portFlag >= 0 ? Number(argv[portFlag + 1]) : Number(process.env.PORT ?? 3000);
|
|
21
|
+
|
|
22
|
+
if (Number.isNaN(port)) throw new Error('janux: --port must be a number');
|
|
23
|
+
|
|
24
|
+
return { command, port, root: cwd };
|
|
25
|
+
}
|
package/src/commands.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { createJanuxServer, type ServerOptions } from '@janux/server';
|
|
4
|
+
import { defineAgent } from '@janux/agent';
|
|
5
|
+
import { apiFiles, apiModuleName, janux, resolveAppConfig } from '@janux/vite';
|
|
6
|
+
import type { CliCommand } from './args';
|
|
7
|
+
|
|
8
|
+
export async function dev({ root, port }: CliCommand): Promise<void> {
|
|
9
|
+
const { createServer } = await import('vite');
|
|
10
|
+
const server = await createServer({ root, plugins: [janux()], server: { port } });
|
|
11
|
+
|
|
12
|
+
await server.listen();
|
|
13
|
+
console.log(`\n janux dev ready\n → app: http://localhost:${port}/`);
|
|
14
|
+
console.log(` → manifest: http://localhost:${port}/_janux/manifest`);
|
|
15
|
+
console.log(` → agent: http://localhost:${port}/_janux/agent\n`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function build({ root }: CliCommand): Promise<void> {
|
|
19
|
+
const app = resolveAppConfig(root);
|
|
20
|
+
|
|
21
|
+
if (app.clientEntry) {
|
|
22
|
+
const { build: viteBuild } = await import('vite');
|
|
23
|
+
|
|
24
|
+
await viteBuild({
|
|
25
|
+
root,
|
|
26
|
+
plugins: [janux()],
|
|
27
|
+
build: {
|
|
28
|
+
outDir: 'dist/client',
|
|
29
|
+
rollupOptions: { input: app.clientEntry, output: { entryFileNames: 'client.js' } },
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
console.log('janux build: no src/client.ts — fully static app, nothing to bundle (0 KB JS).');
|
|
34
|
+
}
|
|
35
|
+
copyStylesheet(app.stylesheet, root);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function copyStylesheet(stylesheet: string | undefined, root: string): void {
|
|
39
|
+
if (!stylesheet) return;
|
|
40
|
+
const outDir = join(root, 'dist/client');
|
|
41
|
+
|
|
42
|
+
mkdirSync(outDir, { recursive: true });
|
|
43
|
+
cpSync(stylesheet, join(outDir, 'styles.css'));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function prodServerOptions(root: string): Promise<ServerOptions> {
|
|
47
|
+
const app = resolveAppConfig(root);
|
|
48
|
+
const apiModules = Object.fromEntries(
|
|
49
|
+
await Promise.all(
|
|
50
|
+
apiFiles(app.serverDir).map(async (file) => [apiModuleName(file), await import(file)]),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
const agentModule = app.agentModule ? await import(app.agentModule) : undefined;
|
|
54
|
+
const storesModule = app.storesModule ? await import(app.storesModule) : undefined;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
routesDir: app.routesDir,
|
|
58
|
+
apis: apiModules,
|
|
59
|
+
agent: agentModule?.default ?? defineAgent(),
|
|
60
|
+
storeDefs: storesModule ?? {},
|
|
61
|
+
runtimeUrl: existsSync(join(root, 'dist/client/client.js')) ? '/client.js' : undefined,
|
|
62
|
+
stylesheets: app.stylesheet ? ['/styles.css'] : [],
|
|
63
|
+
title: app.title,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function start({ root, port }: CliCommand): Promise<void> {
|
|
68
|
+
const options = await prodServerOptions(root);
|
|
69
|
+
const server = createJanuxServer(options);
|
|
70
|
+
const staticDir = join(root, 'dist/client');
|
|
71
|
+
|
|
72
|
+
Bun.serve({
|
|
73
|
+
port,
|
|
74
|
+
fetch: async (req) => {
|
|
75
|
+
const { pathname } = new URL(req.url);
|
|
76
|
+
const staticFile = Bun.file(join(staticDir, pathname.slice(1)));
|
|
77
|
+
|
|
78
|
+
if (pathname !== '/' && (await staticFile.exists())) return new Response(staticFile);
|
|
79
|
+
|
|
80
|
+
return server.fetch(req);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
console.log(`janux start: production server on http://localhost:${port}/ (Bun)`);
|
|
84
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HELP_TEXT, parseArgs } from './args';
|
|
2
|
+
import { build, dev, start } from './commands';
|
|
3
|
+
|
|
4
|
+
export { parseArgs, HELP_TEXT } from './args';
|
|
5
|
+
|
|
6
|
+
export async function runCli(argv: string[]): Promise<void> {
|
|
7
|
+
const parsed = parseArgs(argv, process.cwd());
|
|
8
|
+
|
|
9
|
+
if (parsed.command === 'dev') return dev(parsed);
|
|
10
|
+
if (parsed.command === 'build') return build(parsed);
|
|
11
|
+
if (parsed.command === 'start') return start(parsed);
|
|
12
|
+
console.log(HELP_TEXT);
|
|
13
|
+
}
|