@kahunalabs/cli 0.0.0-stage → 0.0.14
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/LICENSE +21 -0
- package/README.md +144 -2
- package/dist/index.js +79 -0
- package/package.json +43 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kalle Hansson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,145 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `kahuna` — the Kahuna command line
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```
|
|
4
|
+
kahuna <noun> [<sub-noun>] <verb> [target] [--flags]
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
kahuna auth login --host https://kahuna.example.com
|
|
9
|
+
kahuna record list vendor --where status=active --json | jq '.[].label'
|
|
10
|
+
kahuna record import vendor vendors.csv --key name
|
|
11
|
+
kahuna concept create Supplier
|
|
12
|
+
kahuna access check --user <id> --action edit --resource concept --id <id>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## The grammar
|
|
16
|
+
|
|
17
|
+
**Every command names the thing that owns the row.** A relation edge belongs to
|
|
18
|
+
a record, so it is `record relation add`; a relation *type* is a field on a
|
|
19
|
+
concept, so it is `concept field add --kind relation`.
|
|
20
|
+
|
|
21
|
+
**The first token after a noun is a verb or a sub-noun — never an id.** Ids come
|
|
22
|
+
after the verb. That is what makes `task status list` (the status catalogue)
|
|
23
|
+
unambiguous next to `task update <id> --status done` (one task's status).
|
|
24
|
+
|
|
25
|
+
**One verb set:** `list · get · create · update · delete · archive · restore`,
|
|
26
|
+
plus domain verbs where the domain has one (`publish`, `transition`, `sync`,
|
|
27
|
+
`assign`, `deactivate`). A verb means the same thing under every noun: `delete`
|
|
28
|
+
always purges, `archive` is always reversible.
|
|
29
|
+
|
|
30
|
+
Run `kahuna help` for the full list, `kahuna <command> --help` for one, and
|
|
31
|
+
`kahuna --version` (or `-v`) for the CLI's own version — no network and no
|
|
32
|
+
sign-in. That last one is deliberately not `kahuna system version`, which asks the
|
|
33
|
+
DEPLOYMENT what it is running and warns if the two have drifted.
|
|
34
|
+
|
|
35
|
+
## Output
|
|
36
|
+
|
|
37
|
+
Data on **stdout**, everything else on **stderr** — `kahuna … --json | jq` never has
|
|
38
|
+
to filter our chatter. `--json`, `--csv`, or an aligned table by default.
|
|
39
|
+
|
|
40
|
+
| exit | meaning |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| 0 | fine |
|
|
43
|
+
| 1 | failed |
|
|
44
|
+
| 2 | you typed something wrong |
|
|
45
|
+
| 3 | not signed in |
|
|
46
|
+
| 4 | not allowed |
|
|
47
|
+
| 5 | not found |
|
|
48
|
+
| 6 | conflict (something changed underneath, or is still in use) |
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
One deployment, stored in `$XDG_CONFIG_HOME/kahuna/config.json` (mode 0600):
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{ "host": "https://kahuna.example.com", "cookie": "…", "email": "you@example.com" }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`kahuna auth login --host <url>` writes it; every later command reads it. **There is
|
|
59
|
+
no default host** — an unconfigured CLI says so rather than quietly trying
|
|
60
|
+
`localhost`. `KAHUNA_HOST` overrides it without touching the file, which is what CI
|
|
61
|
+
wants.
|
|
62
|
+
|
|
63
|
+
## Signing in through the browser
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
kahuna auth login --browser # or --sso, which is the same thing
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Open: https://kahuna.example.com/api/cli/device?code=WDJB-MJHT
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The CLI prints a link and waits. Open it in **any browser, on any machine** —
|
|
74
|
+
your laptop, your phone — and sign in however this deployment allows (password,
|
|
75
|
+
SSO, anything). Opening the link is the approval; there is nothing to type. The
|
|
76
|
+
CLI is polling, and continues on its own.
|
|
77
|
+
|
|
78
|
+
Nothing is redirected anywhere and nothing listens on a local port, which is
|
|
79
|
+
deliberate: the usual loopback-redirect flow needs the browser and the CLI on
|
|
80
|
+
the same machine, so it breaks over ssh, in a devcontainer, or any time you would
|
|
81
|
+
rather open the link on your phone. The CLI never talks to your identity
|
|
82
|
+
provider either, which is why this works where a CLI-driven SSO flow cannot.
|
|
83
|
+
|
|
84
|
+
The credential is the browser's session, so **signing out in the browser signs
|
|
85
|
+
the CLI out too**. That goes away when token credentials land.
|
|
86
|
+
|
|
87
|
+
## Keeping the CLI current
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
kahuna cli update # upgrade to the newest published version
|
|
91
|
+
kahuna cli update --dry-run # print what it would run, change nothing
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Why this matters more than it looks.** The CLI carries a *copy* of the wire
|
|
95
|
+
contract, compiled in at build time. Against a deployment built from newer
|
|
96
|
+
sources, a procedure whose shape changed fails inside a schema decode with a
|
|
97
|
+
message about a field nobody typed. `kahuna system version` detects that skew and
|
|
98
|
+
says "update the CLI first"; this is how you do it.
|
|
99
|
+
|
|
100
|
+
`kahuna cli update` asks **npm** what the newest `@kahunalabs/cli` is and upgrades a
|
|
101
|
+
global install in place, with no prompt. `kahuna system version` asks your
|
|
102
|
+
**deployment** what image it runs — different artifact, different registry. Set
|
|
103
|
+
`KAHUNA_REGISTRY` to point at a mirror.
|
|
104
|
+
|
|
105
|
+
It works out which package manager owns the install from where the binary
|
|
106
|
+
actually lives (`npm`, `bun`, `pnpm`, `yarn`) and **refuses** in three cases,
|
|
107
|
+
naming the command to run instead:
|
|
108
|
+
|
|
109
|
+
| where it is | why it refuses |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| a source checkout or `npm link` | installing would write over a working tree |
|
|
112
|
+
| a project dependency | it would rewrite that project's lockfile, and the CLI's CWD is not reliably that project's root |
|
|
113
|
+
| an `npx` cache | it is ephemeral; there is nothing installed to update |
|
|
114
|
+
|
|
115
|
+
The package manager's output goes to **stderr** with all other chatter, so
|
|
116
|
+
`kahuna cli update --json` stays parseable.
|
|
117
|
+
|
|
118
|
+
## What it cannot do yet
|
|
119
|
+
|
|
120
|
+
- **Unattended CI.** A session expires, and `kahuna auth token create` needs
|
|
121
|
+
BetterAuth's API-key plugin server-side. Until then, CI needs a
|
|
122
|
+
password-capable account and `kahuna auth login --email --password`.
|
|
123
|
+
- **Filter or sort on the server.** `listRecords` takes a concept and nothing
|
|
124
|
+
else, so `--where` / `--sort` / `--limit` run client-side over the whole set,
|
|
125
|
+
which the server caps at 50 000 records. Above that the CLI says so rather
|
|
126
|
+
than returning a truncated list that looks complete.
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
bun run --cwd packages/cli test # unit tests, no server needed
|
|
132
|
+
bun run cli:build # bundle for Node -> dist/index.js
|
|
133
|
+
bun run --cwd packages/app scripts/verify-cli.ts # end-to-end, needs a live server
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The bundle is ~620 KB, ~190 KB installed, with no dependencies. Effect is about
|
|
137
|
+
80% of that: the wire contract is defined with `effect/Schema` and the client is
|
|
138
|
+
`@effect/rpc`, so the CLI carries the server's runtime in order to speak its
|
|
139
|
+
format. That is the price of one shared contract and no drift, and it is a price
|
|
140
|
+
paid once at install.
|
|
141
|
+
|
|
142
|
+
The CLI targets **Node**, not Bun: `@types/bun` is deliberately absent, and
|
|
143
|
+
TypeScript that emits (parameter properties, enums) breaks
|
|
144
|
+
`node --experimental-strip-types` even though it typechecks. CI builds the
|
|
145
|
+
bundle and smoke-tests it with plain `node` for exactly that reason.
|