@nimbus-sh/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 +127 -0
- package/dist/bin.d.ts +22 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +81 -0
- package/dist/commands/runtime-sync.d.ts +31 -0
- package/dist/commands/runtime-sync.d.ts.map +1 -0
- package/dist/commands/runtime-sync.js +140 -0
- package/dist/commands/scaffold.d.ts +27 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +290 -0
- package/dist/commands/session.d.ts +6 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +45 -0
- package/dist/commands/token.d.ts +25 -0
- package/dist/commands/token.d.ts.map +1 -0
- package/dist/commands/token.js +105 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/scaffold-bin.d.ts +10 -0
- package/dist/scaffold-bin.d.ts.map +1 -0
- package/dist/scaffold-bin.js +13 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @nimbus-sh/cli
|
|
2
|
+
|
|
3
|
+
CLI for Nimbus: scaffolding, token mint/verify, session helpers, and runtime
|
|
4
|
+
cache operations.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx @nimbus-sh/cli --help
|
|
10
|
+
npx @nimbus-sh/cli init my-app
|
|
11
|
+
npx create-nimbus-app my-app
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
From this repository:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun packages/cli/src/bin.ts --help
|
|
18
|
+
bun packages/cli/src/bin.ts init my-app
|
|
19
|
+
bun packages/cli/src/scaffold-bin.ts my-app
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Verbs
|
|
23
|
+
|
|
24
|
+
### `nimbus init [directory]`
|
|
25
|
+
|
|
26
|
+
Scaffolds a Nimbus-powered Workers project. `.` uses the current directory.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
nimbus init my-nimbus
|
|
30
|
+
cd my-nimbus
|
|
31
|
+
npm install
|
|
32
|
+
wrangler secret put JWT_SECRET
|
|
33
|
+
wrangler deploy
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `create-nimbus-app <name>`
|
|
37
|
+
|
|
38
|
+
Scaffolds a new Nimbus-powered Workers project.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
create-nimbus-app my-nimbus
|
|
42
|
+
cd my-nimbus
|
|
43
|
+
npm install
|
|
44
|
+
wrangler secret put JWT_SECRET
|
|
45
|
+
wrangler deploy
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Flags:
|
|
49
|
+
|
|
50
|
+
| Flag | Default | What |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `--name <wrangler-name>` | project name | Becomes the deployed Worker name. |
|
|
53
|
+
| `--template <name>` | `worker-only` | Only `worker-only` ships in v0.1. |
|
|
54
|
+
| `--force` | off | Overwrite existing directory. |
|
|
55
|
+
|
|
56
|
+
### `nimbus token mint`
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
JWT_SECRET=<hex> nimbus token mint --tenant acme --sub alice [--ttl 3600]
|
|
60
|
+
# Prints the JWT to stdout. Pipe with `> /tmp/jwt` or `| pbcopy`.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Flags:
|
|
64
|
+
|
|
65
|
+
| Flag | What |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `--tenant <id>` (required) | Sets `tn` claim. |
|
|
68
|
+
| `--sub <id>` | Sets `sub` claim. |
|
|
69
|
+
| `--ttl <sec>` | Token lifetime in seconds. Default 3600 (1h), max 2,592,000 (30d). |
|
|
70
|
+
| `--scopes <a,b>` | Comma-separated capability scopes. |
|
|
71
|
+
| `--sid <id>` | Pin token to a specific session ID. |
|
|
72
|
+
|
|
73
|
+
### `nimbus token verify <token>`
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
JWT_SECRET=<hex> nimbus token verify eyJhbGciOi...
|
|
77
|
+
# Prints the verified claims as JSON.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Exit codes: 0 success, 65 token-validation failure, 78 env missing.
|
|
81
|
+
|
|
82
|
+
### `nimbus runtime sync`
|
|
83
|
+
|
|
84
|
+
Uploads runtime blobs/manifests and updates the runtime catalog through the
|
|
85
|
+
public CLI wrapper. This is the user-facing path for Python, Ruby, and clang
|
|
86
|
+
runtime cache operations.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
CLOUDFLARE_ACCOUNT_ID=<id> nimbus runtime sync --bucket nimbus-runtime-cache-public clang
|
|
90
|
+
CLOUDFLARE_ACCOUNT_ID=<id> nimbus runtime sync --bucket nimbus-runtime-cache-public python
|
|
91
|
+
CLOUDFLARE_ACCOUNT_ID=<id> nimbus runtime sync --bucket nimbus-runtime-cache-public ruby
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Runtime versions default to the shipped catalog versions
|
|
95
|
+
(`clang@binji-2020`, `python@0.29.4`, `ruby@3.3.4`) unless explicitly
|
|
96
|
+
overridden as `name@version`.
|
|
97
|
+
|
|
98
|
+
### `nimbus runtime list`
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
nimbus runtime list
|
|
102
|
+
# JSON catalog: [{name, version, size_mb, license}, ...]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `nimbus session new`
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
NIMBUS_ENDPOINT=https://my-nimbus.workers.dev nimbus session new
|
|
109
|
+
# {"sessionId":"pretty-otter-1234","url":"https://.../s/pretty-otter-1234/"}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Programmatic use
|
|
113
|
+
|
|
114
|
+
Every verb is also exported as a function:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { mintToken, syncRuntimes, scaffold } from '@nimbus-sh/cli';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`mintToken(argv)` and friends return a Promise<number> (process exit
|
|
121
|
+
code). Stdout / stderr write directly via `process.stdout`/`process.stderr`.
|
|
122
|
+
|
|
123
|
+
## Engines
|
|
124
|
+
|
|
125
|
+
Requires Node >= 20 (for native `fetch`, `crypto.subtle`, etc.).
|
|
126
|
+
|
|
127
|
+
MIT.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/nimbus — Multi-verb CLI dispatcher.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* nimbus token mint --tenant acme --sub alice [--ttl 3600]
|
|
7
|
+
* nimbus token verify <token>
|
|
8
|
+
* nimbus runtime sync # upload runtime blobs/catalog through CLI wrapper
|
|
9
|
+
* nimbus runtime list # show staged runtimes
|
|
10
|
+
* nimbus session new # mint a session via /new
|
|
11
|
+
* nimbus --version
|
|
12
|
+
* nimbus --help
|
|
13
|
+
*
|
|
14
|
+
* Design notes:
|
|
15
|
+
* - Zero dependencies on commander/yargs. The verb table is ~50 LOC.
|
|
16
|
+
* This keeps the install footprint tiny (matters for `npx`).
|
|
17
|
+
* - Every verb writes JSON to stdout on success (machine-parseable),
|
|
18
|
+
* a human-readable summary line to stderr, and exits 0. Failures
|
|
19
|
+
* emit JSON `{ error, code }` + exit code from CLI_EXIT_CODES.
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=bin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/nimbus — Multi-verb CLI dispatcher.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* nimbus token mint --tenant acme --sub alice [--ttl 3600]
|
|
7
|
+
* nimbus token verify <token>
|
|
8
|
+
* nimbus runtime sync # upload runtime blobs/catalog through CLI wrapper
|
|
9
|
+
* nimbus runtime list # show staged runtimes
|
|
10
|
+
* nimbus session new # mint a session via /new
|
|
11
|
+
* nimbus --version
|
|
12
|
+
* nimbus --help
|
|
13
|
+
*
|
|
14
|
+
* Design notes:
|
|
15
|
+
* - Zero dependencies on commander/yargs. The verb table is ~50 LOC.
|
|
16
|
+
* This keeps the install footprint tiny (matters for `npx`).
|
|
17
|
+
* - Every verb writes JSON to stdout on success (machine-parseable),
|
|
18
|
+
* a human-readable summary line to stderr, and exits 0. Failures
|
|
19
|
+
* emit JSON `{ error, code }` + exit code from CLI_EXIT_CODES.
|
|
20
|
+
*/
|
|
21
|
+
import { mintToken, verifyTokenCmd } from './commands/token.js';
|
|
22
|
+
import { syncRuntimes, listRuntimes } from './commands/runtime-sync.js';
|
|
23
|
+
import { newSession } from './commands/session.js';
|
|
24
|
+
import { CLI_VERSION } from './version.js';
|
|
25
|
+
const verbs = {
|
|
26
|
+
token: {
|
|
27
|
+
mint: mintToken,
|
|
28
|
+
verify: verifyTokenCmd,
|
|
29
|
+
},
|
|
30
|
+
runtime: {
|
|
31
|
+
sync: syncRuntimes,
|
|
32
|
+
list: listRuntimes,
|
|
33
|
+
},
|
|
34
|
+
session: {
|
|
35
|
+
new: newSession,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
async function main(argv) {
|
|
39
|
+
if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
|
|
40
|
+
printHelp();
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
if (argv[0] === '--version' || argv[0] === '-v') {
|
|
44
|
+
process.stdout.write(`${CLI_VERSION}\n`);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
if (argv[0] === 'init') {
|
|
48
|
+
const { scaffold } = await import('./commands/scaffold.js');
|
|
49
|
+
return scaffold(argv.slice(1).length > 0 ? argv.slice(1) : ['.']);
|
|
50
|
+
}
|
|
51
|
+
const [namespace, verb, ...rest] = argv;
|
|
52
|
+
if (!verbs[namespace] || !verbs[namespace][verb]) {
|
|
53
|
+
process.stderr.write(`nimbus: unknown command \`${namespace} ${verb || ''}\`. Try \`nimbus --help\`.\n`);
|
|
54
|
+
return 64;
|
|
55
|
+
}
|
|
56
|
+
return verbs[namespace][verb](rest);
|
|
57
|
+
}
|
|
58
|
+
function printHelp() {
|
|
59
|
+
process.stdout.write(`nimbus ${CLI_VERSION}
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
nimbus token mint --tenant <t> [--sub <s>] [--ttl <sec>] [--scopes <a,b>]
|
|
63
|
+
nimbus token verify <token>
|
|
64
|
+
nimbus runtime sync [--bucket <name>] [runtime[@version]...]
|
|
65
|
+
nimbus runtime list
|
|
66
|
+
nimbus session new [--endpoint <url>]
|
|
67
|
+
nimbus init [directory]
|
|
68
|
+
|
|
69
|
+
For embedder scaffolding:
|
|
70
|
+
create-nimbus-app <project-name>
|
|
71
|
+
|
|
72
|
+
Env:
|
|
73
|
+
JWT_SECRET Shared secret for HS256 (required for token mint/verify).
|
|
74
|
+
NIMBUS_ENDPOINT Base URL for session new. Defaults to localhost:8787.
|
|
75
|
+
CLOUDFLARE_ACCOUNT_ID Required for runtime sync.
|
|
76
|
+
`);
|
|
77
|
+
}
|
|
78
|
+
main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
|
|
79
|
+
process.stderr.write(`nimbus: ${err?.stack || err}\n`);
|
|
80
|
+
process.exit(70);
|
|
81
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/runtime-sync — Re-runs the runtime-bundle pipeline that
|
|
3
|
+
* populates an R2 bucket with the clang / python / ruby blobs.
|
|
4
|
+
*
|
|
5
|
+
* Two modes:
|
|
6
|
+
* - Default (no --bucket): syncs the canonical Nimbus-operated bucket
|
|
7
|
+
* `nimbus-runtime-cache-public` for the catalog the project ships
|
|
8
|
+
* today. This is what we run; embedders typically don't need it.
|
|
9
|
+
* - `--bucket <name>`: BYOA mode. Runtime names may be positional
|
|
10
|
+
* (`nimbus runtime sync python clang`) or comma-separated via
|
|
11
|
+
* `--runtimes python,clang`.
|
|
12
|
+
*
|
|
13
|
+
* Implementation: shells out to wrangler r2 object put for each blob
|
|
14
|
+
* listed in `@nimbus-sh/worker`'s bundled `scripts/bundle-runtime.mjs`.
|
|
15
|
+
* The actual fetch+verify+upload work lives in that script; this CLI
|
|
16
|
+
* is a thin proxy.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Sync runtime blobs to an R2 bucket via the bundled
|
|
20
|
+
* `bundle-runtime.mjs` script.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```bash
|
|
24
|
+
* # BYOA mode — sync into your own bucket.
|
|
25
|
+
* CLOUDFLARE_ACCOUNT_ID=… nimbus runtime sync --bucket my-runtime-cache python
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function syncRuntimes(args: string[]): Promise<number>;
|
|
29
|
+
/** `nimbus runtime list` — print the catalog the SDK ships against. */
|
|
30
|
+
export declare function listRuntimes(_args: string[]): Promise<number>;
|
|
31
|
+
//# sourceMappingURL=runtime-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-sync.d.ts","sourceRoot":"","sources":["../../src/commands/runtime-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAeH;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsClE;AAED,uEAAuE;AACvE,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAUnE"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/runtime-sync — Re-runs the runtime-bundle pipeline that
|
|
3
|
+
* populates an R2 bucket with the clang / python / ruby blobs.
|
|
4
|
+
*
|
|
5
|
+
* Two modes:
|
|
6
|
+
* - Default (no --bucket): syncs the canonical Nimbus-operated bucket
|
|
7
|
+
* `nimbus-runtime-cache-public` for the catalog the project ships
|
|
8
|
+
* today. This is what we run; embedders typically don't need it.
|
|
9
|
+
* - `--bucket <name>`: BYOA mode. Runtime names may be positional
|
|
10
|
+
* (`nimbus runtime sync python clang`) or comma-separated via
|
|
11
|
+
* `--runtimes python,clang`.
|
|
12
|
+
*
|
|
13
|
+
* Implementation: shells out to wrangler r2 object put for each blob
|
|
14
|
+
* listed in `@nimbus-sh/worker`'s bundled `scripts/bundle-runtime.mjs`.
|
|
15
|
+
* The actual fetch+verify+upload work lives in that script; this CLI
|
|
16
|
+
* is a thin proxy.
|
|
17
|
+
*/
|
|
18
|
+
import { spawn } from 'node:child_process';
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
24
|
+
const DEFAULT_RUNTIME_VERSIONS = {
|
|
25
|
+
clang: 'binji-2020',
|
|
26
|
+
python: '0.29.4',
|
|
27
|
+
ruby: '3.3.4',
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Sync runtime blobs to an R2 bucket via the bundled
|
|
31
|
+
* `bundle-runtime.mjs` script.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```bash
|
|
35
|
+
* # BYOA mode — sync into your own bucket.
|
|
36
|
+
* CLOUDFLARE_ACCOUNT_ID=… nimbus runtime sync --bucket my-runtime-cache python
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export async function syncRuntimes(args) {
|
|
40
|
+
const parsed = parseFlags(args);
|
|
41
|
+
const bucket = parsed.bucket ?? 'nimbus-runtime-cache-public';
|
|
42
|
+
const runtimes = parsed.runtimes.length > 0
|
|
43
|
+
? parsed.runtimes
|
|
44
|
+
: ['clang', 'python', 'ruby'];
|
|
45
|
+
if (!process.env.CLOUDFLARE_ACCOUNT_ID) {
|
|
46
|
+
process.stderr.write('nimbus runtime sync: CLOUDFLARE_ACCOUNT_ID env var required\n');
|
|
47
|
+
return 78;
|
|
48
|
+
}
|
|
49
|
+
// Locate the bundle-runtime.mjs script in `@nimbus-sh/worker`. When
|
|
50
|
+
// installed via npm, resolve via require; in workspace dogfood, use
|
|
51
|
+
// a relative path.
|
|
52
|
+
const scriptPath = resolveBundleRuntimeScript();
|
|
53
|
+
if (!scriptPath) {
|
|
54
|
+
process.stderr.write('nimbus runtime sync: cannot locate @nimbus-sh/worker/scripts/bundle-runtime.mjs\n');
|
|
55
|
+
return 70;
|
|
56
|
+
}
|
|
57
|
+
process.stderr.write(`nimbus: syncing runtimes [${runtimes.join(', ')}] → r2://${bucket}\n`);
|
|
58
|
+
for (const rt of runtimes) {
|
|
59
|
+
const [name, explicitVersion] = rt.split('@');
|
|
60
|
+
const version = explicitVersion || DEFAULT_RUNTIME_VERSIONS[name];
|
|
61
|
+
if (!version) {
|
|
62
|
+
process.stderr.write(`nimbus runtime sync: unknown runtime "${rt}" (use name@version)\n`);
|
|
63
|
+
return 64;
|
|
64
|
+
}
|
|
65
|
+
const code = await runOne(scriptPath, [name, version, '--bucket', bucket]);
|
|
66
|
+
if (code !== 0) {
|
|
67
|
+
process.stderr.write(`nimbus runtime sync: ${rt} failed (exit ${code})\n`);
|
|
68
|
+
return code;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write(JSON.stringify({ ok: true, bucket, runtimes }) + '\n');
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
/** `nimbus runtime list` — print the catalog the SDK ships against. */
|
|
75
|
+
export async function listRuntimes(_args) {
|
|
76
|
+
// v0.1: print the static known-list. v0.2 will fetch the live
|
|
77
|
+
// catalog.json from R2.
|
|
78
|
+
const catalog = [
|
|
79
|
+
{ name: 'clang', version: 'binji-2020', size_mb: 9, license: 'Apache-2.0-LLVM' },
|
|
80
|
+
{ name: 'python', version: 'pyodide-0.29.4', size_mb: 10, license: 'MPL-2.0' },
|
|
81
|
+
{ name: 'ruby', version: 'ruby.wasm-2.9.4', size_mb: 25, license: 'BSD-2-Clause' },
|
|
82
|
+
];
|
|
83
|
+
process.stdout.write(JSON.stringify(catalog, null, 2) + '\n');
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
// ── helpers ──────────────────────────────────────────────────────────
|
|
87
|
+
function resolveBundleRuntimeScript() {
|
|
88
|
+
// 1. Try resolving via the @nimbus-sh/worker package (npm-installed).
|
|
89
|
+
try {
|
|
90
|
+
const packageJson = nodeRequire.resolve('@nimbus-sh/worker/package.json');
|
|
91
|
+
const script = join(dirname(packageJson), 'scripts', 'bundle-runtime.mjs');
|
|
92
|
+
if (existsSync(script))
|
|
93
|
+
return script;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// fallthrough
|
|
97
|
+
}
|
|
98
|
+
// 2. Workspace dogfood — relative path inside the monorepo.
|
|
99
|
+
try {
|
|
100
|
+
const wsUrl = new URL('../../../worker/scripts/bundle-runtime.mjs', import.meta.url);
|
|
101
|
+
const script = fileURLToPath(wsUrl);
|
|
102
|
+
return existsSync(script) ? script : null;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function runOne(scriptPath, args) {
|
|
109
|
+
return new Promise((resolveExit) => {
|
|
110
|
+
const child = spawn('node', [scriptPath, ...args], {
|
|
111
|
+
stdio: 'inherit',
|
|
112
|
+
env: process.env,
|
|
113
|
+
});
|
|
114
|
+
child.on('exit', (code) => resolveExit(code ?? 1));
|
|
115
|
+
child.on('error', (e) => {
|
|
116
|
+
process.stderr.write(`spawn error: ${e.message}\n`);
|
|
117
|
+
resolveExit(70);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function parseFlags(args) {
|
|
122
|
+
const out = { runtimes: [] };
|
|
123
|
+
for (let i = 0; i < args.length; i++) {
|
|
124
|
+
const a = args[i];
|
|
125
|
+
if (a === '--bucket') {
|
|
126
|
+
out.bucket = args[i + 1] ?? '';
|
|
127
|
+
i++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (a === '--runtimes') {
|
|
131
|
+
out.runtimes.push(...(args[i + 1] ?? '').split(',').map((s) => s.trim()).filter(Boolean));
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (a.startsWith('--'))
|
|
136
|
+
continue;
|
|
137
|
+
out.runtimes.push(a);
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/scaffold — `create-nimbus-app <project-name>`.
|
|
3
|
+
*
|
|
4
|
+
* v0.1 ships a single template: `worker-only`. The scaffolder writes:
|
|
5
|
+
*
|
|
6
|
+
* <project>/
|
|
7
|
+
* ├── package.json — deps: @nimbus-sh/worker, @nimbus-sh/sdk
|
|
8
|
+
* ├── wrangler.jsonc — the canonical 28-LOC embedder snippet
|
|
9
|
+
* ├── src/
|
|
10
|
+
* │ └── index.ts — 6 LOC default-export
|
|
11
|
+
* ├── README.md — install + deploy instructions
|
|
12
|
+
* └── .gitignore — node_modules, .wrangler
|
|
13
|
+
*
|
|
14
|
+
* The scaffolder does NOT call `wrangler login`, `wrangler secret put`,
|
|
15
|
+
* or `wrangler deploy` — those are interactive and operator-owned. We
|
|
16
|
+
* print the exact commands to run as a "next steps" trailer.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Scaffold a new Nimbus project at the given directory.
|
|
20
|
+
*
|
|
21
|
+
* @param args Argv: `[project-name, ...flags]`. Flags:
|
|
22
|
+
* `--template worker-only` (default)
|
|
23
|
+
* `--name <wrangler-name>` (default: project-name)
|
|
24
|
+
* `--force` overwrite existing files.
|
|
25
|
+
*/
|
|
26
|
+
export declare function scaffold(args: string[]): Promise<number>;
|
|
27
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/commands/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkE9D"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/scaffold — `create-nimbus-app <project-name>`.
|
|
3
|
+
*
|
|
4
|
+
* v0.1 ships a single template: `worker-only`. The scaffolder writes:
|
|
5
|
+
*
|
|
6
|
+
* <project>/
|
|
7
|
+
* ├── package.json — deps: @nimbus-sh/worker, @nimbus-sh/sdk
|
|
8
|
+
* ├── wrangler.jsonc — the canonical 28-LOC embedder snippet
|
|
9
|
+
* ├── src/
|
|
10
|
+
* │ └── index.ts — 6 LOC default-export
|
|
11
|
+
* ├── README.md — install + deploy instructions
|
|
12
|
+
* └── .gitignore — node_modules, .wrangler
|
|
13
|
+
*
|
|
14
|
+
* The scaffolder does NOT call `wrangler login`, `wrangler secret put`,
|
|
15
|
+
* or `wrangler deploy` — those are interactive and operator-owned. We
|
|
16
|
+
* print the exact commands to run as a "next steps" trailer.
|
|
17
|
+
*/
|
|
18
|
+
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
19
|
+
import { resolve, join, basename } from 'node:path';
|
|
20
|
+
/**
|
|
21
|
+
* Scaffold a new Nimbus project at the given directory.
|
|
22
|
+
*
|
|
23
|
+
* @param args Argv: `[project-name, ...flags]`. Flags:
|
|
24
|
+
* `--template worker-only` (default)
|
|
25
|
+
* `--name <wrangler-name>` (default: project-name)
|
|
26
|
+
* `--force` overwrite existing files.
|
|
27
|
+
*/
|
|
28
|
+
export async function scaffold(args) {
|
|
29
|
+
const rawProjectName = args[0];
|
|
30
|
+
const projectName = rawProjectName === '.'
|
|
31
|
+
? basename(process.cwd())
|
|
32
|
+
: rawProjectName;
|
|
33
|
+
if (!projectName || projectName.startsWith('-')) {
|
|
34
|
+
process.stderr.write('create-nimbus-app: <project-name> required\n');
|
|
35
|
+
process.stderr.write('Usage: create-nimbus-app my-app [--name my-worker] [--force]\n');
|
|
36
|
+
return 64;
|
|
37
|
+
}
|
|
38
|
+
const parsed = parseFlags(args.slice(1));
|
|
39
|
+
const wranglerName = parsed['--name'] || projectName;
|
|
40
|
+
const force = '--force' in parsed;
|
|
41
|
+
const template = parsed['--template'] || 'worker-only';
|
|
42
|
+
if (template !== 'worker-only') {
|
|
43
|
+
process.stderr.write(`create-nimbus-app: unknown template "${template}". Only "worker-only" ships in v0.1.\n`);
|
|
44
|
+
return 64;
|
|
45
|
+
}
|
|
46
|
+
const target = rawProjectName === '.'
|
|
47
|
+
? process.cwd()
|
|
48
|
+
: resolve(process.cwd(), projectName);
|
|
49
|
+
if (existsSync(target) && !force) {
|
|
50
|
+
process.stderr.write(`create-nimbus-app: directory exists: ${target} (use --force to overwrite)\n`);
|
|
51
|
+
return 73;
|
|
52
|
+
}
|
|
53
|
+
mkdirSync(target, { recursive: true });
|
|
54
|
+
mkdirSync(join(target, 'src'), { recursive: true });
|
|
55
|
+
writeFileSync(join(target, 'package.json'), renderPackageJson(projectName));
|
|
56
|
+
writeFileSync(join(target, 'wrangler.jsonc'), renderWranglerJsonc(wranglerName));
|
|
57
|
+
writeFileSync(join(target, 'src', 'index.ts'), renderIndexTs());
|
|
58
|
+
writeFileSync(join(target, 'README.md'), renderReadme(projectName, wranglerName));
|
|
59
|
+
writeFileSync(join(target, '.gitignore'), '.wrangler\nnode_modules\ndist\n');
|
|
60
|
+
writeFileSync(join(target, 'tsconfig.json'), renderTsconfig());
|
|
61
|
+
// Stdout — JSON for machine, friendly stderr for humans.
|
|
62
|
+
process.stdout.write(JSON.stringify({
|
|
63
|
+
ok: true,
|
|
64
|
+
path: target,
|
|
65
|
+
nextSteps: [
|
|
66
|
+
`cd ${projectName}`,
|
|
67
|
+
'npm install',
|
|
68
|
+
'npx wrangler secret put JWT_SECRET',
|
|
69
|
+
'npx wrangler deploy',
|
|
70
|
+
],
|
|
71
|
+
}) + '\n');
|
|
72
|
+
process.stderr.write(`
|
|
73
|
+
✨ Scaffolded ${projectName} at ${target}
|
|
74
|
+
|
|
75
|
+
Next steps:
|
|
76
|
+
cd ${projectName}
|
|
77
|
+
npm install
|
|
78
|
+
npx wrangler secret put JWT_SECRET # paste a 32+ char hex secret
|
|
79
|
+
npx wrangler deploy
|
|
80
|
+
|
|
81
|
+
Deploy will provision the DO + R2 buckets on first run. Visit the
|
|
82
|
+
returned URL and hit "Launch" to mint a session.
|
|
83
|
+
|
|
84
|
+
Docs: https://github.com/AshishKumar4/Nimbus#readme
|
|
85
|
+
`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
// ── template renderers ──────────────────────────────────────────────
|
|
89
|
+
function renderPackageJson(name) {
|
|
90
|
+
return JSON.stringify({
|
|
91
|
+
name,
|
|
92
|
+
version: '0.1.0',
|
|
93
|
+
private: true,
|
|
94
|
+
type: 'module',
|
|
95
|
+
scripts: {
|
|
96
|
+
dev: 'wrangler dev --ip 0.0.0.0 --port 8787',
|
|
97
|
+
deploy: 'wrangler deploy',
|
|
98
|
+
typecheck: 'tsc --noEmit',
|
|
99
|
+
},
|
|
100
|
+
dependencies: {
|
|
101
|
+
'@nimbus-sh/worker': '^0.1.0',
|
|
102
|
+
'@nimbus-sh/sdk': '^0.1.0',
|
|
103
|
+
},
|
|
104
|
+
devDependencies: {
|
|
105
|
+
'@cloudflare/workers-types': '^4.20250327.0',
|
|
106
|
+
typescript: '^5.7.0',
|
|
107
|
+
wrangler: '^4.0.0',
|
|
108
|
+
},
|
|
109
|
+
}, null, 2) + '\n';
|
|
110
|
+
}
|
|
111
|
+
function renderWranglerJsonc(name) {
|
|
112
|
+
return `{
|
|
113
|
+
"$schema": "./node_modules/wrangler/config-schema.json",
|
|
114
|
+
"name": "${name}",
|
|
115
|
+
"main": "src/index.ts",
|
|
116
|
+
"compatibility_date": "2026-04-01",
|
|
117
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
118
|
+
"placement": { "mode": "smart" },
|
|
119
|
+
|
|
120
|
+
"assets": {
|
|
121
|
+
"directory": "node_modules/@nimbus-sh/worker/public",
|
|
122
|
+
"binding": "ASSETS",
|
|
123
|
+
"run_worker_first": ["/s/*", "/new"]
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// The Node-compat shims isomorphic-git + the npm installer need. Do
|
|
127
|
+
// not omit any of these — \`git clone\` will fail at runtime if any
|
|
128
|
+
// are missing.
|
|
129
|
+
"alias": {
|
|
130
|
+
"@lifo-sh/ui": "./node_modules/@nimbus-sh/worker/dist/stubs/lifo-ui.js",
|
|
131
|
+
"clean-git-ref": "clean-git-ref/lib/index.js",
|
|
132
|
+
"is-git-ref-name-valid": "is-git-ref-name-valid/index.js",
|
|
133
|
+
"crc-32": "crc-32",
|
|
134
|
+
"sha.js": "sha.js",
|
|
135
|
+
"pako": "pako",
|
|
136
|
+
"pify": "pify",
|
|
137
|
+
"diff": "diff",
|
|
138
|
+
"diff3": "diff3",
|
|
139
|
+
"ignore": "ignore",
|
|
140
|
+
"readable-stream": "readable-stream",
|
|
141
|
+
"simple-get": "simple-get",
|
|
142
|
+
"minimisted": "minimisted"
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
"durable_objects": {
|
|
146
|
+
"bindings": [{ "name": "NIMBUS_SESSION", "class_name": "NimbusSession" }]
|
|
147
|
+
},
|
|
148
|
+
"migrations": [
|
|
149
|
+
{ "tag": "nimbus-v1", "new_sqlite_classes": ["NimbusSession"] }
|
|
150
|
+
],
|
|
151
|
+
|
|
152
|
+
"worker_loaders": [{ "binding": "LOADER" }],
|
|
153
|
+
|
|
154
|
+
"r2_buckets": [
|
|
155
|
+
{ "binding": "NPM_TARBALL_CACHE", "bucket_name": "${name}-npm-cache" },
|
|
156
|
+
{ "binding": "NPM_PACKUMENT_CACHE", "bucket_name": "${name}-npm-packument-cache" },
|
|
157
|
+
{ "binding": "NIMBUS_RUNTIME_CACHE", "bucket_name": "nimbus-runtime-cache-public" }
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
function renderIndexTs() {
|
|
163
|
+
return `import {
|
|
164
|
+
NimbusSession,
|
|
165
|
+
SupervisorRPC,
|
|
166
|
+
NimbusAssetsRPC,
|
|
167
|
+
NimbusLoaderRPC,
|
|
168
|
+
NimbusLoadedWorker,
|
|
169
|
+
NimbusLoadedEntrypoint,
|
|
170
|
+
NimbusDurableObjectNamespace,
|
|
171
|
+
NimbusDOStub,
|
|
172
|
+
CirrusHmrRPC,
|
|
173
|
+
createNimbusHandler,
|
|
174
|
+
} from '@nimbus-sh/worker';
|
|
175
|
+
|
|
176
|
+
// Re-export the DO class + every RPC class so wrangler's
|
|
177
|
+
// \`durable_objects.bindings[].class_name\` lookup + \`enable_ctx_exports\`
|
|
178
|
+
// auto-populate loopback bindings (env.SUPERVISOR, etc.).
|
|
179
|
+
export {
|
|
180
|
+
NimbusSession,
|
|
181
|
+
SupervisorRPC,
|
|
182
|
+
NimbusAssetsRPC,
|
|
183
|
+
NimbusLoaderRPC,
|
|
184
|
+
NimbusLoadedWorker,
|
|
185
|
+
NimbusLoadedEntrypoint,
|
|
186
|
+
NimbusDurableObjectNamespace,
|
|
187
|
+
NimbusDOStub,
|
|
188
|
+
CirrusHmrRPC,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export default createNimbusHandler();
|
|
192
|
+
`;
|
|
193
|
+
}
|
|
194
|
+
function renderTsconfig() {
|
|
195
|
+
return JSON.stringify({
|
|
196
|
+
compilerOptions: {
|
|
197
|
+
target: 'ES2022',
|
|
198
|
+
module: 'ES2022',
|
|
199
|
+
moduleResolution: 'bundler',
|
|
200
|
+
lib: ['ES2022'],
|
|
201
|
+
types: ['@cloudflare/workers-types'],
|
|
202
|
+
strict: true,
|
|
203
|
+
noEmit: true,
|
|
204
|
+
skipLibCheck: true,
|
|
205
|
+
},
|
|
206
|
+
include: ['src/**/*.ts'],
|
|
207
|
+
}, null, 2) + '\n';
|
|
208
|
+
}
|
|
209
|
+
function renderReadme(name, wranglerName) {
|
|
210
|
+
return `# ${name}
|
|
211
|
+
|
|
212
|
+
A Nimbus-powered Cloudflare Worker.
|
|
213
|
+
|
|
214
|
+
## Setup
|
|
215
|
+
|
|
216
|
+
\`\`\`bash
|
|
217
|
+
npm install
|
|
218
|
+
npx wrangler secret put JWT_SECRET # paste a 32+ char hex secret
|
|
219
|
+
npx wrangler deploy
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
Then visit the URL wrangler prints. Click "Launch" to mint a session.
|
|
223
|
+
|
|
224
|
+
## Custom auth
|
|
225
|
+
|
|
226
|
+
By default tokens are enforced (\`JWT_SECRET\` set). To mint:
|
|
227
|
+
|
|
228
|
+
\`\`\`bash
|
|
229
|
+
JWT_SECRET=<your-secret> npx @nimbus-sh/cli token mint --tenant acme --sub alice
|
|
230
|
+
\`\`\`
|
|
231
|
+
|
|
232
|
+
Embed in a React app:
|
|
233
|
+
|
|
234
|
+
\`\`\`tsx
|
|
235
|
+
import { NimbusTerminal } from '@nimbus-sh/react';
|
|
236
|
+
|
|
237
|
+
<NimbusTerminal
|
|
238
|
+
endpoint="https://${wranglerName}.your-account.workers.dev"
|
|
239
|
+
token={jwt}
|
|
240
|
+
tenant="acme"
|
|
241
|
+
sub="alice"
|
|
242
|
+
/>
|
|
243
|
+
\`\`\`
|
|
244
|
+
|
|
245
|
+
## Customize
|
|
246
|
+
|
|
247
|
+
Edit \`src/index.ts\`:
|
|
248
|
+
|
|
249
|
+
\`\`\`ts
|
|
250
|
+
import { NimbusSession, createNimbusHandler } from '@nimbus-sh/worker';
|
|
251
|
+
import { issueNimbusToken } from '@nimbus-sh/sdk/token';
|
|
252
|
+
|
|
253
|
+
export { NimbusSession };
|
|
254
|
+
|
|
255
|
+
export default createNimbusHandler({
|
|
256
|
+
hooks: {
|
|
257
|
+
onSessionStart: ({ sessionId, tenantSegment }) => {
|
|
258
|
+
console.log(\`session \${sessionId} for \${tenantSegment}\`);
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
routes: async (req, env) => {
|
|
262
|
+
if (new URL(req.url).pathname === '/api/auth/mint') {
|
|
263
|
+
const { tenant, sub } = await req.json();
|
|
264
|
+
const token = await issueNimbusToken(env, { tn: tenant, sub });
|
|
265
|
+
return Response.json({ token });
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
\`\`\`
|
|
271
|
+
|
|
272
|
+
Docs: https://github.com/AshishKumar4/Nimbus
|
|
273
|
+
`;
|
|
274
|
+
}
|
|
275
|
+
function parseFlags(args) {
|
|
276
|
+
const out = {};
|
|
277
|
+
for (let i = 0; i < args.length; i++) {
|
|
278
|
+
const a = args[i];
|
|
279
|
+
if (!a.startsWith('--'))
|
|
280
|
+
continue;
|
|
281
|
+
if (args[i + 1] && !args[i + 1].startsWith('--')) {
|
|
282
|
+
out[a] = args[i + 1];
|
|
283
|
+
i++;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
out[a] = '';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qDAAqD;AACrD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BhE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/session — `nimbus session new` — mint a session via POST /new.
|
|
3
|
+
*/
|
|
4
|
+
/** Mint a fresh session and print its attach URL. */
|
|
5
|
+
export async function newSession(args) {
|
|
6
|
+
const parsed = parseFlags(args);
|
|
7
|
+
const endpoint = parsed['--endpoint']
|
|
8
|
+
?? process.env.NIMBUS_ENDPOINT
|
|
9
|
+
?? 'http://127.0.0.1:8787';
|
|
10
|
+
try {
|
|
11
|
+
const r = await fetch(`${endpoint.replace(/\/+$/, '')}/new`, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
redirect: 'manual',
|
|
14
|
+
});
|
|
15
|
+
const loc = r.headers.get('Location');
|
|
16
|
+
if (!loc) {
|
|
17
|
+
process.stderr.write(`nimbus session new: POST /new returned no Location (status ${r.status})\n`);
|
|
18
|
+
return 70;
|
|
19
|
+
}
|
|
20
|
+
const m = loc.match(/\/s\/([^/]+)/);
|
|
21
|
+
if (!m) {
|
|
22
|
+
process.stderr.write(`nimbus session new: unexpected Location: ${loc}\n`);
|
|
23
|
+
return 70;
|
|
24
|
+
}
|
|
25
|
+
const sessionId = m[1];
|
|
26
|
+
const url = `${endpoint.replace(/\/+$/, '')}/s/${sessionId}/`;
|
|
27
|
+
process.stdout.write(JSON.stringify({ sessionId, url }) + '\n');
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
process.stderr.write(`nimbus session new: ${e?.message || e}\n`);
|
|
32
|
+
return 70;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function parseFlags(args) {
|
|
36
|
+
const out = {};
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
const a = args[i];
|
|
39
|
+
if (!a.startsWith('--'))
|
|
40
|
+
continue;
|
|
41
|
+
out[a] = args[i + 1] ?? '';
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/token — `nimbus token mint` + `nimbus token verify`.
|
|
3
|
+
*
|
|
4
|
+
* Both verbs require `JWT_SECRET` in env. Mint writes the JWT to stdout
|
|
5
|
+
* (so `nimbus token mint --tenant acme > /tmp/jwt` works in scripts);
|
|
6
|
+
* verify parses a token from argv and prints the claims as JSON.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Programmatic interface for token mint. Used by `bin.ts` and exported
|
|
10
|
+
* from `@nimbus-sh/cli` so embedder scripts can call it directly.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { mintToken } from '@nimbus-sh/cli';
|
|
15
|
+
* const code = await mintToken(['--tenant', 'acme', '--sub', 'alice']);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @param args Raw CLI args (after the `token mint` prefix).
|
|
19
|
+
* @returns Process exit code (0 success, non-zero per
|
|
20
|
+
* `CLI_EXIT_CODES` semantics).
|
|
21
|
+
*/
|
|
22
|
+
export declare function mintToken(args: string[]): Promise<number>;
|
|
23
|
+
/** `nimbus token verify <token>` — verify + print claims as JSON. */
|
|
24
|
+
export declare function verifyTokenCmd(args: string[]): Promise<number>;
|
|
25
|
+
//# sourceMappingURL=token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/commands/token.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B/D;AAED,qEAAqE;AACrE,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBpE"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/token — `nimbus token mint` + `nimbus token verify`.
|
|
3
|
+
*
|
|
4
|
+
* Both verbs require `JWT_SECRET` in env. Mint writes the JWT to stdout
|
|
5
|
+
* (so `nimbus token mint --tenant acme > /tmp/jwt` works in scripts);
|
|
6
|
+
* verify parses a token from argv and prints the claims as JSON.
|
|
7
|
+
*/
|
|
8
|
+
import { issueNimbusToken, verifyNimbusToken } from '@nimbus-sh/sdk/token';
|
|
9
|
+
import { NimbusAuthError } from '@nimbus-sh/sdk/errors';
|
|
10
|
+
/**
|
|
11
|
+
* Programmatic interface for token mint. Used by `bin.ts` and exported
|
|
12
|
+
* from `@nimbus-sh/cli` so embedder scripts can call it directly.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { mintToken } from '@nimbus-sh/cli';
|
|
17
|
+
* const code = await mintToken(['--tenant', 'acme', '--sub', 'alice']);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @param args Raw CLI args (after the `token mint` prefix).
|
|
21
|
+
* @returns Process exit code (0 success, non-zero per
|
|
22
|
+
* `CLI_EXIT_CODES` semantics).
|
|
23
|
+
*/
|
|
24
|
+
export async function mintToken(args) {
|
|
25
|
+
const parsed = parseArgs(args, ['--tenant', '--sub', '--ttl', '--scopes', '--sid']);
|
|
26
|
+
const tn = parsed['--tenant'];
|
|
27
|
+
const sub = parsed['--sub'];
|
|
28
|
+
const ttlSec = parsed['--ttl'] ? Number(parsed['--ttl']) : undefined;
|
|
29
|
+
const scopes = parsed['--scopes'] ? parsed['--scopes'].split(',').map((s) => s.trim()) : undefined;
|
|
30
|
+
const sid = parsed['--sid'];
|
|
31
|
+
if (!tn) {
|
|
32
|
+
process.stderr.write('nimbus token mint: --tenant required\n');
|
|
33
|
+
return 64;
|
|
34
|
+
}
|
|
35
|
+
const secret = process.env.JWT_SECRET;
|
|
36
|
+
if (!secret) {
|
|
37
|
+
process.stderr.write('nimbus token mint: JWT_SECRET env var required\n');
|
|
38
|
+
return 78;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const token = await issueNimbusToken({ JWT_SECRET: secret }, { tn, sub, scopes, sid }, ttlSec !== undefined ? { ttlMs: ttlSec * 1000 } : {});
|
|
42
|
+
process.stdout.write(`${token}\n`);
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
return reportError(e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** `nimbus token verify <token>` — verify + print claims as JSON. */
|
|
50
|
+
export async function verifyTokenCmd(args) {
|
|
51
|
+
const token = args[0];
|
|
52
|
+
if (!token) {
|
|
53
|
+
process.stderr.write('nimbus token verify: <token> argument required\n');
|
|
54
|
+
return 64;
|
|
55
|
+
}
|
|
56
|
+
const secret = process.env.JWT_SECRET;
|
|
57
|
+
if (!secret) {
|
|
58
|
+
process.stderr.write('nimbus token verify: JWT_SECRET env var required\n');
|
|
59
|
+
return 78;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const verified = await verifyNimbusToken({ JWT_SECRET: secret }, token);
|
|
63
|
+
process.stdout.write(`${JSON.stringify(verified, null, 2)}\n`);
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return reportError(e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Map a NimbusAuthError or unknown error to a JSON stderr report. */
|
|
71
|
+
function reportError(e) {
|
|
72
|
+
if (e instanceof NimbusAuthError) {
|
|
73
|
+
process.stderr.write(`${JSON.stringify({ error: e.message, code: e.code })}\n`);
|
|
74
|
+
return e.httpStatus >= 500 ? 70 : 65;
|
|
75
|
+
}
|
|
76
|
+
process.stderr.write(`${JSON.stringify({ error: String(e?.message ?? e), code: 'E_UNKNOWN' })}\n`);
|
|
77
|
+
return 70;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Mini argv parser. Long flags only (`--key value` or `--key=value`).
|
|
81
|
+
* Returns a record of seen flags. Unknown flags are silently dropped
|
|
82
|
+
* (matches Unix tradition).
|
|
83
|
+
*/
|
|
84
|
+
function parseArgs(args, known) {
|
|
85
|
+
const out = {};
|
|
86
|
+
for (let i = 0; i < args.length; i++) {
|
|
87
|
+
const a = args[i];
|
|
88
|
+
if (!a.startsWith('--'))
|
|
89
|
+
continue;
|
|
90
|
+
const eq = a.indexOf('=');
|
|
91
|
+
let key, val;
|
|
92
|
+
if (eq >= 0) {
|
|
93
|
+
key = a.slice(0, eq);
|
|
94
|
+
val = a.slice(eq + 1);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
key = a;
|
|
98
|
+
val = args[i + 1] ?? '';
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
if (known.includes(key))
|
|
102
|
+
out[key] = val;
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nimbus-sh/cli — Programmatic surface.
|
|
3
|
+
*
|
|
4
|
+
* The CLI's `bin` entries (`nimbus`, `create-nimbus-app`) call these
|
|
5
|
+
* functions. Embedders building custom dev tooling can import them
|
|
6
|
+
* directly:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { mintToken, syncRuntimes } from '@nimbus-sh/cli';
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export { mintToken } from './commands/token.js';
|
|
13
|
+
export { syncRuntimes } from './commands/runtime-sync.js';
|
|
14
|
+
export { scaffold } from './commands/scaffold.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nimbus-sh/cli — Programmatic surface.
|
|
3
|
+
*
|
|
4
|
+
* The CLI's `bin` entries (`nimbus`, `create-nimbus-app`) call these
|
|
5
|
+
* functions. Embedders building custom dev tooling can import them
|
|
6
|
+
* directly:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { mintToken, syncRuntimes } from '@nimbus-sh/cli';
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export { mintToken } from './commands/token.js';
|
|
13
|
+
export { syncRuntimes } from './commands/runtime-sync.js';
|
|
14
|
+
export { scaffold } from './commands/scaffold.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/create-nimbus-app — scaffolder entry.
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper around `scaffold()` in commands/scaffold.ts. Exists as a
|
|
6
|
+
* separate bin so the eventual npm artifact can support
|
|
7
|
+
* `create-nimbus-app` without verb routing.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=scaffold-bin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-bin.d.ts","sourceRoot":"","sources":["../src/scaffold-bin.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/create-nimbus-app — scaffolder entry.
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper around `scaffold()` in commands/scaffold.ts. Exists as a
|
|
6
|
+
* separate bin so the eventual npm artifact can support
|
|
7
|
+
* `create-nimbus-app` without verb routing.
|
|
8
|
+
*/
|
|
9
|
+
import { scaffold } from './commands/scaffold.js';
|
|
10
|
+
scaffold(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
|
|
11
|
+
process.stderr.write(`create-nimbus-app: ${err?.stack || err}\n`);
|
|
12
|
+
process.exit(70);
|
|
13
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,WAAW,UAAU,CAAC"}
|
package/dist/version.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nimbus-sh/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Nimbus CLI — `create-nimbus-app` scaffolder + `token mint` + `runtime sync`.",
|
|
5
|
+
"homepage": "https://github.com/AshishKumar4/Nimbus",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/AshishKumar4/Nimbus.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"bugs": "https://github.com/AshishKumar4/Nimbus/issues",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"nimbus": "./dist/bin.js",
|
|
16
|
+
"create-nimbus-app": "./dist/scaffold-bin.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"workspace": "./src/index.ts",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json --noCheck --noEmit false --declaration true --declarationMap true --outDir dist --rootDir src && chmod +x dist/bin.js dist/scaffold-bin.js",
|
|
34
|
+
"prepack": "npm run build",
|
|
35
|
+
"typecheck": "tsc --noEmit"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@nimbus-sh/sdk": "^0.1.0",
|
|
39
|
+
"@nimbus-sh/worker": "^0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.0.0",
|
|
46
|
+
"typescript": "^5.7.0"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|