@nylorun/cli 0.14.1-beta → 0.15.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 +32 -0
- package/README.md +17 -92
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +10 -144
- package/package.json +3 -5
- package/dist/access/commands.d.ts +0 -8
- package/dist/access/commands.js +0 -33
- package/dist/baseline.d.ts +0 -2
- package/dist/baseline.js +0 -10
- package/dist/doctor.d.ts +0 -25
- package/dist/doctor.js +0 -65
- package/dist/environment.d.ts +0 -5
- package/dist/environment.js +0 -73
- package/dist/errors.d.ts +0 -5
- package/dist/errors.js +0 -9
- package/dist/installation/commands.d.ts +0 -7
- package/dist/installation/commands.js +0 -80
- package/dist/model/configure.d.ts +0 -57
- package/dist/model/configure.js +0 -293
- package/dist/model/host-model.d.ts +0 -4
- package/dist/model/host-model.js +0 -13
- package/dist/project/connection.d.ts +0 -20
- package/dist/project/connection.js +0 -38
- package/dist/project/credentials.d.ts +0 -9
- package/dist/project/credentials.js +0 -21
- package/dist/project/env.d.ts +0 -3
- package/dist/project/env.js +0 -20
- package/dist/project/link.d.ts +0 -10
- package/dist/project/link.js +0 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @nylorun/cli
|
|
2
2
|
|
|
3
|
+
## 0.15.0-beta
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 1345f61: **`nylo` ships in `nylorun`; `@nylorun/cli` is deprecated.** One package runs a project's local Tenant (`nylorun`) and is the Runtime client of the linked installation (`nylo`). MIGRATION.md ("`nylo` ships in `nylorun`") has the details.
|
|
8
|
+
|
|
9
|
+
- `nylorun`: a second bin, `nylo` (`npx -p nylorun nylo <command>`), with the same commands, flags, output and exit codes as `@nylorun/cli`'s. The `nylorun` command never loads it. nylorun now depends on `@nylorun/admin`, which `nylo` uses. `nylo configure`'s provider sign-in comes from `@earendil-works/pi-ai` (about 100 MB with its provider SDKs), which is not a dependency: the first `nylo configure` installs the tested version into `~/.nylorun/lib/pi-ai-<version>/` with npm, without install scripts, unless it resolves beside nylorun, so `npx nylorun` stays about 11 MB. `nylorun configure`, `nylorun status --env` and `nylorun doctor sandbox` still exit 2, now naming `npx -p nylorun nylo …`. `nylorun status|reset` (the local Tenant's containers and volumes) and `nylo status|reset` (the Management API) keep their meanings; both usages and the README say which is which.
|
|
10
|
+
- **Deprecated (`@nylorun/cli`):** its `nylo` prints one line on stderr and runs nylorun's `nylo` with the same arguments and exit code; stdout is unchanged, so `eval "$(npx @nylorun/cli env)"` still works. It depends on `nylorun` only. A later release removes it.
|
|
11
|
+
- **Breaking (`@nylorun/admin`): the `./project` subpath is removed.** It re-exported `@nylorun/core/project` for `nylo`, which now imports Core directly. Import `@nylorun/core/project` instead.
|
|
12
|
+
- `@nylorun/create-agent`: `compatibility.json` no longer pins `@nylorun/cli`; the examples run `nylo` from their `nylorun` devDependency.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Pin nylorun to the tested release.
|
|
17
|
+
- Updated dependencies [554df94]
|
|
18
|
+
- Updated dependencies [1345f61]
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- nylorun@0.13.0-beta
|
|
23
|
+
|
|
24
|
+
## 0.14.2-beta
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Pin agents to the tested release.
|
|
29
|
+
- Pin admin to the tested release.
|
|
30
|
+
- Updated dependencies
|
|
31
|
+
- Updated dependencies
|
|
32
|
+
- @nylorun/agents@0.19.1-beta
|
|
33
|
+
- @nylorun/admin@0.14.1-beta
|
|
34
|
+
|
|
3
35
|
## 0.14.1-beta
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,101 +1,26 @@
|
|
|
1
|
-
# @nylorun/cli
|
|
1
|
+
# @nylorun/cli (deprecated)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
and
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
[runtime/src/CONTEXT.md](../runtime/src/CONTEXT.md).
|
|
3
|
+
`nylo`, the Runtime client, ships in [`nylorun`](../nylorun/README.md#the-runtime-client-nylo)
|
|
4
|
+
beside the `nylorun` command. This package is kept for one release so that
|
|
5
|
+
existing commands and scripts keep working: its `nylo` prints one line on
|
|
6
|
+
stderr, then runs nylorun's `nylo` with the same arguments and exits with its
|
|
7
|
+
code. stdout is `nylo`'s alone, so `eval` still works:
|
|
9
8
|
|
|
10
9
|
```sh
|
|
11
|
-
npx nylorun
|
|
12
|
-
npx @nylorun/cli
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Commands
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
nylo status [--json] # the Tenant, its checks and counts
|
|
19
|
-
nylo reset [--sessions|--sandboxes|--all] [--yes]
|
|
20
|
-
nylo access signing-keys … # the Tenant's signing keys: list, rotate, revoke
|
|
21
|
-
nylo configure # set or replace the Tenant's model provider
|
|
22
|
-
nylo env # export lines for the linked Project
|
|
23
|
-
nylo doctor sandbox [--json] # sandbox backend via the Management API
|
|
10
|
+
npx @nylorun/cli status
|
|
11
|
+
eval "$(npx @nylorun/cli env)"
|
|
24
12
|
```
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
credentials (below), or `NYLORUN_RUNTIME_URL` and `NYLORUN_SERVER_KEY` when the
|
|
28
|
-
Project has no link. An installation serves one Tenant, so nothing selects it.
|
|
29
|
-
`status`, `reset`, `access`, `configure` and `doctor` use the Management API
|
|
30
|
-
with the Project's management key, or `NYLORUN_MANAGEMENT_KEY` (with
|
|
31
|
-
`NYLORUN_RUNTIME_URL` when there is no link); `env` prints the Runtime URL and
|
|
32
|
-
the application key, for the Runtime API.
|
|
33
|
-
|
|
34
|
-
Local Tenant commands (`up`, `down`, `start`, `stop`, `logs`, `studio`) exit 2
|
|
35
|
-
naming `npx nylorun <command>`. `nylo tenant …` exits 2: `npx nylorun start`
|
|
36
|
-
creates the project's Tenant and the link, and `nylo status|reset` replace
|
|
37
|
-
`nylo tenant status|reset`. `nylo endpoints` was removed with Action endpoints
|
|
38
|
-
and exits 2. `nylorun dev` was removed: run
|
|
39
|
-
`npx nylorun start` once, then the project's own `npm run dev`
|
|
40
|
-
(`tsx watch src/main.ts`).
|
|
41
|
-
|
|
42
|
-
## `nylo status`
|
|
43
|
-
|
|
44
|
-
Reads the Management API's status (`GET /v1/tenant`, with a management key) and
|
|
45
|
-
prints the Tenant, the Runtime URL, the checks, the counts and the sandbox
|
|
46
|
-
backend. When the Tenant is not open it does not answer; `nylo status` then
|
|
47
|
-
fails and says to run `npx nylorun status`, which reports why from the
|
|
48
|
-
Runtime's container (`nylorun-operate status`).
|
|
49
|
-
|
|
50
|
-
## `nylo reset`
|
|
51
|
-
|
|
52
|
-
Clears the Tenant's sessions (the default), its sandboxes, or all its data
|
|
53
|
-
(`--all`), after draining work in flight. `--all` asks first; pass `--yes` when
|
|
54
|
-
not in a terminal. The Project link and credentials are kept.
|
|
55
|
-
|
|
56
|
-
## Project link
|
|
57
|
-
|
|
58
|
-
`npx nylorun start` writes it; `nylo` only reads it. A **Project** stores only:
|
|
59
|
-
|
|
60
|
-
- `.nylorun/link.json`: `{ format: 3, tenant, hostUrl, hostId, tenantId }`
|
|
61
|
-
(`tenant` is the local Tenant's name; `tenantId` is information)
|
|
62
|
-
- `.nylorun/credentials.json` (0600): the application key `project` and the
|
|
63
|
-
management key `project-management`, with their ids (`applicationKey`,
|
|
64
|
-
`principalId`, `managementKey`, `managementPrincipalId`)
|
|
65
|
-
- `.nylorun/.gitignore` containing `*`
|
|
66
|
-
|
|
67
|
-
A link from an older nylorun (format 0 to 2): `nylo` refuses it and says to run
|
|
68
|
-
`npx nylorun start`, which links the project again.
|
|
69
|
-
|
|
70
|
-
`createClient` in `@nylorun/agents` reads the link (or the two variables), so the
|
|
71
|
-
project's `npm run dev` and `npm start`, which save its agents, need no Nylorun tool.
|
|
72
|
-
`nylo endpoints` was removed with Action endpoints (protocol 8): the Runtime runs no code
|
|
73
|
-
of yours during a session.
|
|
14
|
+
Run nylorun's `nylo` instead:
|
|
74
15
|
|
|
75
16
|
```sh
|
|
76
|
-
|
|
77
|
-
|
|
17
|
+
npx -p nylorun nylo status
|
|
18
|
+
eval "$(npx -p nylorun nylo env)"
|
|
78
19
|
```
|
|
79
20
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| 2 | Usage error, or a moved or removed command |
|
|
87
|
-
| 6 | No Runtime at the linked or local URL |
|
|
88
|
-
| 130 / 143 | SIGINT / SIGTERM |
|
|
89
|
-
|
|
90
|
-
## Troubleshooting
|
|
91
|
-
|
|
92
|
-
| Symptom | What to do |
|
|
93
|
-
| --- | --- |
|
|
94
|
-
| No Runtime answers | `npx nylorun start`, then `npx nylorun status` |
|
|
95
|
-
| Tenant not open | `npx nylorun status` shows the cause and its `repair` |
|
|
96
|
-
| No management key | `npx nylorun start` in the project writes one; elsewhere set `NYLORUN_MANAGEMENT_KEY` |
|
|
97
|
-
| `403 key_role_mismatch` | The key belongs to the other API: a management key for the Management API, an application key for the Runtime API |
|
|
98
|
-
| Old Project link refused | `npx nylorun start` in the project |
|
|
99
|
-
| `426` from the Runtime | Upgrade the CLI, or pin a matching older set |
|
|
100
|
-
|
|
101
|
-
See [MIGRATION.md](../MIGRATION.md).
|
|
21
|
+
or add `nylorun` as a devDependency and run `nylo` there (`npx nylo` in the
|
|
22
|
+
project; outside it, `npx nylo` looks for an unrelated npm package named
|
|
23
|
+
`nylo`). The commands, their output and their exit codes are the same:
|
|
24
|
+
[nylorun/README.md](../nylorun/README.md#the-runtime-client-nylo) documents
|
|
25
|
+
them. This package depends on `nylorun` only, at the version released with it,
|
|
26
|
+
and a later release removes it. See [MIGRATION.md](../MIGRATION.md).
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,145 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Runtime client for the linked installation and its one Tenant (the Project link that
|
|
14
|
-
npx nylorun start writes, or NYLORUN_RUNTIME_URL with NYLORUN_SERVER_KEY, and
|
|
15
|
-
NYLORUN_MANAGEMENT_KEY for status, reset, access, configure and doctor):
|
|
16
|
-
status [--json] the Tenant, its checks and counts
|
|
17
|
-
reset [--sessions|--sandboxes|--all] [--yes]
|
|
18
|
-
clear the Tenant's sessions, sandboxes or all its data
|
|
19
|
-
access signing-keys list|rotate|revoke the Tenant's token signing keys (nylo access --help)
|
|
20
|
-
configure set the linked Tenant's model provider
|
|
21
|
-
env print the linked Project's NYLORUN_* variables as exports
|
|
22
|
-
doctor sandbox [--json] show which sandbox backend this Tenant's Host offers
|
|
23
|
-
|
|
24
|
-
Local Tenants are run by the nylorun package: npx nylorun start in a project creates its
|
|
25
|
-
Tenant and the Project link.`;
|
|
26
|
-
/** Local Tenant commands, which the nylorun package runs. */
|
|
27
|
-
const LOCAL_COMMANDS = new Set([
|
|
28
|
-
"up", "down", "start", "stop", "logs", "studio", "runtime", "restart", "run",
|
|
29
|
-
]);
|
|
30
|
-
const TENANT_REMOVED = `nylo tenant was removed: an installation serves one Tenant.
|
|
31
|
-
Run "npx nylorun start" in your project to create its Tenant and the Project link.
|
|
32
|
-
Then use nylo status and nylo reset on the linked installation.`;
|
|
33
|
-
const ENDPOINTS_REMOVED = `nylo endpoints was removed: the Runtime runs no code of yours during a session,
|
|
34
|
-
so there are no Action endpoints. An agent's tools are http() tools and remote MCP servers
|
|
35
|
-
(see MIGRATION.md).`;
|
|
36
|
-
function parseFlags(args, allowed = {}) {
|
|
37
|
-
const booleans = new Set();
|
|
38
|
-
const values = new Map();
|
|
39
|
-
const rest = [];
|
|
40
|
-
const booleanNames = new Set(allowed.booleans ?? []);
|
|
41
|
-
const valueNames = new Set(allowed.values ?? []);
|
|
42
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
43
|
-
const arg = args[index];
|
|
44
|
-
if (!arg.startsWith("-")) {
|
|
45
|
-
rest.push(arg);
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (arg === "--global" || arg === "--db") {
|
|
49
|
-
throw usageError(`${arg} was removed. Use the Runtime Host root (NYLORUN_HOME) and a Project link instead.`);
|
|
50
|
-
}
|
|
51
|
-
if (booleanNames.has(arg)) {
|
|
52
|
-
if (booleans.has(arg))
|
|
53
|
-
throw usageError(`${arg} may only be supplied once.`);
|
|
54
|
-
booleans.add(arg);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (valueNames.has(arg)) {
|
|
58
|
-
if (values.has(arg))
|
|
59
|
-
throw usageError(`${arg} may only be supplied once.`);
|
|
60
|
-
const value = args[++index];
|
|
61
|
-
if (!value || value.startsWith("-"))
|
|
62
|
-
throw usageError(`${arg} requires a value.`);
|
|
63
|
-
values.set(arg, value);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
throw usageError(usage);
|
|
67
|
-
}
|
|
68
|
-
return { rest, booleans, values };
|
|
69
|
-
}
|
|
70
|
-
const usageError = (message) => new CliError(message, 2);
|
|
71
|
-
async function main() {
|
|
72
|
-
const [command, ...args] = process.argv.slice(2);
|
|
73
|
-
if (!command || command === "--help" || command === "-h")
|
|
74
|
-
return void console.log(usage);
|
|
75
|
-
if (process.platform === "win32")
|
|
76
|
-
throw new CliError("Nylorun does not run on native Windows. Use WSL2: install Node 24 and Docker (Docker Desktop's WSL integration) inside your WSL distribution and run nylorun there (https://learn.microsoft.com/windows/wsl/install).", 1);
|
|
77
|
-
if (command === "tenant")
|
|
78
|
-
throw usageError(TENANT_REMOVED);
|
|
79
|
-
if (command === "status")
|
|
80
|
-
return await statusCommand(args);
|
|
81
|
-
if (command === "reset")
|
|
82
|
-
return await resetCommand(args);
|
|
83
|
-
if (command === "endpoints")
|
|
84
|
-
throw usageError(ENDPOINTS_REMOVED);
|
|
85
|
-
if (command === "access") {
|
|
86
|
-
if (args[0] === "--help" || args[0] === "-h" || args.length === 0) {
|
|
87
|
-
const { accessUsage } = await import("./access/commands.js");
|
|
88
|
-
return void console.log(accessUsage);
|
|
89
|
-
}
|
|
90
|
-
return await accessCommand(args);
|
|
91
|
-
}
|
|
92
|
-
if (command === "env") {
|
|
93
|
-
if (args.length)
|
|
94
|
-
throw usageError("Usage: nylo env");
|
|
95
|
-
await printLinkedEnvExports(findProjectRoot() ?? process.cwd());
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (command === "doctor") {
|
|
99
|
-
const [topic, ...options] = args;
|
|
100
|
-
if (topic !== "sandbox" || options.some((option) => option !== "--json"))
|
|
101
|
-
throw usageError("Usage: nylo doctor sandbox [--json]. Check the local Tenant with npx nylorun doctor.");
|
|
102
|
-
const { doctorSandbox } = await import("./doctor.js");
|
|
103
|
-
await doctorSandbox({ json: options.includes("--json") });
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (command === "dev")
|
|
107
|
-
throw usageError('nylorun dev was removed: run "npx nylorun start" once in your project, then your project\'s npm run dev.');
|
|
108
|
-
if (LOCAL_COMMANDS.has(command))
|
|
109
|
-
throw usageError(`Local Tenants are run by the nylorun package: npx nylorun ${command}`);
|
|
110
|
-
if (command === "configure") {
|
|
111
|
-
const flags = parseFlags(args);
|
|
112
|
-
if (flags.rest.length)
|
|
113
|
-
throw usageError(usage);
|
|
114
|
-
const projectRoot = findProjectRoot() ?? process.cwd();
|
|
115
|
-
const auth = await linkedConnection(projectRoot);
|
|
116
|
-
const admin = managementClient(auth);
|
|
117
|
-
const controller = new AbortController();
|
|
118
|
-
const cancel = (signal) => controller.abort(new ConfigurationCancelled(signal));
|
|
119
|
-
process.once("SIGINT", () => cancel("SIGINT"));
|
|
120
|
-
process.once("SIGTERM", () => cancel("SIGTERM"));
|
|
121
|
-
const health = await fetch(`${auth.url}/health`).catch(() => undefined);
|
|
122
|
-
if (!health?.ok)
|
|
123
|
-
throw new CliError(`No Runtime is listening at ${auth.url}. Start the local Tenant with "npx nylorun start".`, 6);
|
|
124
|
-
const catalog = await fetchModelCatalog(admin);
|
|
125
|
-
const prompted = await configureProvider({
|
|
126
|
-
signal: controller.signal,
|
|
127
|
-
catalog,
|
|
128
|
-
});
|
|
129
|
-
await putHostModel(admin, prompted);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
throw usageError(usage);
|
|
133
|
-
}
|
|
134
|
-
async function finish(code) {
|
|
135
|
-
process.exitCode = code;
|
|
136
|
-
for (const stream of [process.stdout, process.stderr])
|
|
137
|
-
await new Promise((resolve) => stream.write("", () => resolve()));
|
|
138
|
-
process.exit(code);
|
|
139
|
-
}
|
|
140
|
-
void main().then(() => finish(process.exitCode === undefined ? 0 : Number(process.exitCode)), (error) => {
|
|
141
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
142
|
-
return finish(error instanceof ConfigurationCancelled || error instanceof CliError
|
|
143
|
-
? error.exitCode
|
|
144
|
-
: 1);
|
|
145
|
-
});
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
// @nylorun/cli is deprecated: `nylo` ships in nylorun. This runs nylorun's `nylo` with the same
|
|
6
|
+
// arguments, after one line on stderr, so `eval "$(npx @nylorun/cli env)"` keeps a clean stdout.
|
|
7
|
+
process.stderr.write("@nylorun/cli is deprecated: nylo ships in nylorun. Use npx -p nylorun nylo, or install nylorun.\n");
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const manifest = require.resolve("nylorun/package.json");
|
|
10
|
+
const { bin } = require(manifest);
|
|
11
|
+
await import(pathToFileURL(join(dirname(manifest), bin.nylo)).href);
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0-beta",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
-
"description": "
|
|
6
|
+
"description": "Deprecated: nylo ships in the nylorun package. This package runs nylorun's nylo for one more release.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/nylorun/agents.git",
|
|
@@ -30,9 +30,7 @@
|
|
|
30
30
|
"prepack": "npm run build"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@nylorun/admin": "0.14.0-beta",
|
|
35
|
-
"@earendil-works/pi-ai": "0.99.1"
|
|
33
|
+
"nylorun": "0.13.0-beta"
|
|
36
34
|
},
|
|
37
35
|
"devDependencies": {
|
|
38
36
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `nylo access`: the linked Tenant's signing keys, which sign capability links and run tokens,
|
|
3
|
-
* through the Management API with the Project's management key. (Subject tokens, the
|
|
4
|
-
* access policy, browser keys and revocations left the Runtime in protocol 7.)
|
|
5
|
-
*/
|
|
6
|
-
import type { ManagementClient } from "@nylorun/admin";
|
|
7
|
-
export declare const accessUsage = "nylo access signing-keys <list|rotate|revoke>\n\n signing-keys list list the signing keys\n signing-keys rotate [--force] rotate; --force ends outstanding links and run tokens\n signing-keys revoke <kid> revoke a previous or standby key";
|
|
8
|
-
export declare function accessCommand(input: readonly string[], client?: () => Promise<ManagementClient>): Promise<void>;
|
package/dist/access/commands.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { findProjectRoot } from "@nylorun/admin/project";
|
|
2
|
-
import { CliError } from "../errors.js";
|
|
3
|
-
import { linkedConnection, managementClient } from "../project/connection.js";
|
|
4
|
-
export const accessUsage = `nylo access signing-keys <list|rotate|revoke>
|
|
5
|
-
|
|
6
|
-
signing-keys list list the signing keys
|
|
7
|
-
signing-keys rotate [--force] rotate; --force ends outstanding links and run tokens
|
|
8
|
-
signing-keys revoke <kid> revoke a previous or standby key`;
|
|
9
|
-
const usageError = (message = accessUsage) => new CliError(message, 2);
|
|
10
|
-
async function linkedClient() {
|
|
11
|
-
return managementClient(await linkedConnection(findProjectRoot() ?? process.cwd()));
|
|
12
|
-
}
|
|
13
|
-
const print = (value) => console.log(JSON.stringify(value, null, 2));
|
|
14
|
-
export async function accessCommand(input, client = linkedClient) {
|
|
15
|
-
const [topic, action, ...args] = input;
|
|
16
|
-
if (topic === "signing-keys") {
|
|
17
|
-
const keys = async () => (await client()).signingKeys;
|
|
18
|
-
if (action === "list" && args.length === 0)
|
|
19
|
-
return print(await (await keys()).list());
|
|
20
|
-
if (action === "rotate") {
|
|
21
|
-
const force = args[0] === "--force";
|
|
22
|
-
if (args.length > (force ? 1 : 0))
|
|
23
|
-
throw usageError();
|
|
24
|
-
return print(await (await keys()).rotate({ force }));
|
|
25
|
-
}
|
|
26
|
-
if (action === "revoke" && args.length === 1)
|
|
27
|
-
return print(await (await keys()).revoke(args[0]));
|
|
28
|
-
throw usageError();
|
|
29
|
-
}
|
|
30
|
-
if (topic === "policy" || topic === "keys" || topic === "revoke" || topic === "token")
|
|
31
|
-
throw usageError(`nylo access ${topic} was removed: the Runtime no longer mints subject tokens or keeps an access policy, browser keys or revocations (protocol 7). Trust your identity provider's tokens with an identity file, and give servers an application key (nylorun key put <id>).`);
|
|
32
|
-
throw usageError();
|
|
33
|
-
}
|
package/dist/baseline.d.ts
DELETED
package/dist/baseline.js
DELETED
package/dist/doctor.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export type SandboxReport = {
|
|
2
|
-
preference: string;
|
|
3
|
-
backend: string | null;
|
|
4
|
-
isolation?: string;
|
|
5
|
-
reason?: string;
|
|
6
|
-
probes: {
|
|
7
|
-
name: string;
|
|
8
|
-
isolation: string;
|
|
9
|
-
available: boolean;
|
|
10
|
-
reason?: string;
|
|
11
|
-
version?: string;
|
|
12
|
-
}[];
|
|
13
|
-
defaultImage?: string;
|
|
14
|
-
/** The Tenant's sandbox configuration (Runtimes with Sandboxes v3). */
|
|
15
|
-
config?: {
|
|
16
|
-
default: "none" | "virtual" | Record<string, unknown>;
|
|
17
|
-
limits?: {
|
|
18
|
-
network?: readonly string[];
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
/** `nylo doctor sandbox`: Tenant sandbox report via the Management API (F2-4). */
|
|
23
|
-
export declare function doctorSandbox(options: {
|
|
24
|
-
json: boolean;
|
|
25
|
-
}): Promise<void>;
|
package/dist/doctor.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { release } from "node:os";
|
|
2
|
-
import { findProjectRoot } from "@nylorun/admin/project";
|
|
3
|
-
import { CliError } from "./errors.js";
|
|
4
|
-
import { linkedConnection, managementClient } from "./project/connection.js";
|
|
5
|
-
function platformLine() {
|
|
6
|
-
const os = process.platform === "darwin"
|
|
7
|
-
? "macOS"
|
|
8
|
-
: process.env.WSL_DISTRO_NAME
|
|
9
|
-
? `Linux (WSL: ${process.env.WSL_DISTRO_NAME})`
|
|
10
|
-
: process.platform;
|
|
11
|
-
return `${os} (kernel ${release()}) · ${process.arch}`;
|
|
12
|
-
}
|
|
13
|
-
/** The Tenant's sandbox report (`GET /v1/tenant/sandbox`, Management API). */
|
|
14
|
-
async function fetchSandboxReport() {
|
|
15
|
-
const connection = await linkedConnection(findProjectRoot() ?? process.cwd());
|
|
16
|
-
return (await managementClient(connection).settings.sandbox.get());
|
|
17
|
-
}
|
|
18
|
-
/** `nylo doctor sandbox`: Tenant sandbox report via the Management API (F2-4). */
|
|
19
|
-
export async function doctorSandbox(options) {
|
|
20
|
-
let report;
|
|
21
|
-
try {
|
|
22
|
-
report = await fetchSandboxReport();
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
throw new CliError(error instanceof Error
|
|
26
|
-
? error.message
|
|
27
|
-
: `Could not read Tenant sandbox status: ${String(error)}`, 1);
|
|
28
|
-
}
|
|
29
|
-
if (options.json) {
|
|
30
|
-
console.log(JSON.stringify({ platform: platformLine(), ...report }, null, 2));
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const rows = [["platform", platformLine()]];
|
|
34
|
-
for (const probe of report.probes)
|
|
35
|
-
rows.push([
|
|
36
|
-
probe.name,
|
|
37
|
-
`${probe.available ? "✓" : "✗"} ${probe.reason ?? ""}${probe.version ? ` · ${probe.version}` : ""}`,
|
|
38
|
-
]);
|
|
39
|
-
rows.push([
|
|
40
|
-
"preference",
|
|
41
|
-
report.preference === "auto"
|
|
42
|
-
? "auto (npx nylorun start seeds the Tenant's sandbox.backend from .env NYLORUN_SANDBOX)"
|
|
43
|
-
: report.preference,
|
|
44
|
-
]);
|
|
45
|
-
if (report.config)
|
|
46
|
-
rows.push([
|
|
47
|
-
"default",
|
|
48
|
-
report.config.default === "none"
|
|
49
|
-
? "none: sessions get a sandbox only when opened with one (set a default with PUT /v1/tenant/sandbox)"
|
|
50
|
-
: report.config.default === "virtual"
|
|
51
|
-
? "virtual: sessions that name no sandbox get one"
|
|
52
|
-
: "a Tenant sandbox: sessions that name no sandbox get it",
|
|
53
|
-
]);
|
|
54
|
-
rows.push([
|
|
55
|
-
"selected",
|
|
56
|
-
report.backend
|
|
57
|
-
? `${report.backend} (${report.isolation} isolation)`
|
|
58
|
-
: `none: ${report.reason}`,
|
|
59
|
-
]);
|
|
60
|
-
const width = Math.max(...rows.map(([key]) => key.length)) + 2;
|
|
61
|
-
for (const [key, value] of rows)
|
|
62
|
-
console.log(` ${key.padEnd(width)}${value}`);
|
|
63
|
-
if (report.backend === "virtual")
|
|
64
|
-
console.log("\n The virtual shell emulates bash in the Runtime process; it is not a VM boundary.");
|
|
65
|
-
}
|
package/dist/environment.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read the Project `.env` into a value map. Does not mutate `process.env` (F3).
|
|
3
|
-
*/
|
|
4
|
-
export declare function loadProjectEnvironment(root?: string): Record<string, string>;
|
|
5
|
-
export declare function saveEnvironment(root: string, updates: Record<string, string | undefined>, signal?: AbortSignal): Promise<void>;
|
package/dist/environment.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { writeFile, rename, rm } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { parseEnv } from "node:util";
|
|
6
|
-
/**
|
|
7
|
-
* Read the Project `.env` into a value map. Does not mutate `process.env` (F3).
|
|
8
|
-
*/
|
|
9
|
-
export function loadProjectEnvironment(root = process.cwd()) {
|
|
10
|
-
const file = join(root, ".env");
|
|
11
|
-
try {
|
|
12
|
-
if (statSync(file).isDirectory())
|
|
13
|
-
throw new Error("The .env directory must be migrated manually: back it up, create a .env file with MODEL_PROVIDER, MODEL and MODEL_PROVIDER_API_KEY, and move OAuth credentials to .nylorun/auth.json. See the Runtime migration guide.");
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
if (error.code === "ENOENT")
|
|
17
|
-
return {};
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
const parsed = parseEnv(readFileSync(file, "utf8"));
|
|
21
|
-
const result = {};
|
|
22
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
23
|
-
if (value !== undefined)
|
|
24
|
-
result[key] = value;
|
|
25
|
-
}
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
|
-
// Match complete dotenv assignments, including quoted multiline values.
|
|
29
|
-
const assignment = /^(?:export\s+)?([\w]+)[\t ]*=[\t ]*(?:"[^"]*"|'[^']*'|`[^`]*`|[^#\r\n]*)([^\r\n]*)(?:\r?\n|$)/gm;
|
|
30
|
-
export async function saveEnvironment(root, updates, signal) {
|
|
31
|
-
const file = join(root, ".env");
|
|
32
|
-
let contents = "";
|
|
33
|
-
try {
|
|
34
|
-
contents = readFileSync(file, "utf8");
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
if (error.code !== "ENOENT")
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
const encode = (value) => {
|
|
41
|
-
// Node's dotenv parser has no general quote-escaping syntax. Select a
|
|
42
|
-
// delimiter absent from the value rather than changing the credential.
|
|
43
|
-
for (const quote of ["'", '"', "`"]) {
|
|
44
|
-
if (!value.includes(quote) && !(quote === '"' && /\\[nr]/.test(value)))
|
|
45
|
-
return quote + value + quote;
|
|
46
|
-
}
|
|
47
|
-
throw new Error("This value contains all dotenv quote delimiters; set it through your process environment instead.");
|
|
48
|
-
};
|
|
49
|
-
const remaining = new Set(Object.keys(updates));
|
|
50
|
-
contents = contents.replace(assignment, (whole, key, suffix) => {
|
|
51
|
-
if (!(key in updates))
|
|
52
|
-
return whole;
|
|
53
|
-
if (!remaining.delete(key))
|
|
54
|
-
return "";
|
|
55
|
-
return updates[key] === undefined
|
|
56
|
-
? ""
|
|
57
|
-
: `${key}=${encode(updates[key])}${suffix}\n`;
|
|
58
|
-
});
|
|
59
|
-
if (contents && !contents.endsWith("\n"))
|
|
60
|
-
contents += "\n";
|
|
61
|
-
for (const key of remaining)
|
|
62
|
-
if (updates[key] !== undefined)
|
|
63
|
-
contents += `${key}=${encode(updates[key])}\n`;
|
|
64
|
-
const temporary = join(root, `.env-${randomUUID()}.tmp`);
|
|
65
|
-
try {
|
|
66
|
-
await writeFile(temporary, contents, { mode: 0o600, signal });
|
|
67
|
-
signal?.throwIfAborted();
|
|
68
|
-
await rename(temporary, file);
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
await rm(temporary, { force: true });
|
|
72
|
-
}
|
|
73
|
-
}
|
package/dist/errors.d.ts
DELETED
package/dist/errors.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `nylo status [--json]`: the Tenant, its checks and counts. A Tenant that is not open does not
|
|
3
|
-
* answer: `npx nylorun status` reports why.
|
|
4
|
-
*/
|
|
5
|
-
export declare function statusCommand(args: readonly string[]): Promise<void>;
|
|
6
|
-
/** `nylo reset [--sessions|--sandboxes|--all] [--yes]`; `--all` asks first. */
|
|
7
|
-
export declare function resetCommand(args: readonly string[]): Promise<void>;
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `nylo status` and `nylo reset`: the linked installation's one Tenant, through the Management
|
|
3
|
-
* API (the Project's management key).
|
|
4
|
-
*/
|
|
5
|
-
import { createInterface } from "node:readline/promises";
|
|
6
|
-
import { findProjectRoot } from "@nylorun/admin/project";
|
|
7
|
-
import { CliError } from "../errors.js";
|
|
8
|
-
import { linkedConnection, managementClient } from "../project/connection.js";
|
|
9
|
-
const projectRoot = () => findProjectRoot() ?? process.cwd();
|
|
10
|
-
/**
|
|
11
|
-
* `nylo status [--json]`: the Tenant, its checks and counts. A Tenant that is not open does not
|
|
12
|
-
* answer: `npx nylorun status` reports why.
|
|
13
|
-
*/
|
|
14
|
-
export async function statusCommand(args) {
|
|
15
|
-
const json = args.includes("--json");
|
|
16
|
-
if (args.some((a) => a !== "--json"))
|
|
17
|
-
throw new CliError("Usage: nylo status [--json]", 2);
|
|
18
|
-
const root = projectRoot();
|
|
19
|
-
const connection = await linkedConnection(root);
|
|
20
|
-
const admin = managementClient(connection);
|
|
21
|
-
let body;
|
|
22
|
-
try {
|
|
23
|
-
body = await admin.tenant.status();
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
throw new CliError(`${(error instanceof Error ? error.message : `The Tenant at ${connection.url} is not reachable`).replace(/\.?$/, ".")} Run "npx nylorun status" to see the Tenant's state and why it is not open.`, 1);
|
|
27
|
-
}
|
|
28
|
-
if (json) {
|
|
29
|
-
console.log(JSON.stringify(body, null, 2));
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
console.log(`${body.tenant.name} ${body.tenant.id}`);
|
|
33
|
-
console.log(`runtime ${connection.url}`);
|
|
34
|
-
console.log(`path ${body.path}`);
|
|
35
|
-
console.log(`checks ${Object.entries(body.checks)
|
|
36
|
-
.map(([k, v]) => `${k}=${v ? "ok" : "fail"}`)
|
|
37
|
-
.join(" ")}`);
|
|
38
|
-
console.log(`counts sessions=${body.counts.sessions} running=${body.counts.runningSessions} uncertain=${body.counts.uncertainEffects}`);
|
|
39
|
-
console.log(`sandbox ${body.sandbox.backend ?? "none"}`);
|
|
40
|
-
}
|
|
41
|
-
const RESET_USAGE = "Usage: nylo reset [--sessions|--sandboxes|--all] [--yes]";
|
|
42
|
-
const SCOPES = { "--sessions": "sessions", "--sandboxes": "sandboxes", "--all": "all" };
|
|
43
|
-
/** `nylo reset [--sessions|--sandboxes|--all] [--yes]`; `--all` asks first. */
|
|
44
|
-
export async function resetCommand(args) {
|
|
45
|
-
const yes = args.includes("--yes");
|
|
46
|
-
const flags = args.filter((a) => a !== "--yes");
|
|
47
|
-
if (flags.some((f) => !(f in SCOPES)) || args.filter((a) => a === "--yes").length > 1)
|
|
48
|
-
throw new CliError(RESET_USAGE, 2);
|
|
49
|
-
if (flags.length > 1)
|
|
50
|
-
throw new CliError("Pass only one of --sessions, --sandboxes, --all.", 2);
|
|
51
|
-
const scope = flags.length ? SCOPES[flags[0]] : "sessions";
|
|
52
|
-
const connection = await linkedConnection(projectRoot());
|
|
53
|
-
const tenant = connection.link?.tenant;
|
|
54
|
-
const target = tenant ? `Tenant ${tenant} (${connection.url})` : `the Tenant on ${connection.url}`;
|
|
55
|
-
const admin = managementClient(connection);
|
|
56
|
-
if (scope === "all" && !yes)
|
|
57
|
-
await confirmOrThrow(`Reset ALL data of ${target}? The Project link and credentials are kept. [y/N] `);
|
|
58
|
-
try {
|
|
59
|
-
await admin.tenant.reset({ scope, activeWork: "drain" });
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
throw new CliError(error instanceof Error ? error.message : `Tenant reset failed: ${String(error)}`, 1);
|
|
63
|
-
}
|
|
64
|
-
console.log(`Reset ${target} (${scope}).`);
|
|
65
|
-
}
|
|
66
|
-
async function confirmOrThrow(prompt) {
|
|
67
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
68
|
-
throw new CliError("Confirmation required; pass --yes for non-interactive use.", 2);
|
|
69
|
-
}
|
|
70
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
71
|
-
try {
|
|
72
|
-
const answer = (await rl.question(prompt)).trim().toLowerCase();
|
|
73
|
-
if (answer !== "y" && answer !== "yes") {
|
|
74
|
-
throw new CliError("Cancelled.", 1);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
rl.close();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { Readable, Writable } from "node:stream";
|
|
2
|
-
import type { Credential } from "@earendil-works/pi-ai";
|
|
3
|
-
import type { ManagementClient } from "@nylorun/admin";
|
|
4
|
-
export declare class ConfigurationCancelled extends Error {
|
|
5
|
-
readonly signal: "SIGINT" | "SIGTERM";
|
|
6
|
-
readonly exitCode: number;
|
|
7
|
-
constructor(signal: "SIGINT" | "SIGTERM");
|
|
8
|
-
}
|
|
9
|
-
export type PromptedModel = {
|
|
10
|
-
provider: string;
|
|
11
|
-
model: string;
|
|
12
|
-
baseUrl?: string;
|
|
13
|
-
/** A custom endpoint's context window and output limit, when given. */
|
|
14
|
-
settings?: {
|
|
15
|
-
contextWindow?: number;
|
|
16
|
-
maxTokens?: number;
|
|
17
|
-
};
|
|
18
|
-
auth: Credential;
|
|
19
|
-
};
|
|
20
|
-
export type ModelCatalog = {
|
|
21
|
-
providers: {
|
|
22
|
-
id: string;
|
|
23
|
-
name: string;
|
|
24
|
-
models: {
|
|
25
|
-
id: string;
|
|
26
|
-
name: string;
|
|
27
|
-
}[];
|
|
28
|
-
auth?: {
|
|
29
|
-
apiKey?: unknown;
|
|
30
|
-
oauth?: unknown;
|
|
31
|
-
};
|
|
32
|
-
}[];
|
|
33
|
-
};
|
|
34
|
-
export type Selection = Readonly<{
|
|
35
|
-
provider: string;
|
|
36
|
-
model: string;
|
|
37
|
-
custom?: Readonly<{
|
|
38
|
-
baseUrl: string;
|
|
39
|
-
}>;
|
|
40
|
-
}>;
|
|
41
|
-
/** The Tenant's model catalog (`GET /v1/tenant/models`, Management API). */
|
|
42
|
-
export declare function fetchModelCatalog(admin: ManagementClient): Promise<ModelCatalog>;
|
|
43
|
-
export declare function configureProvider(options?: {
|
|
44
|
-
signal?: AbortSignal;
|
|
45
|
-
root?: string;
|
|
46
|
-
input?: Readable;
|
|
47
|
-
output?: Writable;
|
|
48
|
-
/** Tenant API catalog from GET /v1/tenant/models. */
|
|
49
|
-
catalog?: ModelCatalog;
|
|
50
|
-
/** This installation's stable id, for OAuth flows that need one (OpenAI). */
|
|
51
|
-
deviceId?: () => string;
|
|
52
|
-
}): Promise<PromptedModel>;
|
|
53
|
-
/**
|
|
54
|
-
* A stable id for this CLI installation, created on first use under the Host root.
|
|
55
|
-
* OpenAI's "Sign in with ChatGPT" sends it as the agent host id.
|
|
56
|
-
*/
|
|
57
|
-
export declare function installationId(home?: string): string;
|
package/dist/model/configure.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import { createInterface } from "node:readline/promises";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
|
|
6
|
-
import { createProvider, } from "@earendil-works/pi-ai";
|
|
7
|
-
import { stream, streamSimple, } from "@earendil-works/pi-ai/api/openai-completions";
|
|
8
|
-
import { nylorunHome } from "@nylorun/admin/project";
|
|
9
|
-
export class ConfigurationCancelled extends Error {
|
|
10
|
-
signal;
|
|
11
|
-
exitCode;
|
|
12
|
-
constructor(signal) {
|
|
13
|
-
super(`Provider configuration cancelled (${signal}).`);
|
|
14
|
-
this.signal = signal;
|
|
15
|
-
this.exitCode = signal === "SIGINT" ? 130 : 143;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Build a pi-ai registry for interactive login. Catalog listing prefers the
|
|
20
|
-
* Tenant API payload when provided (F2-4).
|
|
21
|
-
*/
|
|
22
|
-
function modelsFor(selection, credentials, catalog) {
|
|
23
|
-
const models = builtinModels({ credentials });
|
|
24
|
-
if (selection.custom) {
|
|
25
|
-
const model = {
|
|
26
|
-
id: selection.model,
|
|
27
|
-
name: selection.model,
|
|
28
|
-
api: "openai-completions",
|
|
29
|
-
provider: "custom",
|
|
30
|
-
baseUrl: selection.custom.baseUrl,
|
|
31
|
-
reasoning: false,
|
|
32
|
-
input: ["text", "image"],
|
|
33
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
34
|
-
contextWindow: 128000,
|
|
35
|
-
maxTokens: 16384,
|
|
36
|
-
};
|
|
37
|
-
models.setProvider(createProvider({
|
|
38
|
-
id: "custom",
|
|
39
|
-
name: "Custom OpenAI-compatible",
|
|
40
|
-
baseUrl: selection.custom.baseUrl,
|
|
41
|
-
auth: {
|
|
42
|
-
apiKey: {
|
|
43
|
-
name: "Custom API key",
|
|
44
|
-
async login(interaction) {
|
|
45
|
-
const key = await interaction.prompt({
|
|
46
|
-
type: "secret",
|
|
47
|
-
message: "API key",
|
|
48
|
-
});
|
|
49
|
-
return { type: "api_key", key: key.trim() };
|
|
50
|
-
},
|
|
51
|
-
async resolve() {
|
|
52
|
-
const credential = await credentials.read("custom");
|
|
53
|
-
if (!credential || credential.type !== "api_key")
|
|
54
|
-
return undefined;
|
|
55
|
-
const key = credential.key;
|
|
56
|
-
if (!key)
|
|
57
|
-
return undefined;
|
|
58
|
-
return { type: "api_key", auth: { apiKey: key } };
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
models: [model],
|
|
63
|
-
api: { stream, streamSimple },
|
|
64
|
-
}));
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
models,
|
|
68
|
-
listProviders() {
|
|
69
|
-
if (catalog?.providers.length) {
|
|
70
|
-
return catalog.providers.map((provider) => {
|
|
71
|
-
const live = models.getProvider(provider.id);
|
|
72
|
-
return {
|
|
73
|
-
id: provider.id,
|
|
74
|
-
name: provider.name,
|
|
75
|
-
auth: {
|
|
76
|
-
...(live?.auth?.apiKey || provider.auth?.apiKey
|
|
77
|
-
? { apiKey: live?.auth?.apiKey ?? {} }
|
|
78
|
-
: {}),
|
|
79
|
-
...(live?.auth?.oauth || provider.auth?.oauth
|
|
80
|
-
? { oauth: live?.auth?.oauth ?? {} }
|
|
81
|
-
: {}),
|
|
82
|
-
...(!live?.auth?.apiKey &&
|
|
83
|
-
!live?.auth?.oauth &&
|
|
84
|
-
!provider.auth?.apiKey &&
|
|
85
|
-
!provider.auth?.oauth
|
|
86
|
-
? { apiKey: {} }
|
|
87
|
-
: {}),
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
return models.getProviders().map((provider) => ({
|
|
93
|
-
id: provider.id,
|
|
94
|
-
name: provider.name,
|
|
95
|
-
auth: {
|
|
96
|
-
...(provider.auth?.apiKey ? { apiKey: provider.auth.apiKey } : {}),
|
|
97
|
-
...(provider.auth?.oauth ? { oauth: provider.auth.oauth } : {}),
|
|
98
|
-
},
|
|
99
|
-
}));
|
|
100
|
-
},
|
|
101
|
-
listModels(providerId) {
|
|
102
|
-
const fromCatalog = catalog?.providers.find((p) => p.id === providerId);
|
|
103
|
-
if (fromCatalog?.models.length)
|
|
104
|
-
return fromCatalog.models;
|
|
105
|
-
return models.getModels(providerId).map((model) => ({
|
|
106
|
-
id: model.id,
|
|
107
|
-
name: model.name,
|
|
108
|
-
}));
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
/** The Tenant's model catalog (`GET /v1/tenant/models`, Management API). */
|
|
113
|
-
export async function fetchModelCatalog(admin) {
|
|
114
|
-
return await admin.models.catalog();
|
|
115
|
-
}
|
|
116
|
-
// Internal options also allow isolated prompt tests without changing process globals.
|
|
117
|
-
export async function configureProvider(options = {}) {
|
|
118
|
-
const login = { getDeviceId: options.deviceId ?? installationId };
|
|
119
|
-
const output = options.output ?? process.stdout;
|
|
120
|
-
const controller = new AbortController();
|
|
121
|
-
const signal = controller.signal;
|
|
122
|
-
const forwardAbort = () => controller.abort(options.signal.reason);
|
|
123
|
-
options.signal?.throwIfAborted();
|
|
124
|
-
let captured;
|
|
125
|
-
const store = {
|
|
126
|
-
async read() {
|
|
127
|
-
return undefined;
|
|
128
|
-
},
|
|
129
|
-
async list() {
|
|
130
|
-
return [];
|
|
131
|
-
},
|
|
132
|
-
async delete() { },
|
|
133
|
-
async modify(_id, fn) {
|
|
134
|
-
const next = await fn(captured);
|
|
135
|
-
if (next?.type === "api_key" && !next.key)
|
|
136
|
-
throw new Error("An API key is required.");
|
|
137
|
-
if (next)
|
|
138
|
-
captured = next;
|
|
139
|
-
return next ?? captured;
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
const registry = modelsFor({ provider: "", model: "" }, store, options.catalog);
|
|
143
|
-
const providers = registry.listProviders();
|
|
144
|
-
const prompt = createInterface({
|
|
145
|
-
input: options.input ?? process.stdin,
|
|
146
|
-
output,
|
|
147
|
-
});
|
|
148
|
-
const onInt = () => controller.abort(new ConfigurationCancelled("SIGINT"));
|
|
149
|
-
const onClose = () => controller.abort(new Error("Configuration input closed before setup completed."));
|
|
150
|
-
const closeOnAbort = () => prompt.close();
|
|
151
|
-
prompt.on("SIGINT", onInt);
|
|
152
|
-
prompt.on("close", onClose);
|
|
153
|
-
signal.addEventListener("abort", closeOnAbort, { once: true });
|
|
154
|
-
options.signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
155
|
-
if (options.signal?.aborted)
|
|
156
|
-
forwardAbort();
|
|
157
|
-
async function question(message) {
|
|
158
|
-
signal.throwIfAborted();
|
|
159
|
-
return prompt.question(message, { signal });
|
|
160
|
-
}
|
|
161
|
-
/** A positive whole number of tokens, or undefined for the default. */
|
|
162
|
-
async function tokens(message) {
|
|
163
|
-
const answer = (await question(message)).trim();
|
|
164
|
-
if (!answer)
|
|
165
|
-
return undefined;
|
|
166
|
-
const value = Number(answer);
|
|
167
|
-
if (!Number.isInteger(value) || value < 1)
|
|
168
|
-
throw new Error(`${message.split(" (")[0]} must be a whole number of tokens.`);
|
|
169
|
-
return value;
|
|
170
|
-
}
|
|
171
|
-
try {
|
|
172
|
-
signal.throwIfAborted();
|
|
173
|
-
output.write("0. Custom OpenAI-compatible provider\n");
|
|
174
|
-
providers.forEach((provider, index) => output.write(`${index + 1}. ${provider.name} (${provider.id})\n`));
|
|
175
|
-
const choice = Number(await question("Choose a provider: "));
|
|
176
|
-
if (choice === 0) {
|
|
177
|
-
const baseUrl = (await question("OpenAI-compatible base URL: "))
|
|
178
|
-
.trim()
|
|
179
|
-
.replace(/\/$/, "");
|
|
180
|
-
const model = (await question("Model id: ")).trim();
|
|
181
|
-
if (!baseUrl || !model)
|
|
182
|
-
throw new Error("A base URL and model id are required.");
|
|
183
|
-
const contextWindow = await tokens("Context window in tokens (Enter for 32768): ");
|
|
184
|
-
const maxTokens = await tokens("Max output tokens (Enter for 8192): ");
|
|
185
|
-
const settings = {
|
|
186
|
-
...(contextWindow ? { contextWindow } : {}),
|
|
187
|
-
...(maxTokens ? { maxTokens } : {}),
|
|
188
|
-
};
|
|
189
|
-
const selection = {
|
|
190
|
-
provider: "custom",
|
|
191
|
-
model,
|
|
192
|
-
custom: { baseUrl },
|
|
193
|
-
};
|
|
194
|
-
const custom = modelsFor(selection, store, options.catalog);
|
|
195
|
-
if (!(await custom.models.checkAuth("custom", { signal })))
|
|
196
|
-
await custom.models.login("custom", "api_key", interaction(), login);
|
|
197
|
-
return {
|
|
198
|
-
...prompted(selection),
|
|
199
|
-
...(Object.keys(settings).length ? { settings } : {}),
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
const chosen = providers[choice - 1];
|
|
204
|
-
if (!chosen)
|
|
205
|
-
throw new Error("Choose a listed provider.");
|
|
206
|
-
const available = registry.listModels(chosen.id);
|
|
207
|
-
available.forEach((model, index) => output.write(`${index + 1}. ${model.name} (${model.id})\n`));
|
|
208
|
-
const model = available[Number(await question("Choose a model: ")) - 1];
|
|
209
|
-
if (!model)
|
|
210
|
-
throw new Error("Choose a listed model.");
|
|
211
|
-
if (!(await registry.models.checkAuth(chosen.id, { signal }))) {
|
|
212
|
-
let method = chosen.auth.apiKey
|
|
213
|
-
? "api_key"
|
|
214
|
-
: "oauth";
|
|
215
|
-
if (chosen.auth.apiKey && chosen.auth.oauth) {
|
|
216
|
-
const answer = (await question("Choose authentication: 1. API key (default), 2. OAuth: ")).trim();
|
|
217
|
-
if (answer && !["1", "2"].includes(answer))
|
|
218
|
-
throw new Error("Choose authentication 1 or 2.");
|
|
219
|
-
if (answer === "2")
|
|
220
|
-
method = "oauth";
|
|
221
|
-
}
|
|
222
|
-
await registry.models.login(chosen.id, method, interaction(), login);
|
|
223
|
-
}
|
|
224
|
-
return prompted({ provider: chosen.id, model: model.id });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
throw signal.aborted ? signal.reason : error;
|
|
229
|
-
}
|
|
230
|
-
finally {
|
|
231
|
-
options.signal?.removeEventListener("abort", forwardAbort);
|
|
232
|
-
signal.removeEventListener("abort", closeOnAbort);
|
|
233
|
-
prompt.removeListener("SIGINT", onInt);
|
|
234
|
-
prompt.removeListener("close", onClose);
|
|
235
|
-
prompt.close();
|
|
236
|
-
}
|
|
237
|
-
function interaction() {
|
|
238
|
-
return {
|
|
239
|
-
signal,
|
|
240
|
-
prompt: async (item) => {
|
|
241
|
-
if (item.type !== "select")
|
|
242
|
-
return question(item.message + ": ");
|
|
243
|
-
item.options.forEach((option, index) => output.write(`${index + 1}. ${option.label}\n`));
|
|
244
|
-
const answer = (await question(item.message + " ")).trim();
|
|
245
|
-
const option = item.options.find((option) => option.id === answer) ??
|
|
246
|
-
item.options[Number(answer) - 1];
|
|
247
|
-
if (!option)
|
|
248
|
-
throw new Error("Choose a listed authentication option.");
|
|
249
|
-
return option.id;
|
|
250
|
-
},
|
|
251
|
-
notify: (event) => {
|
|
252
|
-
output.write(("url" in event
|
|
253
|
-
? event.url
|
|
254
|
-
: "verificationUri" in event
|
|
255
|
-
? event.verificationUri
|
|
256
|
-
: event.message) + "\n");
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
function prompted(selection) {
|
|
261
|
-
signal.throwIfAborted();
|
|
262
|
-
if (!captured)
|
|
263
|
-
throw new Error("An API key or OAuth credential is required.");
|
|
264
|
-
output.write("Provider configuration saved.\n");
|
|
265
|
-
return {
|
|
266
|
-
provider: selection.provider,
|
|
267
|
-
model: selection.model,
|
|
268
|
-
...(selection.custom?.baseUrl
|
|
269
|
-
? { baseUrl: selection.custom.baseUrl }
|
|
270
|
-
: {}),
|
|
271
|
-
auth: captured,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* A stable id for this CLI installation, created on first use under the Host root.
|
|
277
|
-
* OpenAI's "Sign in with ChatGPT" sends it as the agent host id.
|
|
278
|
-
*/
|
|
279
|
-
export function installationId(home = nylorunHome()) {
|
|
280
|
-
const file = join(home, "cli-installation-id");
|
|
281
|
-
try {
|
|
282
|
-
const existing = readFileSync(file, "utf8").trim();
|
|
283
|
-
if (existing)
|
|
284
|
-
return existing;
|
|
285
|
-
}
|
|
286
|
-
catch {
|
|
287
|
-
/* Created below on first use. */
|
|
288
|
-
}
|
|
289
|
-
const id = randomUUID();
|
|
290
|
-
mkdirSync(home, { recursive: true });
|
|
291
|
-
writeFileSync(file, `${id}\n`, { mode: 0o600 });
|
|
292
|
-
return id;
|
|
293
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ManagementClient } from "@nylorun/admin";
|
|
2
|
-
import type { PromptedModel } from "./configure.js";
|
|
3
|
-
/** Set the installation's Tenant model provider (`PUT /v1/tenant/model`, Management API). */
|
|
4
|
-
export declare function putHostModel(admin: ManagementClient, model: PromptedModel): ReturnType<ManagementClient["models"]["put"]>;
|
package/dist/model/host-model.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
/** Set the installation's Tenant model provider (`PUT /v1/tenant/model`, Management API). */
|
|
3
|
-
export async function putHostModel(admin, model) {
|
|
4
|
-
return await admin.models.put({
|
|
5
|
-
idempotencyKey: randomUUID(),
|
|
6
|
-
provider: model.provider,
|
|
7
|
-
model: model.model,
|
|
8
|
-
...(model.baseUrl ? { baseUrl: model.baseUrl } : {}),
|
|
9
|
-
...(model.settings ? { settings: model.settings } : {}),
|
|
10
|
-
// A prompted credential is complete: pi-ai's type leaves its fields optional.
|
|
11
|
-
auth: model.auth,
|
|
12
|
-
});
|
|
13
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { type ManagementClient } from "@nylorun/admin";
|
|
2
|
-
import { type ProjectLink } from "./link.js";
|
|
3
|
-
/** The installation `nylo` acts on: a Runtime URL, an application key and a management key. */
|
|
4
|
-
export interface LinkedConnection {
|
|
5
|
-
url: string;
|
|
6
|
-
/** The application key (Runtime API); absent when only a management key is set. */
|
|
7
|
-
key?: string;
|
|
8
|
-
/** The management key (Management API), when there is one. */
|
|
9
|
-
managementKey?: string;
|
|
10
|
-
/** The Project link it came from, when it came from one. */
|
|
11
|
-
link?: ProjectLink;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* The Project's link and credentials, else `NYLORUN_RUNTIME_URL` with `NYLORUN_SERVER_KEY`, or
|
|
15
|
-
* `NYLORUN_MANAGEMENT_KEY`, or both; `NYLORUN_MANAGEMENT_KEY` also stands in for a Project
|
|
16
|
-
* credentials file without one. The installation serves one Tenant, so nothing names it.
|
|
17
|
-
*/
|
|
18
|
-
export declare function linkedConnection(projectRoot: string, env?: Readonly<Record<string, string | undefined>>): Promise<LinkedConnection>;
|
|
19
|
-
/** The Management API client of `connection`; refused without a management key. */
|
|
20
|
-
export declare function managementClient(connection: LinkedConnection): ManagementClient;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { createManagementClient } from "@nylorun/admin";
|
|
2
|
-
import { CliError } from "../errors.js";
|
|
3
|
-
import { readCredentials } from "./credentials.js";
|
|
4
|
-
import { readLink } from "./link.js";
|
|
5
|
-
/**
|
|
6
|
-
* The Project's link and credentials, else `NYLORUN_RUNTIME_URL` with `NYLORUN_SERVER_KEY`, or
|
|
7
|
-
* `NYLORUN_MANAGEMENT_KEY`, or both; `NYLORUN_MANAGEMENT_KEY` also stands in for a Project
|
|
8
|
-
* credentials file without one. The installation serves one Tenant, so nothing names it.
|
|
9
|
-
*/
|
|
10
|
-
export async function linkedConnection(projectRoot, env = process.env) {
|
|
11
|
-
const link = await readLink(projectRoot);
|
|
12
|
-
const credentials = await readCredentials(projectRoot);
|
|
13
|
-
const managementKey = env.NYLORUN_MANAGEMENT_KEY?.trim();
|
|
14
|
-
if (link && credentials) {
|
|
15
|
-
const management = credentials.managementKey ?? managementKey;
|
|
16
|
-
return {
|
|
17
|
-
url: link.hostUrl,
|
|
18
|
-
key: credentials.applicationKey,
|
|
19
|
-
...(management ? { managementKey: management } : {}),
|
|
20
|
-
link,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
const url = env.NYLORUN_RUNTIME_URL?.trim();
|
|
24
|
-
const key = env.NYLORUN_SERVER_KEY?.trim();
|
|
25
|
-
if (url && (key || managementKey))
|
|
26
|
-
return {
|
|
27
|
-
url: url.replace(/\/$/, ""),
|
|
28
|
-
...(key ? { key } : {}),
|
|
29
|
-
...(managementKey ? { managementKey } : {}),
|
|
30
|
-
};
|
|
31
|
-
throw new CliError(`No Project link in ${projectRoot}. Run "npx nylorun start" in this project to start its Tenant and link it, or set NYLORUN_RUNTIME_URL and NYLORUN_SERVER_KEY (or NYLORUN_MANAGEMENT_KEY).`, 1);
|
|
32
|
-
}
|
|
33
|
-
/** The Management API client of `connection`; refused without a management key. */
|
|
34
|
-
export function managementClient(connection) {
|
|
35
|
-
if (!connection.managementKey)
|
|
36
|
-
throw new CliError(`No management key for the Tenant at ${connection.url}: run npx nylorun start, or set NYLORUN_MANAGEMENT_KEY.`, 1);
|
|
37
|
-
return createManagementClient({ url: connection.url, key: connection.managementKey });
|
|
38
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type ProjectCredentials } from "@nylorun/admin/project";
|
|
2
|
-
/**
|
|
3
|
-
* Project-local credentials (mode 0600): the application key and its principal, and the
|
|
4
|
-
* management key and its principal when `nylorun start` issued one. A format 0 file may still
|
|
5
|
-
* carry a legacy `executors` map from before protocol 3; it is ignored.
|
|
6
|
-
*/
|
|
7
|
-
export type { ProjectCredentials };
|
|
8
|
-
/** The Project's credentials, which `nylorun start` writes beside the link; kept 0600. */
|
|
9
|
-
export declare function readCredentials(projectRoot: string): Promise<ProjectCredentials | undefined>;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { chmod } from "node:fs/promises";
|
|
2
|
-
import { ProjectFileError, projectCredentialsPath, readProjectCredentials, } from "@nylorun/admin/project";
|
|
3
|
-
import { CliError } from "../errors.js";
|
|
4
|
-
/** The Project's credentials, which `nylorun start` writes beside the link; kept 0600. */
|
|
5
|
-
export async function readCredentials(projectRoot) {
|
|
6
|
-
const path = projectCredentialsPath(projectRoot);
|
|
7
|
-
let credentials;
|
|
8
|
-
try {
|
|
9
|
-
credentials = readProjectCredentials(projectRoot);
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
if (!(error instanceof ProjectFileError))
|
|
13
|
-
throw error;
|
|
14
|
-
throw new CliError(error.reason === "newer"
|
|
15
|
-
? `Project credentials at ${path} have an unsupported format. Upgrade the CLI.`
|
|
16
|
-
: `Invalid Project credentials at ${path}. Remove .nylorun/credentials.json and run "npx nylorun start".`, 1);
|
|
17
|
-
}
|
|
18
|
-
if (credentials)
|
|
19
|
-
await chmod(path, 0o600);
|
|
20
|
-
return credentials;
|
|
21
|
-
}
|
package/dist/project/env.d.ts
DELETED
package/dist/project/env.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { findProjectRoot } from "@nylorun/admin/project";
|
|
2
|
-
import { readCredentials } from "./credentials.js";
|
|
3
|
-
import { readLink } from "./link.js";
|
|
4
|
-
/** Two `export` lines for `nylo env` (was `nylorun status --env`, F2-7). */
|
|
5
|
-
export async function printLinkedEnvExports(projectRoot = process.cwd()) {
|
|
6
|
-
const root = findProjectRoot(projectRoot) ?? projectRoot;
|
|
7
|
-
const link = await readLink(root);
|
|
8
|
-
const credentials = await readCredentials(root);
|
|
9
|
-
if (!link || !credentials) {
|
|
10
|
-
console.log('# No Project link in this directory. Run "npx nylorun start" in this project to start its Tenant and link it.');
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
console.log(`export NYLORUN_RUNTIME_URL=${shellQuote(link.hostUrl)}`);
|
|
14
|
-
console.log(`export NYLORUN_SERVER_KEY=${shellQuote(credentials.applicationKey)}`);
|
|
15
|
-
}
|
|
16
|
-
export function shellQuote(value) {
|
|
17
|
-
if (/^[A-Za-z0-9_./:-]+$/.test(value))
|
|
18
|
-
return value;
|
|
19
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
20
|
-
}
|
package/dist/project/link.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type ProjectLink as LinkFile } from "@nylorun/admin/project";
|
|
2
|
-
/**
|
|
3
|
-
* Project-local link to an installation (format 3). `nylorun start` writes it; `nylo` only
|
|
4
|
-
* reads it, with `@nylorun/core/project`'s reader (through `@nylorun/admin/project`).
|
|
5
|
-
* `tenantId` is information: the installation serves one Tenant, so nothing selects it.
|
|
6
|
-
*/
|
|
7
|
-
export type ProjectLink = LinkFile & {
|
|
8
|
-
format: 3;
|
|
9
|
-
};
|
|
10
|
-
export declare function readLink(projectRoot: string): Promise<ProjectLink | undefined>;
|
package/dist/project/link.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ProjectFileError, projectLinkPath, readProjectLink, } from "@nylorun/admin/project";
|
|
2
|
-
import { CliError } from "../errors.js";
|
|
3
|
-
export async function readLink(projectRoot) {
|
|
4
|
-
const path = projectLinkPath(projectRoot);
|
|
5
|
-
let link;
|
|
6
|
-
try {
|
|
7
|
-
link = readProjectLink(projectRoot);
|
|
8
|
-
}
|
|
9
|
-
catch (error) {
|
|
10
|
-
if (!(error instanceof ProjectFileError))
|
|
11
|
-
throw error;
|
|
12
|
-
throw new CliError(error.reason === "newer"
|
|
13
|
-
? `Project link at ${path} has an unsupported format. Upgrade the CLI.`
|
|
14
|
-
: `Invalid Project link at ${path}. Remove .nylorun/link.json and run "npx nylorun start".`, 1);
|
|
15
|
-
}
|
|
16
|
-
if (link === undefined)
|
|
17
|
-
return undefined;
|
|
18
|
-
// Formats 0 to 2 are from older releases.
|
|
19
|
-
if (link.format !== 3)
|
|
20
|
-
throw new CliError(`The Project link at ${path} is from an older nylorun. Run "npx nylorun start" in this project to link it again.`, 1);
|
|
21
|
-
return { ...link, format: 3 };
|
|
22
|
-
}
|