@nylorun/cli 0.2.0-beta → 0.3.0-beta
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/CHANGELOG.md +47 -0
- package/README.md +87 -40
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +132 -220
- package/dist/dev-entry.d.ts +5 -1
- package/dist/dev-entry.js +7 -6
- package/dist/dev.d.ts +45 -4
- package/dist/dev.js +245 -32
- package/dist/doctor.d.ts +25 -3
- package/dist/doctor.js +104 -16
- package/dist/environment.d.ts +4 -1
- package/dist/environment.js +12 -3
- package/dist/launcher.d.ts +5 -5
- package/dist/launcher.js +6 -3
- package/dist/model/configure.d.ts +30 -0
- package/dist/model/configure.js +117 -11
- package/dist/model/host-model.d.ts +5 -3
- package/dist/model/host-model.js +36 -15
- package/dist/project/attach.d.ts +32 -0
- package/dist/project/attach.js +241 -0
- package/dist/project/create-tenant.d.ts +26 -0
- package/dist/project/create-tenant.js +65 -0
- package/dist/project/credentials.d.ts +18 -0
- package/dist/project/credentials.js +64 -0
- package/dist/project/link.d.ts +20 -0
- package/dist/project/link.js +77 -0
- package/dist/project/root.d.ts +9 -0
- package/dist/project/root.js +51 -0
- package/dist/project/seed.d.ts +19 -0
- package/dist/project/seed.js +94 -0
- package/dist/runtime/baseline.d.ts +2 -0
- package/dist/runtime/baseline.js +11 -0
- package/dist/runtime/commands.d.ts +16 -0
- package/dist/runtime/commands.js +292 -0
- package/dist/runtime/ephemeral.d.ts +37 -0
- package/dist/runtime/ephemeral.js +84 -0
- package/dist/runtime/exit-codes.d.ts +9 -0
- package/dist/runtime/exit-codes.js +43 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +6 -0
- package/dist/runtime/launcher.d.ts +81 -0
- package/dist/runtime/launcher.js +243 -0
- package/dist/runtime/progress.d.ts +10 -0
- package/dist/runtime/progress.js +8 -0
- package/dist/runtime/version.d.ts +6 -0
- package/dist/runtime/version.js +27 -0
- package/dist/tenant/commands.d.ts +4 -0
- package/dist/tenant/commands.js +262 -0
- package/package.json +6 -3
- package/dist/project-runner.d.ts +0 -20
- package/dist/project-runner.js +0 -141
- package/dist/runtime-host.d.ts +0 -39
- package/dist/runtime-host.js +0 -355
- package/dist/scope.d.ts +0 -65
- package/dist/scope.js +0 -191
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# @nylorun/cli
|
|
2
2
|
|
|
3
|
+
## 0.3.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c49efed: **Breaking (pre-1.0 minor):** CLI reaches the local Runtime only through the installed Runtime's launcher.
|
|
8
|
+
|
|
9
|
+
- Dependencies: `@nylorun/agents` and `@nylorun/admin` only (no `@nylorun/runtime`, no `@nylorun/studio`).
|
|
10
|
+
- `nylorun runtime …` finds `nylorun-runtime` on PATH and checks its launcher and Host protocol; a missing or incompatible Runtime exits 1 with the install command for the recommended version (`package.json` `nylorun.runtime`). Nothing is downloaded. `nylorun doctor runtime` checks the prerequisites.
|
|
11
|
+
- `nylorun dev` runs the application entry under `tsx watch`; creates Tenants through `@nylorun/admin`.
|
|
12
|
+
- **Removed:** `nylorun serve`, `nylorun studio`, `--no-studio`, in-process Project runner.
|
|
13
|
+
- Install as a **devDependency**; production `start` is `node dist/src/main.js`.
|
|
14
|
+
|
|
15
|
+
- c49efed: **Breaking (pre-1.0 minor):** Replace a single SQLite Runtime per Project or home directory with a **Runtime Host** that serves isolated **Tenants**, selected by `Nylorun-Tenant` and negotiated with `Nylorun-Protocol` (protocol `2`, feature `runtime-tenants`). Vocabulary: Host root + Tenant + Project link.
|
|
16
|
+
|
|
17
|
+
- **core:** `PROTOCOL_VERSION = 2`, `HOST_PROTOCOL`, `TENANT_HEADER`, `PROTOCOL_HEADER`, `newTenantId` / `isTenantId`, `checkCompatibility`; health schema gains `hostId` + `protocol` (`service: "nylorun-runtime"`); Tenant/admin wire schemas; Tenant model routes under `/v1/tenant/*`.
|
|
18
|
+
- **runtime:** Host process + Tenant module; Tenant model routes under `/v1/tenant/*`; `startEphemeralRuntime` for tests/embeds; executors via `PUT /v1/executors`; sandbox prefix `nylorun-<tenant-id>-`.
|
|
19
|
+
- **agents:** `createClient({ url, key, tenant })`; Transport sends Tenant + protocol headers; `/health` compatibility cache; `IncompatibleRuntimeError` with upgrade remedies.
|
|
20
|
+
- **cli:** Host root lifecycle (`runtime up|down|status|logs|restart|run`); Project link (`.nylorun/link.json` + `credentials.json`); `tenant` commands; `runtime status --env` exports `NYLORUN_RUNTIME_URL`, `NYLORUN_SERVER_KEY`, `NYLORUN_TENANT`; removed Project/home SQLite selectors.
|
|
21
|
+
- **studio:** `startStudio({ …, tenant: { id, name } })`; proxy forwards Tenant + protocol headers; UI shows Tenant name/short id.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Pin agents to the tested release.
|
|
26
|
+
- Pin admin to the tested release.
|
|
27
|
+
- Pin runtime to the tested release.
|
|
28
|
+
- Updated dependencies [c49efed]
|
|
29
|
+
- Updated dependencies [c49efed]
|
|
30
|
+
- Updated dependencies [c49efed]
|
|
31
|
+
- Updated dependencies [fd9fd87]
|
|
32
|
+
- Updated dependencies
|
|
33
|
+
- Updated dependencies
|
|
34
|
+
- @nylorun/agents@0.6.0-beta
|
|
35
|
+
- @nylorun/admin@0.2.0-beta
|
|
36
|
+
|
|
37
|
+
## 0.2.1-beta
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- Pin agents to the tested release.
|
|
42
|
+
- Pin runtime to the tested release.
|
|
43
|
+
- Updated dependencies [1cd7dc7]
|
|
44
|
+
- Updated dependencies
|
|
45
|
+
- Updated dependencies
|
|
46
|
+
- Updated dependencies
|
|
47
|
+
- @nylorun/agents@0.5.0-beta
|
|
48
|
+
- @nylorun/runtime@0.9.0-beta
|
|
49
|
+
|
|
3
50
|
## 0.2.0-beta
|
|
4
51
|
|
|
5
52
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,61 +1,108 @@
|
|
|
1
1
|
# @nylorun/cli
|
|
2
2
|
|
|
3
|
-
The local `nylorun` executable.
|
|
3
|
+
The local `nylorun` executable. Depends on `@nylorun/agents` and `@nylorun/admin`
|
|
4
|
+
only among Nylorun packages — it runs the installed Runtime's **launcher** as a
|
|
5
|
+
process and never imports `@nylorun/runtime`. Vocabulary:
|
|
6
|
+
[runtime/src/CONTEXT.md](../runtime/src/CONTEXT.md).
|
|
7
|
+
|
|
8
|
+
Prerequisites, installed by the developer (the CLI never downloads them):
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
node --version # 24 or newer
|
|
12
|
+
npm install --global @nylorun/runtime # provides nylorun-runtime
|
|
13
|
+
nylorun doctor runtime # checks both
|
|
14
|
+
```
|
|
4
15
|
|
|
5
16
|
```sh
|
|
6
|
-
nylorun up
|
|
7
|
-
nylorun
|
|
8
|
-
nylorun
|
|
9
|
-
nylorun
|
|
10
|
-
nylorun
|
|
11
|
-
nylorun
|
|
12
|
-
nylorun runtime
|
|
13
|
-
nylorun
|
|
17
|
+
nylorun runtime up # launcher: start the Host
|
|
18
|
+
nylorun runtime down # stop the Host; keep Tenants and host.json
|
|
19
|
+
nylorun runtime status # Host id, address, version, Tenant list
|
|
20
|
+
nylorun runtime status --env # export lines for the linked Project
|
|
21
|
+
nylorun runtime logs [--follow] # Host + Tenant logs
|
|
22
|
+
nylorun runtime restart # restart onto the installed Runtime
|
|
23
|
+
nylorun runtime run # attached foreground Host
|
|
24
|
+
nylorun dev [entry] # up → link/create Tenant → tsx watch entry
|
|
25
|
+
nylorun dev --ephemeral # temporary Host root + one Tenant
|
|
26
|
+
nylorun dev --local-ui # also start Studio with a local dashboard
|
|
27
|
+
nylorun studio [--local-ui] # start Studio (launch URL + proxy token)
|
|
28
|
+
nylorun configure # replace model credential on the linked Tenant
|
|
29
|
+
nylorun tenant list|delete|status|reset
|
|
30
|
+
nylorun doctor runtime # prerequisites: Node 24+, installed Runtime
|
|
31
|
+
nylorun doctor sandbox # sandbox backend via Tenant API
|
|
14
32
|
```
|
|
15
33
|
|
|
16
|
-
|
|
34
|
+
`nylorun serve` remains removed. Production `start` is
|
|
35
|
+
`node dist/src/main.js` with `connectAgents` in the application.
|
|
36
|
+
`--local-ui` cannot be combined with `--no-studio`.
|
|
37
|
+
|
|
38
|
+
## The launcher
|
|
39
|
+
|
|
40
|
+
`nylorun runtime …` finds `nylorun-runtime` on PATH, checks that it speaks
|
|
41
|
+
launcher protocol 1 and a compatible Host protocol, and invokes it with
|
|
42
|
+
`--json`. The Host runs on the same Node, from the installed
|
|
43
|
+
`@nylorun/runtime`. When the launcher is missing or incompatible, the command
|
|
44
|
+
exits 1 with the install command for the recommended version
|
|
45
|
+
(`cli/package.json` `nylorun.runtime`). A project devDependency on
|
|
46
|
+
`@nylorun/runtime` also works, because npm scripts put `node_modules/.bin` on
|
|
47
|
+
PATH. See [building a desktop client](../docs/building-a-desktop-client.md)
|
|
48
|
+
for the launcher contract.
|
|
49
|
+
|
|
50
|
+
`dev` starts (or reuses) the Host via the launcher, creates a Tenant through
|
|
51
|
+
`@nylorun/admin` when the Project has no link, writes format-1 link +
|
|
52
|
+
credentials, and spawns `tsx watch <entry>` with
|
|
53
|
+
`NYLORUN_RUNTIME_URL`, `NYLORUN_TENANT` and `NYLORUN_SERVER_KEY`. The ready
|
|
54
|
+
banner hints `Studio: npm run studio`, or prints the Studio `launchUrl` when
|
|
55
|
+
started with `--local-ui`. Ctrl-C stops the child; the Host stays up.
|
|
56
|
+
`nylorun studio` resolves `@nylorun/studio`, supplies `cacheDir` from the Host
|
|
57
|
+
root (`NYLORUN_HOME` / `~/.nylorun`, never cwd), and prints `launchUrl`.
|
|
17
58
|
|
|
18
|
-
|
|
19
|
-
terminal. `dev` and `serve` attach to it, registering the project's exported
|
|
20
|
-
agents and connecting scoped executors through `PUT /v1/executors`. They start
|
|
21
|
-
the Runtime themselves when nothing is listening, and they leave it running on
|
|
22
|
-
exit, so a watch restart re-registers agents without losing sessions. Pass
|
|
23
|
-
`--no-autostart` to fail instead, which is what CI should do. `--foreground`
|
|
24
|
-
keeps `runtime start` attached for logs.
|
|
59
|
+
## Host root, Tenants and Project link
|
|
25
60
|
|
|
26
|
-
|
|
61
|
+
The **Host root** is `NYLORUN_HOME` or `~/.nylorun` (resolved absolute once).
|
|
62
|
+
It holds `host.json`, admin credentials and every Tenant; the Runtime itself
|
|
63
|
+
is installed by npm. Isolation is per **Tenant**, not per Project directory.
|
|
27
64
|
|
|
28
|
-
A
|
|
29
|
-
live in `.nylorun/` beside the nearest `package.json`, created on first use and
|
|
30
|
-
added to an existing `.gitignore`. `--global` (and running outside a project)
|
|
31
|
-
uses `~/.nylorun` instead, relocatable with `NYLORUN_HOME`. Both default to
|
|
32
|
-
`127.0.0.1:8787`; `--port` beats `NYLORUN_PORT`, then `PORT`, then the port
|
|
33
|
-
recorded for a running Runtime. `--db` or `NYLORUN_SQLITE_PATH` moves the
|
|
34
|
-
database. `nylorun runtime status --output json` prints the resolved scope for
|
|
35
|
-
scripts, and `nylorun logs [-f]` tails the Runtime's own output.
|
|
65
|
+
A **Project** stores only:
|
|
36
66
|
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
67
|
+
- `.nylorun/link.json` — `{ format, hostUrl, hostId, tenantId }`
|
|
68
|
+
- `.nylorun/credentials.json` — application key and principal id (0600); no
|
|
69
|
+
executor tokens
|
|
70
|
+
- `.nylorun/.gitignore` containing `*`
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
eval "$(npx nylorun runtime status --env)"
|
|
74
|
+
# → NYLORUN_RUNTIME_URL, NYLORUN_SERVER_KEY, NYLORUN_TENANT
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Port defaults to `8787` on loopback. On first Host setup, if that port is
|
|
78
|
+
taken, the launcher picks a free loopback port and persists it. Explicit
|
|
79
|
+
`--port` is strict.
|
|
40
80
|
|
|
41
81
|
## Exit codes
|
|
42
82
|
|
|
43
83
|
| Code | Meaning |
|
|
44
84
|
| --- | --- |
|
|
45
85
|
| 0 | Success, including `down` when nothing was running |
|
|
46
|
-
| 1 | Generic failure |
|
|
86
|
+
| 1 | Generic failure / launcher operation failed |
|
|
47
87
|
| 2 | Usage error |
|
|
48
88
|
| 3 | `runtime status` or `logs`: not running |
|
|
49
|
-
| 4 | Port held by another process
|
|
50
|
-
| 5 |
|
|
51
|
-
|
|
|
52
|
-
| 7 | The Runtime did not become healthy |
|
|
89
|
+
| 4 | Port held by another process |
|
|
90
|
+
| 5 | Protocol / feature incompatible with this CLI |
|
|
91
|
+
| 7 | The Host did not become ready |
|
|
53
92
|
| 130 / 143 | SIGINT / SIGTERM |
|
|
54
93
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
94
|
+
Install the CLI as a **devDependency**. Generated applications keep
|
|
95
|
+
`@nylorun/agents` alone in production dependencies.
|
|
96
|
+
|
|
97
|
+
## Troubleshooting
|
|
98
|
+
|
|
99
|
+
| Symptom | What to do |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| Quarantined Tenant | `nylorun tenant status` shows reason and `repair` |
|
|
102
|
+
| `426` / exit 5 | Upgrade CLI / Runtime pin, or pin a matching older set |
|
|
103
|
+
| Port conflict | Stop the other process or `nylorun runtime up --port <n>` |
|
|
104
|
+
| Logs | `nylorun runtime logs --follow` |
|
|
105
|
+
| Runtime not installed | `npm install --global @nylorun/runtime`; `nylorun doctor runtime` checks |
|
|
58
106
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
harness depends on this package. See [package architecture](../docs/design/package-architecture.md).
|
|
107
|
+
See [package architecture](../docs/design/package-architecture.md) and
|
|
108
|
+
[MIGRATION.md](../MIGRATION.md#runtime-clients-and-admin-api-breaking-beta).
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import "./runtime/baseline.js";
|
package/dist/cli.js
CHANGED
|
@@ -1,46 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
2
|
+
import "./runtime/baseline.js";
|
|
4
3
|
import { createRequire } from "node:module";
|
|
5
|
-
import {
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { ConfigurationCancelled, configureProvider, fetchModelCatalog, } from "./model/configure.js";
|
|
6
6
|
import { putHostModel } from "./model/host-model.js";
|
|
7
|
-
import { develop, developmentPreflight } from "./dev.js";
|
|
7
|
+
import { develop, developmentPreflight, startStudio } from "./dev.js";
|
|
8
8
|
import { CliError } from "./errors.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
9
|
+
import { runtimeCommand, runtimeUsage } from "./runtime/commands.js";
|
|
10
|
+
import { findProjectRoot, requireProjectRoot } from "./project/root.js";
|
|
11
|
+
import { printLinkedEnvExports } from "./project/attach.js";
|
|
12
|
+
import { readLink as readProjectLink } from "./project/link.js";
|
|
13
|
+
import { readCredentials as readProjectCredentials } from "./project/credentials.js";
|
|
14
|
+
import { tenantCommand } from "./tenant/commands.js";
|
|
15
|
+
import { resolveHome } from "./runtime/launcher.js";
|
|
16
|
+
const usage = `nylorun <runtime|up|down|logs|dev|studio|configure|doctor|tenant>
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
runtime stop [--global] [--timeout <seconds>]
|
|
16
|
-
runtime status [--output json] [--global]
|
|
17
|
-
runtime logs [-f] [-n <lines>] [--global]
|
|
18
|
-
up alias for "runtime start"
|
|
19
|
-
down alias for "runtime stop"
|
|
20
|
-
logs alias for "runtime logs"
|
|
18
|
+
${runtimeUsage}
|
|
21
19
|
|
|
22
|
-
dev [--
|
|
23
|
-
|
|
24
|
-
configure
|
|
25
|
-
|
|
26
|
-
doctor sandbox [--json] show which sandbox backend this
|
|
27
|
-
|
|
28
|
-
* Flags are parsed before the scope is resolved so --port can win over a PORT in .env, which
|
|
29
|
-
* is only loaded once the project root is known.
|
|
30
|
-
*/
|
|
20
|
+
dev [entry] [--ephemeral] [--local-ui] [--no-studio] [--no-open]
|
|
21
|
+
studio [--local-ui] [--port <n>] [--no-open]
|
|
22
|
+
configure
|
|
23
|
+
doctor runtime [--json] check the prerequisites: Node 24+ and an installed Runtime
|
|
24
|
+
doctor sandbox [--json] show which sandbox backend this Tenant's Host offers
|
|
25
|
+
tenant current|list [--json]|use <name-or-id>|status [--json]|reset|delete`;
|
|
31
26
|
function parseFlags(args, allowed = {}) {
|
|
32
27
|
const booleans = new Set();
|
|
33
28
|
const values = new Map();
|
|
34
29
|
const rest = [];
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const valueNames = new Set(["--port", "--db", ...(allowed.values ?? [])]);
|
|
30
|
+
const booleanNames = new Set(allowed.booleans ?? []);
|
|
31
|
+
const valueNames = new Set(allowed.values ?? []);
|
|
38
32
|
for (let index = 0; index < args.length; index += 1) {
|
|
39
33
|
const arg = args[index];
|
|
40
34
|
if (!arg.startsWith("-")) {
|
|
41
35
|
rest.push(arg);
|
|
42
36
|
continue;
|
|
43
37
|
}
|
|
38
|
+
if (arg === "--global" || arg === "--db") {
|
|
39
|
+
throw usageError(`${arg} was removed. Use the Runtime Host root (NYLORUN_HOME) and a Project link instead.`);
|
|
40
|
+
}
|
|
44
41
|
if (booleanNames.has(arg)) {
|
|
45
42
|
if (booleans.has(arg))
|
|
46
43
|
throw usageError(`${arg} may only be supplied once.`);
|
|
@@ -58,198 +55,131 @@ function parseFlags(args, allowed = {}) {
|
|
|
58
55
|
}
|
|
59
56
|
throw usageError(usage);
|
|
60
57
|
}
|
|
61
|
-
|
|
62
|
-
scope.global = true;
|
|
63
|
-
if (values.has("--port"))
|
|
64
|
-
scope.port = parsePort(values.get("--port"));
|
|
65
|
-
if (values.has("--db"))
|
|
66
|
-
scope.db = values.get("--db");
|
|
67
|
-
return { scope, rest, booleans, values };
|
|
58
|
+
return { rest, booleans, values };
|
|
68
59
|
}
|
|
69
60
|
const usageError = (message) => new CliError(message, 2);
|
|
70
61
|
function parsePort(value) {
|
|
71
|
-
if (value === undefined)
|
|
72
|
-
|
|
62
|
+
if (value === undefined || !/^\d+$/u.test(value))
|
|
63
|
+
throw usageError(`Invalid --port: ${value ?? "(missing)"}`);
|
|
73
64
|
const port = Number(value);
|
|
74
65
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
75
|
-
throw usageError(
|
|
66
|
+
throw usageError(`Invalid --port: ${value}`);
|
|
76
67
|
return port;
|
|
77
68
|
}
|
|
78
|
-
async function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
69
|
+
async function resolveLinkedAuth(projectRoot) {
|
|
70
|
+
const link = await readProjectLink(projectRoot);
|
|
71
|
+
const credentials = await readProjectCredentials(projectRoot);
|
|
72
|
+
if (link && credentials) {
|
|
73
|
+
return {
|
|
74
|
+
url: link.hostUrl,
|
|
75
|
+
key: credentials.applicationKey,
|
|
76
|
+
tenantId: link.tenantId,
|
|
77
|
+
tenantName: link.tenantId,
|
|
78
|
+
};
|
|
86
79
|
}
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
const url = process.env.NYLORUN_RUNTIME_URL?.trim();
|
|
81
|
+
const key = process.env.NYLORUN_SERVER_KEY?.trim();
|
|
82
|
+
const tenantId = process.env.NYLORUN_TENANT?.trim();
|
|
83
|
+
if (url && key && tenantId) {
|
|
84
|
+
return { url, key, tenantId, tenantName: tenantId };
|
|
89
85
|
}
|
|
90
|
-
|
|
91
|
-
return studio.startStudio({
|
|
92
|
-
runtimeUrl: agentServerUrl,
|
|
93
|
-
serverKey,
|
|
94
|
-
open,
|
|
95
|
-
...(port === undefined ? {} : { port }),
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
function printStatus(status) {
|
|
99
|
-
const { scope, runtime } = status;
|
|
100
|
-
const lines = [
|
|
101
|
-
["Scope", `${scope.kind} (${scope.kind === "project" ? scope.root : scope.dataDir})`],
|
|
102
|
-
["State", status.state],
|
|
103
|
-
["URL", scope.url],
|
|
104
|
-
["PID", runtime?.pid ? String(runtime.pid) : status.pid ? String(status.pid) : undefined],
|
|
105
|
-
["Runtime", status.version],
|
|
106
|
-
["CLI expects", cliRuntimeVersion()],
|
|
107
|
-
["Database", scope.sqlitePath],
|
|
108
|
-
["Log", scope.logPath],
|
|
109
|
-
["Started", runtime?.startedAt],
|
|
110
|
-
];
|
|
111
|
-
const width = Math.max(...lines.map(([label]) => label.length));
|
|
112
|
-
for (const [label, value] of lines)
|
|
113
|
-
if (value !== undefined)
|
|
114
|
-
console.log(`${label.padEnd(width)} ${value}`);
|
|
115
|
-
}
|
|
116
|
-
async function runtimeCommand(args) {
|
|
117
|
-
const [verb, ...rest] = args;
|
|
118
|
-
if (verb === "start") {
|
|
119
|
-
const flags = parseFlags(rest, { booleans: ["--foreground", "--restart"] });
|
|
120
|
-
if (flags.rest.length)
|
|
121
|
-
throw usageError(usage);
|
|
122
|
-
const scope = resolveScope(flags.scope);
|
|
123
|
-
loadScopeEnvironment(scope);
|
|
124
|
-
const before = await hostStatus(scope);
|
|
125
|
-
if (before.state === "running" && !flags.booleans.has("--restart")) {
|
|
126
|
-
console.log(`Runtime already running for ${scope.label} at ${scope.url} (pid ${before.runtime?.pid ?? before.pid}).`);
|
|
127
|
-
warnIncompatible(scope, before);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const status = await startHost(scope, {
|
|
131
|
-
foreground: flags.booleans.has("--foreground"),
|
|
132
|
-
restart: flags.booleans.has("--restart"),
|
|
133
|
-
});
|
|
134
|
-
printStatus(status);
|
|
135
|
-
console.log(`\n→ npx nylorun dev`);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (verb === "stop") {
|
|
139
|
-
const flags = parseFlags(rest, { values: ["--timeout"] });
|
|
140
|
-
if (flags.rest.length)
|
|
141
|
-
throw usageError(usage);
|
|
142
|
-
const scope = resolveScope(flags.scope);
|
|
143
|
-
const seconds = flags.values.has("--timeout")
|
|
144
|
-
? Number(flags.values.get("--timeout"))
|
|
145
|
-
: undefined;
|
|
146
|
-
if (seconds !== undefined && (!Number.isFinite(seconds) || seconds <= 0))
|
|
147
|
-
throw usageError("--timeout must be a positive number of seconds.");
|
|
148
|
-
const result = await stopHost(scope, {
|
|
149
|
-
...(seconds === undefined ? {} : { timeoutMs: seconds * 1000 }),
|
|
150
|
-
});
|
|
151
|
-
console.log(result === "stopped"
|
|
152
|
-
? `Stopped the Runtime for ${scope.label}. SQLite and credentials are kept in ${scope.dataDir}.`
|
|
153
|
-
: `No Runtime is running for ${scope.label}.`);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (verb === "status") {
|
|
157
|
-
const flags = parseFlags(rest, { values: ["--output"] });
|
|
158
|
-
if (flags.rest.length)
|
|
159
|
-
throw usageError(usage);
|
|
160
|
-
const output = flags.values.get("--output") ?? "text";
|
|
161
|
-
if (output !== "text" && output !== "json")
|
|
162
|
-
throw usageError("--output must be text or json.");
|
|
163
|
-
const scope = resolveScope(flags.scope);
|
|
164
|
-
const status = await hostStatus(scope);
|
|
165
|
-
if (output === "json")
|
|
166
|
-
console.log(JSON.stringify({
|
|
167
|
-
scope: scope.kind,
|
|
168
|
-
root: scope.kind === "project" ? scope.root : scope.dataDir,
|
|
169
|
-
url: scope.url,
|
|
170
|
-
state: status.state,
|
|
171
|
-
pid: status.runtime?.pid ?? status.pid ?? null,
|
|
172
|
-
runtimeVersion: status.version ?? null,
|
|
173
|
-
cliVersion: cliRuntimeVersion() ?? null,
|
|
174
|
-
scopeId: status.scopeId ?? null,
|
|
175
|
-
sqlitePath: scope.sqlitePath,
|
|
176
|
-
logPath: scope.logPath,
|
|
177
|
-
startedAt: status.runtime?.startedAt ?? null,
|
|
178
|
-
}, null, 2));
|
|
179
|
-
else
|
|
180
|
-
printStatus(status);
|
|
181
|
-
if (status.state !== "running")
|
|
182
|
-
process.exitCode = status.state === "port-conflict" ? 4 : 3;
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (verb === "logs") {
|
|
186
|
-
const flags = parseFlags(rest, { booleans: ["-f"], values: ["-n"] });
|
|
187
|
-
if (flags.rest.length)
|
|
188
|
-
throw usageError(usage);
|
|
189
|
-
const lines = flags.values.has("-n") ? Number(flags.values.get("-n")) : 200;
|
|
190
|
-
if (!Number.isInteger(lines) || lines < 1)
|
|
191
|
-
throw usageError("-n must be a positive integer.");
|
|
192
|
-
await tailLog(resolveScope(flags.scope), {
|
|
193
|
-
follow: flags.booleans.has("-f"),
|
|
194
|
-
lines,
|
|
195
|
-
});
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
throw usageError(usage);
|
|
86
|
+
throw new CliError(`No Project link in ${projectRoot}. Run nylorun dev, or set NYLORUN_RUNTIME_URL, NYLORUN_SERVER_KEY and NYLORUN_TENANT.`, 1);
|
|
199
87
|
}
|
|
200
88
|
async function main() {
|
|
201
89
|
const [rawCommand, ...rawArgs] = process.argv.slice(2);
|
|
202
90
|
if (!rawCommand || rawCommand === "--help" || rawCommand === "-h")
|
|
203
91
|
return void console.log(usage);
|
|
204
92
|
const aliases = {
|
|
205
|
-
up: "
|
|
206
|
-
down: "
|
|
93
|
+
up: "up",
|
|
94
|
+
down: "down",
|
|
207
95
|
logs: "logs",
|
|
208
96
|
};
|
|
209
97
|
const command = rawCommand in aliases ? "runtime" : rawCommand;
|
|
210
98
|
const args = rawCommand in aliases ? [aliases[rawCommand], ...rawArgs] : rawArgs;
|
|
211
|
-
if (command === "
|
|
212
|
-
return await
|
|
99
|
+
if (command === "tenant")
|
|
100
|
+
return await tenantCommand(args);
|
|
101
|
+
if (command === "runtime") {
|
|
102
|
+
// F2-7: envHook prints the linked Project's three variables.
|
|
103
|
+
return await runtimeCommand(args, {
|
|
104
|
+
envHook: async () => {
|
|
105
|
+
const root = findProjectRoot() ?? process.cwd();
|
|
106
|
+
await printLinkedEnvExports(root);
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
213
110
|
if (command === "doctor") {
|
|
214
111
|
const [topic, ...options] = args;
|
|
215
|
-
if (topic !== "sandbox"
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
112
|
+
if ((topic !== "sandbox" && topic !== "runtime") ||
|
|
113
|
+
options.some((option) => option !== "--json"))
|
|
114
|
+
throw usageError("Usage: nylorun doctor runtime|sandbox [--json]");
|
|
115
|
+
const { doctorRuntime, doctorSandbox } = await import("./doctor.js");
|
|
116
|
+
const json = options.includes("--json");
|
|
117
|
+
if (topic === "runtime")
|
|
118
|
+
await doctorRuntime({ json });
|
|
119
|
+
else
|
|
120
|
+
await doctorSandbox({ json });
|
|
219
121
|
return;
|
|
220
122
|
}
|
|
221
123
|
if (command === "dev") {
|
|
222
124
|
const flags = parseFlags(args, {
|
|
223
|
-
booleans: ["--
|
|
125
|
+
booleans: ["--ephemeral", "--local-ui", "--no-studio", "--no-open"],
|
|
224
126
|
});
|
|
225
|
-
if (flags.rest.length)
|
|
127
|
+
if (flags.rest.length > 1)
|
|
226
128
|
throw usageError(usage);
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
129
|
+
if (flags.booleans.has("--local-ui") && flags.booleans.has("--no-studio"))
|
|
130
|
+
throw usageError("--local-ui cannot be combined with --no-studio.");
|
|
131
|
+
requireProjectRoot();
|
|
132
|
+
developmentPreflight([
|
|
133
|
+
...flags.rest,
|
|
134
|
+
...[...flags.booleans],
|
|
135
|
+
]);
|
|
136
|
+
process.exitCode = await develop({
|
|
137
|
+
...(flags.rest[0] ? { entry: flags.rest[0] } : {}),
|
|
138
|
+
flags: [...flags.booleans],
|
|
237
139
|
});
|
|
238
|
-
process.exitCode = await develop([...flags.booleans]);
|
|
239
140
|
return;
|
|
240
141
|
}
|
|
241
142
|
if (command === "serve") {
|
|
242
|
-
|
|
243
|
-
|
|
143
|
+
throw usageError("nylorun serve was removed. Use nylorun dev [entry] in development, or node dist/src/main.js with NYLORUN_RUNTIME_URL, NYLORUN_TENANT and NYLORUN_SERVER_KEY.");
|
|
144
|
+
}
|
|
145
|
+
if (command === "studio") {
|
|
146
|
+
const flags = parseFlags(args, {
|
|
147
|
+
booleans: ["--local-ui", "--no-open"],
|
|
148
|
+
values: ["--port"],
|
|
149
|
+
});
|
|
150
|
+
if (flags.rest.length)
|
|
244
151
|
throw usageError(usage);
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
152
|
+
const projectRoot = findProjectRoot() ?? process.cwd();
|
|
153
|
+
// Surface missing Studio early with the same Install message as preflight.
|
|
154
|
+
try {
|
|
155
|
+
createRequire(join(projectRoot, "package.json")).resolve("@nylorun/studio");
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
throw new Error("Install @nylorun/studio to use the Studio dashboard.");
|
|
159
|
+
}
|
|
160
|
+
const auth = await resolveLinkedAuth(projectRoot);
|
|
161
|
+
const localUi = flags.booleans.has("--local-ui");
|
|
162
|
+
// I2: startStudio default is hosted; `--local-ui` forces local + cacheDir.
|
|
163
|
+
const dashboard = await startStudio({
|
|
164
|
+
runtimeUrl: auth.url,
|
|
165
|
+
serverKey: auth.key,
|
|
166
|
+
tenant: { id: auth.tenantId, name: auth.tenantName },
|
|
167
|
+
open: !flags.booleans.has("--no-open"),
|
|
168
|
+
localUi,
|
|
169
|
+
cacheDir: resolveHome(),
|
|
170
|
+
projectRoot,
|
|
171
|
+
...(flags.values.has("--port")
|
|
172
|
+
? { port: parsePort(flags.values.get("--port")) }
|
|
173
|
+
: {}),
|
|
174
|
+
});
|
|
175
|
+
console.log(`Studio ${dashboard.launchUrl}`);
|
|
176
|
+
if (!localUi && dashboard.launchUrl.startsWith("https://local.nylorun.studio")) {
|
|
177
|
+
console.log(`Safari or offline: nylorun studio --local-ui`);
|
|
178
|
+
}
|
|
179
|
+
await new Promise((resolve, reject) => {
|
|
180
|
+
const close = () => void dashboard.close().then(resolve, reject);
|
|
181
|
+
process.once("SIGINT", close);
|
|
182
|
+
process.once("SIGTERM", close);
|
|
253
183
|
});
|
|
254
184
|
return;
|
|
255
185
|
}
|
|
@@ -257,47 +187,29 @@ async function main() {
|
|
|
257
187
|
const flags = parseFlags(args);
|
|
258
188
|
if (flags.rest.length)
|
|
259
189
|
throw usageError(usage);
|
|
260
|
-
const
|
|
261
|
-
|
|
190
|
+
const projectRoot = findProjectRoot() ?? process.cwd();
|
|
191
|
+
const auth = await resolveLinkedAuth(projectRoot);
|
|
262
192
|
const controller = new AbortController();
|
|
263
193
|
const cancel = (signal) => controller.abort(new ConfigurationCancelled(signal));
|
|
264
194
|
process.once("SIGINT", () => cancel("SIGINT"));
|
|
265
195
|
process.once("SIGTERM", () => cancel("SIGTERM"));
|
|
266
|
-
const
|
|
267
|
-
const health = await fetch(`${scope.url}/health`).catch(() => undefined);
|
|
196
|
+
const health = await fetch(`${auth.url}/health`).catch(() => undefined);
|
|
268
197
|
if (!health?.ok)
|
|
269
|
-
throw new CliError(`No Runtime is listening at ${
|
|
270
|
-
const
|
|
271
|
-
|
|
198
|
+
throw new CliError(`No Runtime Host is listening at ${auth.url}. Start one with "nylorun runtime up".`, 6);
|
|
199
|
+
const catalog = await fetchModelCatalog({
|
|
200
|
+
url: auth.url,
|
|
201
|
+
key: auth.key,
|
|
202
|
+
tenantId: auth.tenantId,
|
|
203
|
+
});
|
|
204
|
+
const prompted = await configureProvider({
|
|
205
|
+
signal: controller.signal,
|
|
206
|
+
catalog,
|
|
207
|
+
});
|
|
208
|
+
await putHostModel(auth.url, auth.key, prompted, auth.tenantId);
|
|
272
209
|
return;
|
|
273
210
|
}
|
|
274
|
-
|
|
275
|
-
throw usageError(usage);
|
|
276
|
-
const flags = parseFlags(args, {
|
|
277
|
-
booleans: ["--no-open"],
|
|
278
|
-
values: ["--runtime-url", "--studio-port"],
|
|
279
|
-
});
|
|
280
|
-
if (flags.rest.length)
|
|
281
|
-
throw usageError(usage);
|
|
282
|
-
const scope = resolveScope(flags.scope);
|
|
283
|
-
const runtimeUrl = flags.values.get("--runtime-url") ?? scope.url;
|
|
284
|
-
const studioPort = flags.values.has("--studio-port")
|
|
285
|
-
? parsePort(flags.values.get("--studio-port"))
|
|
286
|
-
: flags.scope.port !== undefined && flags.values.has("--runtime-url")
|
|
287
|
-
? flags.scope.port
|
|
288
|
-
: undefined;
|
|
289
|
-
const dashboard = await startStudio(runtimeUrl, await serverKeyFor(scope), !flags.booleans.has("--no-open"), studioPort);
|
|
290
|
-
console.log(`Studio on ${dashboard.address}`);
|
|
291
|
-
await new Promise((resolve, reject) => {
|
|
292
|
-
const close = () => void dashboard.close().then(resolve, reject);
|
|
293
|
-
process.once("SIGINT", close);
|
|
294
|
-
process.once("SIGTERM", close);
|
|
295
|
-
});
|
|
211
|
+
throw usageError(usage);
|
|
296
212
|
}
|
|
297
|
-
/**
|
|
298
|
-
* Probing a port that answers but is not a Runtime can leave a pooled connection holding the
|
|
299
|
-
* event loop open, so a finished command exits explicitly once its output has drained.
|
|
300
|
-
*/
|
|
301
213
|
async function finish(code) {
|
|
302
214
|
process.exitCode = code;
|
|
303
215
|
for (const stream of [process.stdout, process.stderr])
|
package/dist/dev-entry.d.ts
CHANGED