kylon-cli 0.1.0-next.4
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 +560 -0
- package/dist/kylon-bundle.mjs +2 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
# kylon-cli
|
|
2
|
+
|
|
3
|
+
Gateway CLI for connecting local agent providers to a P2 workspace.
|
|
4
|
+
|
|
5
|
+
Requires Node.js 22 or newer.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
The current release train ships a single self-contained `.mjs` bundle
|
|
10
|
+
as a GitHub Release asset on the `fre-so/p2` repo. The Web UI
|
|
11
|
+
generates two separate commands — an **install** step and a **run**
|
|
12
|
+
step — each with its own Copy button. Operators paste the install
|
|
13
|
+
once per host (and any time they want to upgrade) and the run
|
|
14
|
+
whenever they want to start the daemon. npm is on the roadmap (see
|
|
15
|
+
[Planned: npm install](#planned-npm-install) below) but not wired yet.
|
|
16
|
+
|
|
17
|
+
### Step 1 — Install kylon (once per host, re-paste to upgrade)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gh release download --repo fre-so/p2 --pattern 'kylon.mjs' --output /tmp/kylon.new \
|
|
21
|
+
&& chmod +x /tmp/kylon.new \
|
|
22
|
+
&& sudo mv /tmp/kylon.new /usr/local/bin/kylon
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Downloads the latest bundle to an unprivileged scratch file, flips
|
|
26
|
+
the executable bit, and `sudo mv`'s it into `/usr/local/bin/kylon`
|
|
27
|
+
atomically. The repo is private, so `gh` (authenticated via
|
|
28
|
+
`gh auth login`) is required instead of plain `curl`.
|
|
29
|
+
|
|
30
|
+
Re-paste to upgrade: the `sudo mv` overwrites the binary in place
|
|
31
|
+
with whatever the latest release publishes as `kylon.mjs`. If you
|
|
32
|
+
want a specific build instead of latest, download the matching
|
|
33
|
+
`kylon-vX.Y.Z.mjs` from that release's page — every release
|
|
34
|
+
publishes both the stable-name `kylon.mjs` and the versioned copy.
|
|
35
|
+
|
|
36
|
+
Requirements: Node.js 22+, [GitHub CLI](https://cli.github.com/)
|
|
37
|
+
(`gh auth login`), `sudo`. Linux / macOS only (Windows operators
|
|
38
|
+
should use WSL).
|
|
39
|
+
|
|
40
|
+
### Step 2 — Start the gateway daemon
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
kylon gateway run \
|
|
44
|
+
--server-url https://<origin>/api \
|
|
45
|
+
--provider codex \
|
|
46
|
+
--api-key <agent-api-key>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`gateway run` registers the session (writing `~/.kylon/gateway-session.json`
|
|
50
|
+
with mode `0600`) and starts the daemon in one step. On the next
|
|
51
|
+
invocation, if a session already exists, plain `kylon gateway run`
|
|
52
|
+
without flags picks it up and jumps straight to start. See
|
|
53
|
+
[Usage → Run](#run) for the full decision table. The daemon runs
|
|
54
|
+
in the foreground until you stop it with `Ctrl+C`; hosts that want a
|
|
55
|
+
supervised daemon typically wrap the same command in
|
|
56
|
+
`launchd` / `systemd` / `supervisord`.
|
|
57
|
+
|
|
58
|
+
### Local development (contributors)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pnpm install
|
|
62
|
+
pnpm --filter kylon-cli build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
See [Development → Exposing `kylon` to provider subprocesses](#exposing-kylon-to-provider-subprocesses)
|
|
66
|
+
for the debugger-friendly tsx-based dev loop.
|
|
67
|
+
|
|
68
|
+
### Planned: npm install
|
|
69
|
+
|
|
70
|
+
The npm distribution path is designed (see
|
|
71
|
+
[`docs/journal_docs/04_13_cli_npx_gateway_run_release_plan.md`](../../docs/journal_docs/04_13_cli_npx_gateway_run_release_plan.md))
|
|
72
|
+
and this package is already publish-ready (`packages/cli/package.json`
|
|
73
|
+
has the metadata and `prepack` runs `bundle:release`). Publishing is
|
|
74
|
+
held pending the rollout-config side of that plan (platform-served
|
|
75
|
+
`recommendedCliVersion`), at which point the Web UI will drop the
|
|
76
|
+
Install step entirely and flip the Run step to
|
|
77
|
+
`npx -y kylon-cli@<recommended> gateway run …`. Don't `npm publish`
|
|
78
|
+
ad-hoc before that — the first live release should come out through
|
|
79
|
+
the paired CI workflow so the token path is exercised end-to-end.
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
### Run
|
|
84
|
+
|
|
85
|
+
`gateway run` is the recommended entrypoint for external agent operators.
|
|
86
|
+
It composes `connect` + `start` into a single command.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
kylon gateway run \
|
|
90
|
+
--server-url https://api.p2.ai \
|
|
91
|
+
--provider codex \
|
|
92
|
+
--api-key pak_xxxxx
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Decision table:
|
|
96
|
+
|
|
97
|
+
| Saved session? | Flags passed? | What `run` does |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| no | `--server-url` + `--api-key` + `--provider` | connect, persist session, start daemon |
|
|
100
|
+
| no | any field missing | error — lists the missing flag |
|
|
101
|
+
| yes | none | start the daemon from the saved session |
|
|
102
|
+
| yes | any | reconnect with the overrides (falls back to saved values for fields you didn't pass), persist the refreshed session, start the daemon |
|
|
103
|
+
|
|
104
|
+
Only CLI flags count as overrides. Setting `KYLON_API_KEY` in the
|
|
105
|
+
environment does **not** trigger a reconnect on a saved session —
|
|
106
|
+
it still feeds the normal API-key resolution inside `gateway start`.
|
|
107
|
+
|
|
108
|
+
### Connect
|
|
109
|
+
|
|
110
|
+
Register this machine as the gateway client for an external agent.
|
|
111
|
+
Most operators should prefer `gateway run` above. Use `connect` on its
|
|
112
|
+
own when scripting or when you need to register a session without
|
|
113
|
+
immediately starting the daemon.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
kylon gateway connect \
|
|
117
|
+
--server-url https://api.p2.ai \
|
|
118
|
+
--api-key pak_xxxxx \
|
|
119
|
+
--provider codex
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The agent's API key identifies which agent the daemon will serve;
|
|
123
|
+
channels are bound separately via the "invite agent into channel" flow
|
|
124
|
+
in the web UI.
|
|
125
|
+
|
|
126
|
+
### Bind
|
|
127
|
+
|
|
128
|
+
Create or update a logical session binding for an agent. Run from the
|
|
129
|
+
target working directory:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
kylon gateway bind \
|
|
133
|
+
--agent agent_123 \
|
|
134
|
+
--provider codex
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Switch an existing binding to a different directory:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
cd /path/to/other/repo
|
|
141
|
+
kylon gateway bind --agent agent_123 --workdir .
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Bindings are keyed by `(gateway session, agent)`. The same agent
|
|
145
|
+
behaves the same way regardless of which channel an assignment
|
|
146
|
+
arrives on — see
|
|
147
|
+
[`docs/journal_docs/05_27_gateway_routing_simplification.md`](../../docs/journal_docs/05_27_gateway_routing_simplification.md).
|
|
148
|
+
|
|
149
|
+
### Commands
|
|
150
|
+
|
|
151
|
+
| Command | Description |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `kylon gateway run` | **Recommended.** Connect + start in one step; idempotent when a session already exists. |
|
|
154
|
+
| `kylon gateway connect` | Register this machine as the gateway client for an agent (API key identifies which), without starting the daemon. |
|
|
155
|
+
| `kylon gateway bind` | Create or update a logical session binding. |
|
|
156
|
+
| `kylon gateway start` | Start the gateway daemon from a saved session — opens the SSE stream and executes assignments. Reads the API key from the saved session. |
|
|
157
|
+
|
|
158
|
+
### Run Options
|
|
159
|
+
|
|
160
|
+
| Flag | Description |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `--server-url <url>` | P2 server URL (required for the first run; override otherwise) |
|
|
163
|
+
| `--api-key <key>` | Agent API key, e.g. `pak_xxx` (required for first run; override otherwise. `KYLON_API_KEY` env var satisfies the first-run requirement but does not count as an override on subsequent runs) |
|
|
164
|
+
| `--provider <name>` | Provider CLI: `codex`, `claude-code`, `hermes`, `openclaw`, `generic` (required for first run; override otherwise) |
|
|
165
|
+
|
|
166
|
+
### Connect Options
|
|
167
|
+
|
|
168
|
+
| Flag | Description |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `--server-url <url>` | P2 server URL (required) |
|
|
171
|
+
| `--api-key <key>` | Agent API key, e.g. `pak_xxx` (required, or set `KYLON_API_KEY`) |
|
|
172
|
+
| `--provider <name>` | Provider CLI: `codex`, `claude-code`, `hermes`, `openclaw`, `generic` (required) |
|
|
173
|
+
|
|
174
|
+
### Bind Options
|
|
175
|
+
|
|
176
|
+
| Flag | Description |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `--agent <id>` | Agent ID (required) |
|
|
179
|
+
| `--provider <name>` | Provider CLI (required for new, optional for update) |
|
|
180
|
+
| `--workdir <path>` | Working directory (default: current directory) |
|
|
181
|
+
|
|
182
|
+
### Supported Providers
|
|
183
|
+
|
|
184
|
+
- `codex` — OpenAI Codex CLI
|
|
185
|
+
- `claude-code` — Anthropic Claude Code CLI
|
|
186
|
+
- `hermes` — Hermes CLI using `hermes -z <prompt>`
|
|
187
|
+
- `openclaw` — OpenClaw CLI using `openclaw agent --message <prompt> --json`
|
|
188
|
+
- `generic` - provider-neutral wrapper. Runs a `kylon-provider`
|
|
189
|
+
executable on `PATH` and expects newline-delimited JSON events matching
|
|
190
|
+
`docs/journal_docs/05_25_external_agent_provider_adapter_contract.md`.
|
|
191
|
+
|
|
192
|
+
## State Model
|
|
193
|
+
|
|
194
|
+
The CLI uses a three-layer state model:
|
|
195
|
+
|
|
196
|
+
- **GatewaySession** — authenticated connection to the P2 server (one per machine)
|
|
197
|
+
- **LogicalSessionState** — per-`(gateway session, agent)` binding holding the current workdir and provider
|
|
198
|
+
- **ProviderRuntimeEntry** — per-conversation provider resume cache, keyed by `(gateway session, channel, agent, scope, provider, workdir)` (disposable)
|
|
199
|
+
|
|
200
|
+
Switching workdir updates the logical session without creating a new one. Provider runtimes are cached per workdir + conversation scope — switching back resumes the old runtime.
|
|
201
|
+
|
|
202
|
+
State is persisted to `~/.kylon/` (or `$XDG_CONFIG_HOME/kylon/`).
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
> This section assumes you cloned `fre-so/p2` and ran `pnpm install` at the
|
|
207
|
+
> repo root. All commands are run from anywhere in the monorepo unless
|
|
208
|
+
> otherwise noted.
|
|
209
|
+
|
|
210
|
+
### Inner loop
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# type-check without emitting
|
|
214
|
+
pnpm --filter kylon-cli typecheck
|
|
215
|
+
|
|
216
|
+
# tsc build — emits dist/bin/kylon.js and other .js files
|
|
217
|
+
pnpm --filter kylon-cli build
|
|
218
|
+
|
|
219
|
+
# unit tests (no network, no DB)
|
|
220
|
+
pnpm --filter kylon-cli test
|
|
221
|
+
|
|
222
|
+
# run the locally built CLI
|
|
223
|
+
node packages/cli/dist/bin/kylon.js --help
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The Web agent-settings "Local dev" block generates the same
|
|
227
|
+
`node packages/cli/dist/bin/kylon.js …` invocation from the logged-in
|
|
228
|
+
agent's API key. If you change `src/bin/kylon.ts` or anything it
|
|
229
|
+
imports, rerun `pnpm --filter kylon-cli build` before re-executing.
|
|
230
|
+
|
|
231
|
+
### Exposing `kylon` to provider subprocesses
|
|
232
|
+
|
|
233
|
+
`node packages/cli/dist/bin/kylon.js gateway run …` starts the daemon
|
|
234
|
+
but leaves **no `kylon` binary on PATH**. When the provider subprocess
|
|
235
|
+
(claude-code / codex) then tries `kylon workspace …` via its Bash tool,
|
|
236
|
+
the shell fails with `command not found`. Two ways to fix this:
|
|
237
|
+
|
|
238
|
+
**Option 1 — `pnpm link` (persistent).** Symlink kylon's published
|
|
239
|
+
`bin.kylon` into pnpm's global bin dir:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
pnpm --filter kylon-cli bundle # or bundle:minify for a prod-shaped build
|
|
243
|
+
pnpm --filter kylon-cli link --global
|
|
244
|
+
|
|
245
|
+
which kylon
|
|
246
|
+
# → ~/Library/pnpm/kylon (or similar) → packages/cli/dist/kylon-bundle.mjs
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
From here on, start the daemon via the linked binary instead of the
|
|
250
|
+
raw `node dist/bin/kylon.js`:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
kylon gateway run --server-url http://localhost:5173/api --provider codex --api-key pak_…
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Every provider call to `kylon workspace …` resolves to the linked
|
|
257
|
+
binary. Edit source → `pnpm --filter kylon-cli bundle` → next provider
|
|
258
|
+
call picks it up (each workspace invocation is a fresh process;
|
|
259
|
+
restart the daemon only for *daemon*-side edits). Clean up with
|
|
260
|
+
`pnpm --filter kylon-cli unlink --global`.
|
|
261
|
+
|
|
262
|
+
**Option 2 — `--dev-cli-shim` (ephemeral, IDE-friendly).** The daemon
|
|
263
|
+
can install a temporary bash shim that execs the TS source through
|
|
264
|
+
tsx. Pass `--dev-cli-shim <abs-path-to-p2-repo>` on `gateway run` or
|
|
265
|
+
`gateway start`:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
node packages/cli/dist/bin/kylon.js gateway run \
|
|
269
|
+
--server-url http://localhost:5173/api \
|
|
270
|
+
--provider codex \
|
|
271
|
+
--api-key pak_… \
|
|
272
|
+
--dev-cli-shim "$(pwd)"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The daemon prints the shim location on startup:
|
|
276
|
+
|
|
277
|
+
```text
|
|
278
|
+
[dev] kylon shim: /tmp/kylon-dev-shim-abc123/kylon
|
|
279
|
+
[dev] provider calls will exec: node --import tsx /abs/path/packages/cli/src/bin/kylon.ts
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The tmpdir is prepended to the daemon's `PATH`, so the provider
|
|
283
|
+
subprocess (and the bash shell it spawns) resolves `kylon` to the
|
|
284
|
+
shim. The shim execs `node --import tsx <src>`, so:
|
|
285
|
+
|
|
286
|
+
- Every `kylon workspace …` invocation reads the current `src/*.ts` —
|
|
287
|
+
no bundle rebuild needed between edits.
|
|
288
|
+
- A Node debugger attached to the daemon is inherited by each shim
|
|
289
|
+
invocation (they exec `node`, so `NODE_OPTIONS` and VS Code's
|
|
290
|
+
Auto-Attach loader pass through).
|
|
291
|
+
- On SIGINT/SIGTERM the tmpdir is wiped.
|
|
292
|
+
|
|
293
|
+
This flag is **only compiled into local (non-minified) builds**. The
|
|
294
|
+
npm package, the GitHub Release bundle, and any `bundle:release`
|
|
295
|
+
output reject `--dev-cli-shim` as an unknown argument and omit it from
|
|
296
|
+
`--help`, so it can never accidentally ship.
|
|
297
|
+
|
|
298
|
+
### Debugging both the daemon and `kylon workspace` calls
|
|
299
|
+
|
|
300
|
+
Combine `--dev-cli-shim` with a Node debugger to step through the
|
|
301
|
+
full chain — daemon → provider subprocess → `kylon workspace …` — in
|
|
302
|
+
one IDE session.
|
|
303
|
+
|
|
304
|
+
**VS Code**:
|
|
305
|
+
|
|
306
|
+
1. Enable `Debug: Toggle Auto Attach → Always` (or `Only With Flag`).
|
|
307
|
+
VS Code prepends its `js-debug` bootloader to `NODE_OPTIONS`, which
|
|
308
|
+
every child Node process — including the ones launched by the
|
|
309
|
+
shim — inherits.
|
|
310
|
+
2. Open an integrated terminal and run:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
node packages/cli/dist/bin/kylon.js gateway run \
|
|
314
|
+
--server-url http://localhost:5173/api \
|
|
315
|
+
--provider codex \
|
|
316
|
+
--api-key pak_… \
|
|
317
|
+
--dev-cli-shim "$(pwd)"
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
3. Set breakpoints in both `packages/cli/src/commands/gateway-start.ts`
|
|
321
|
+
(daemon) and `packages/cli/src/commands/workspace/*.ts` (child
|
|
322
|
+
commands). Both fire the next time the provider issues a workspace
|
|
323
|
+
call.
|
|
324
|
+
|
|
325
|
+
**JetBrains / others**: export `NODE_OPTIONS=--inspect=0.0.0.0:0`
|
|
326
|
+
before launching the daemon. Every subsequent Node process — daemon
|
|
327
|
+
and every `kylon workspace …` invocation — opens its own inspector
|
|
328
|
+
port. Attach your IDE to the process list.
|
|
329
|
+
|
|
330
|
+
No debugger attach? The shim still works — source edits are picked up
|
|
331
|
+
on the next provider call, but breakpoints just don't fire.
|
|
332
|
+
|
|
333
|
+
### Bundles
|
|
334
|
+
|
|
335
|
+
The release artifact is a single-file ESM bundle produced by
|
|
336
|
+
`scripts/bundle.mjs`. Three variants:
|
|
337
|
+
|
|
338
|
+
| Script | Output | Passes to `bundle.mjs` | Use when |
|
|
339
|
+
|---|---|---|---|
|
|
340
|
+
| `pnpm --filter kylon-cli bundle` | `dist/kylon-bundle.mjs` | _(none)_ | debugging the bundled shape while keeping readable names — not shipped |
|
|
341
|
+
| `pnpm --filter kylon-cli bundle:minify` | `dist/kylon-bundle.mjs` | `--minify` | reproducing the pre-obfuscation size and behavior for a bisect |
|
|
342
|
+
| `pnpm --filter kylon-cli bundle:release` | `dist/kylon-bundle.mjs` | `--minify --obfuscate` | what ships on npm and in GitHub Releases |
|
|
343
|
+
|
|
344
|
+
The `bundle:release` path runs esbuild with `--minify`, then passes the
|
|
345
|
+
output through `javascript-obfuscator`. It's also what the `prepack` hook
|
|
346
|
+
runs, so `pnpm pack` / `npm publish` always produce the obfuscated shape
|
|
347
|
+
even if you forget to call `bundle:release` explicitly.
|
|
348
|
+
|
|
349
|
+
Run the bundle directly to sanity-check it:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
pnpm --filter kylon-cli bundle:release
|
|
353
|
+
node packages/cli/dist/kylon-bundle.mjs --help
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Testing
|
|
357
|
+
|
|
358
|
+
Unit tests run against Node's built-in test runner (`node:test`) plus
|
|
359
|
+
`tsx`. They touch the filesystem inside temp dirs but never the
|
|
360
|
+
network or a database, so `pnpm --filter kylon-cli test` is safe to
|
|
361
|
+
run anywhere.
|
|
362
|
+
|
|
363
|
+
E2E tests drive the CLI as a subprocess against a configurable mock
|
|
364
|
+
or live server:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# headless e2e (mock provider processes spawned from scripts/mock-*.mjs)
|
|
368
|
+
pnpm --filter kylon-cli test:e2e
|
|
369
|
+
|
|
370
|
+
# live e2e against a real P2 environment — requires doppler secrets
|
|
371
|
+
doppler run --project p2 --config prd -- pnpm --filter kylon-cli test:e2e:live
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Live E2E tests provision a throwaway workspace via the REST API, so
|
|
375
|
+
expect them to take several minutes and to leave audit trail rows in
|
|
376
|
+
the target environment. Do not point them at production casually.
|
|
377
|
+
|
|
378
|
+
Before shipping a release, also smoke-test the packaged artifact
|
|
379
|
+
exactly as operators will receive it:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
# 1. produce the release bundle + tarball
|
|
383
|
+
pnpm --filter kylon-cli bundle:release
|
|
384
|
+
cd packages/cli && pnpm pack --pack-destination /tmp/kylon-out
|
|
385
|
+
|
|
386
|
+
# 2. install the tarball in a clean directory
|
|
387
|
+
WORK=$(mktemp -d) && cd "$WORK"
|
|
388
|
+
npm init -y >/dev/null
|
|
389
|
+
npm install /tmp/kylon-out/kylon-cli-*.tgz
|
|
390
|
+
|
|
391
|
+
# 3. the published shape should have only three entries
|
|
392
|
+
ls node_modules/kylon-cli # dist/ package.json README.md
|
|
393
|
+
ls node_modules/kylon-cli/dist # kylon-bundle.mjs
|
|
394
|
+
|
|
395
|
+
# 4. verify the binary is runnable
|
|
396
|
+
node_modules/.bin/kylon --help
|
|
397
|
+
|
|
398
|
+
# 5. confirm the bundle is actually obfuscated
|
|
399
|
+
head -c 200 node_modules/kylon-cli/dist/kylon-bundle.mjs
|
|
400
|
+
# expect `#!/usr/bin/env node` followed by hexadecimal identifier soup,
|
|
401
|
+
# not recognizable function names or source strings
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Obfuscation
|
|
405
|
+
|
|
406
|
+
`bundle:release` and `prepack` run `javascript-obfuscator` with a
|
|
407
|
+
conservative preset chosen for runtime safety:
|
|
408
|
+
|
|
409
|
+
- **On:** `compact`, `identifierNamesGenerator: "hexadecimal"`,
|
|
410
|
+
`stringArray` with `base64` encoding + rotate + shuffle, two
|
|
411
|
+
wrapper function layers.
|
|
412
|
+
- **Off:** `controlFlowFlattening`, `deadCodeInjection`,
|
|
413
|
+
`selfDefending`, `debugProtection`, `unicodeEscapeSequence`. These
|
|
414
|
+
trade correctness and startup latency for marginal protection — do
|
|
415
|
+
not turn them on without measuring startup and rerunning the full
|
|
416
|
+
test:e2e suite.
|
|
417
|
+
- `renameGlobals` stays off so Node built-ins keep their names.
|
|
418
|
+
|
|
419
|
+
Startup stays under 100 ms on modern hardware; bundle grows from
|
|
420
|
+
~85 KB (minify only) to ~230 KB (obfuscated). Obfuscation is a
|
|
421
|
+
tampering and casual-reading deterrent, not a security control — the
|
|
422
|
+
API server is the security boundary.
|
|
423
|
+
|
|
424
|
+
## Release
|
|
425
|
+
|
|
426
|
+
Today there is **one** active release channel: a bundled `.mjs`
|
|
427
|
+
uploaded as a GitHub Release asset. The npm channel is designed and
|
|
428
|
+
publish-ready but intentionally held pending the
|
|
429
|
+
`recommendedCliVersion` rollout config described in
|
|
430
|
+
[`docs/journal_docs/04_13_cli_npx_gateway_run_release_plan.md`](../../docs/journal_docs/04_13_cli_npx_gateway_run_release_plan.md).
|
|
431
|
+
See [Planned: npm publish](#planned-npm-publish) below.
|
|
432
|
+
|
|
433
|
+
### Versioning
|
|
434
|
+
|
|
435
|
+
Use explicit semver in `packages/cli/package.json`:
|
|
436
|
+
|
|
437
|
+
| Bump | When |
|
|
438
|
+
|---|---|
|
|
439
|
+
| patch (`0.1.0 → 0.1.1`) | bug fix, no new flags, no behavior change |
|
|
440
|
+
| minor (`0.1.x → 0.2.0`) | backward-compatible capability (new command, new flag) |
|
|
441
|
+
| major (`0.x.x → 1.0.0`) | breaking change to CLI contract or runtime behavior |
|
|
442
|
+
|
|
443
|
+
The GitHub Release tag still uses the historical
|
|
444
|
+
`cli/v0.0.${github.run_number}` scheme, which is fine as an opaque
|
|
445
|
+
build id while there is no consumer that pins to it. The moment we
|
|
446
|
+
flip the npm plan on, bump `packages/cli/package.json` to explicit
|
|
447
|
+
semver and let the publish workflow own version selection.
|
|
448
|
+
|
|
449
|
+
### Active channel — GitHub Release asset
|
|
450
|
+
|
|
451
|
+
Triggered automatically by `.github/workflows/release-cli.yml` on any
|
|
452
|
+
push to `main` that touches `packages/cli/**`,
|
|
453
|
+
`packages/workspace-cli-core/**`, or `packages/types/**`. The workflow:
|
|
454
|
+
|
|
455
|
+
1. Runs `pnpm --filter kylon-cli bundle:release` (obfuscated output).
|
|
456
|
+
2. Smoke-tests `node packages/cli/dist/kylon-bundle.mjs --help`.
|
|
457
|
+
3. Copies the bundle to **two** asset names under the release:
|
|
458
|
+
- `kylon-v0.0.${run_number}.mjs` — versioned, useful for pinning.
|
|
459
|
+
- `kylon.mjs` — stable name, downloadable via
|
|
460
|
+
`gh release download --repo fre-so/p2 --pattern 'kylon.mjs'`.
|
|
461
|
+
4. Creates a GitHub Release tagged `cli/v0.0.${run_number}` with both
|
|
462
|
+
`.mjs` files attached.
|
|
463
|
+
|
|
464
|
+
The repo is private, so downloads require `gh` (authenticated) rather
|
|
465
|
+
than plain `curl`. This is the shape the Web UI generates:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
gh release download --repo fre-so/p2 --pattern 'kylon.mjs' --output /tmp/kylon
|
|
469
|
+
chmod +x /tmp/kylon
|
|
470
|
+
sudo mv /tmp/kylon /usr/local/bin/kylon
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Pin to a specific build by downloading the versioned asset from its
|
|
474
|
+
release page instead.
|
|
475
|
+
|
|
476
|
+
No npm token required; the workflow only needs `contents: write`.
|
|
477
|
+
|
|
478
|
+
### Rollback (GitHub channel)
|
|
479
|
+
|
|
480
|
+
If the most recent push produced a broken CLI:
|
|
481
|
+
|
|
482
|
+
1. Identify the last known good release under
|
|
483
|
+
<https://github.com/fre-so/p2/releases>.
|
|
484
|
+
2. Manually re-upload that release's bundle under the name
|
|
485
|
+
`kylon.mjs` on the most recent release — that's what the Web UI's
|
|
486
|
+
stable URL resolves against. (Alternatively, revert the bad commit
|
|
487
|
+
and let `release-cli.yml` cut a new release.)
|
|
488
|
+
3. Hosts with persistent installs re-run `curl -fsSL … -o
|
|
489
|
+
/usr/local/bin/kylon` to pull the rolled-back bundle.
|
|
490
|
+
|
|
491
|
+
There is no equivalent of `npm deprecate` on GitHub Releases, so a
|
|
492
|
+
broken bundle is "fixed" only by publishing a newer one.
|
|
493
|
+
|
|
494
|
+
### Planned: npm publish
|
|
495
|
+
|
|
496
|
+
This package is already wired for `npm publish` (metadata, `files`,
|
|
497
|
+
`prepack`, `publishConfig.access: public`). The only things missing
|
|
498
|
+
are the publish CI workflow and the owning npm account. The plan:
|
|
499
|
+
|
|
500
|
+
1. Someone claims `kylon-cli` on npm (the name is currently 404).
|
|
501
|
+
2. A granular publish token (Read + Write on `kylon-cli`) is stored
|
|
502
|
+
as the `NPM_TOKEN` GitHub Actions secret on `fre-so/p2`.
|
|
503
|
+
3. Split the release automation:
|
|
504
|
+
- **`cli-verify.yml`** — runs on PRs and pushes touching
|
|
505
|
+
`packages/cli/**`. Runs typecheck, lint, test, `bundle:release`,
|
|
506
|
+
and `npm pack`. Does not publish.
|
|
507
|
+
- **`cli-publish.yml`** — runs on push of tag `cli/vX.Y.Z`.
|
|
508
|
+
Verifies the tag matches `packages/cli/package.json#version`,
|
|
509
|
+
then `pnpm --filter kylon-cli publish --access public
|
|
510
|
+
--no-git-checks` with
|
|
511
|
+
`NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}`.
|
|
512
|
+
4. The API exposes `recommendedCliVersion`; the Web UI flips from
|
|
513
|
+
the `curl` shape above to `npx -y kylon-cli@<recommended> gateway
|
|
514
|
+
run …`.
|
|
515
|
+
|
|
516
|
+
Until (2) + (3) land, do **not** `npm publish` ad-hoc — the first
|
|
517
|
+
live release should come out through the paired CI workflow so the
|
|
518
|
+
token path is exercised end-to-end. Once the workflow exists, the
|
|
519
|
+
release ritual becomes:
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
# 1. On a release branch
|
|
523
|
+
pnpm --filter kylon-cli version 0.1.1 # bumps packages/cli/package.json only
|
|
524
|
+
|
|
525
|
+
# 2. Merge to main via PR, verify CI is green
|
|
526
|
+
|
|
527
|
+
# 3. Tag the commit on main
|
|
528
|
+
git pull
|
|
529
|
+
git tag cli/v0.1.1
|
|
530
|
+
git push origin cli/v0.1.1
|
|
531
|
+
|
|
532
|
+
# 4. GitHub Actions publishes; verify
|
|
533
|
+
npm view kylon-cli@0.1.1 dist-tags
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Rollback once npm is live: flip `recommendedCliVersion` in the API
|
|
537
|
+
back to the last known good version (new operators get that version
|
|
538
|
+
in their install command; in-flight daemons keep their current CLI
|
|
539
|
+
until they restart). If the pushed version is actively broken, also
|
|
540
|
+
`npm deprecate kylon-cli@<bad> "…"` so operators that ignore the
|
|
541
|
+
recommendation see a warning. Do not `npm unpublish`.
|
|
542
|
+
|
|
543
|
+
### Pre-release channel (npm-era, also planned)
|
|
544
|
+
|
|
545
|
+
Once npm is live, risky changes can go out under a `next` dist-tag so
|
|
546
|
+
the Web UI's recommended version is untouched:
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
# 1. Bump to a prerelease version
|
|
550
|
+
pnpm --filter kylon-cli version 0.2.0-next.0
|
|
551
|
+
|
|
552
|
+
# 2. Publish under the `next` dist-tag
|
|
553
|
+
cd packages/cli && pnpm publish --tag next --access public --no-git-checks
|
|
554
|
+
|
|
555
|
+
# 3. Operators can opt in explicitly
|
|
556
|
+
npx -y kylon-cli@next gateway run …
|
|
557
|
+
|
|
558
|
+
# 4. Promote to `latest` once validated
|
|
559
|
+
npm dist-tag add kylon-cli@0.2.0-next.0 latest
|
|
560
|
+
```
|