@dashai/cli 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Status
8
8
 
9
- `0.5.x` — full author workflow + **Query Plane v1 author tooling** + **P7 cross-module-action observability** (shipped 2026-05-20) + **BaaS Phase 1 deploy loop** (`api-keys`, `env export`, `--deploy-target`; shipped 2026-05-24). Commands: `login`, `logout`, `profile`, `workspace`, `init` / `module init` (incl. `--custom`, `--deploy-target railway|vercel|fly`) / `connect-git` / `generate` / `dev` / `validate` / `list` / `publish` / `diff` / `pull-data` / `clone` / `query-log` / `action-log`, plus `query` (run AST queries against the local module), `pull` (sync canonical manifest from backend), `api-keys list|create|revoke`, `env export`, and `deps add` / `list` / `remove` / `check`.
9
+ `0.5.x` — full author workflow + **Query Plane v1 author tooling** + **P7 cross-module-action observability** (shipped 2026-05-20) + **BaaS Phase 1 deploy loop** (`api-keys`, `env export`, `--deploy-target`; shipped 2026-05-24). Commands: `login`, `logout`, `profile`, `workspace`, `init` / `module init` (incl. `--custom`, `--deploy-target railway|vercel|fly`) / `connect-git` / `generate` / `dev` / `validate` / `list` / `publish` / `diff` / `pull-data` / `clone` / `query-log` / `action-log`, plus `query` (run AST queries against the local module), `pull` (sync canonical manifest from backend), `api-keys list|create|revoke`, `origins list|add|remove` (SSO callback allowlist), `env export`, and `deps` (live connect status) / `add` / `list` / `remove` / `check` / `bind` / `unbind` (cross-module dependency connect lifecycle).
10
10
 
11
11
  ## Install
12
12
 
@@ -85,9 +85,11 @@ One-command local dev loop — collapses the historical two-terminal flow into a
85
85
  1. `dashwise module dev` — manifest watcher + SDK regen
86
86
  2. `next dev` — Next.js dev server (via the project's `dev:next` script if defined, else `npx next dev`)
87
87
 
88
- Both children inherit a pre-populated env with `DASHWISE_API_URL`, `DASHWISE_API_TOKEN`, and `DASHWISE_INSTALLATION_ID` automatically resolved from your saved CLI config (`~/.config/dashwise/auth.json`). No more copying tokens into `.env.local` by hand.
88
+ Both children inherit a pre-populated env with the Phase 2 two-var contract (`DASHWISE_API_URL` + `DASHWISE_API_KEY`, plus a `NEXT_PUBLIC_DASHWISE_API_URL` mirror) automatically resolved from your saved CLI config (`~/.config/dashwise/auth.json`). No more copying credentials into `.env.local` by hand.
89
89
 
90
- **HH-CC-LDI-7:** when the current module has been provisioned via `dashwise init` (LDI-6), the per-module runtime token cached at `~/.config/dashwise/auth.json#modules[<slug>]` is automatically injected as `DASHWISE_RUNTIME_TOKEN` into both children. `DASHWISE_INSTALLATION_ID` flips to the literal `sandbox` (the URL placeholder the backend's runtime-token guard accepts) so the SDK can hit the data plane without you passing `--installation-id`. The pre-flight banner prints which auth source is in use.
90
+ **Two-var contract:** when the current module has been provisioned via `dashwise init` / `dashwise module register`, the per-module scoped `dwk_` API key cached at `~/.config/dashwise/auth.json#modules[<slug>]` is injected as `DASHWISE_API_KEY` into both children. The key resolves the install server-side, so the SDK hits the install-agnostic `/api/module-api` there is no `DASHWISE_INSTALLATION_ID`. `DASHWISE_API_TOKEN` (your CLI bearer), `DASHWISE_RUNTIME_TOKEN` (retired), and `DASHWISE_DEV_SESSION_ID` are **not** injected into the children the scoped key is the only data-plane credential, and the scaffold's sign-in route uses it (via `/api/module-api/sso/start-url`) for SSO too. The pre-flight banner prints which auth source is in use.
91
+
92
+ **Self-heal:** if your `auth.json` still has a legacy `runtimeToken` (pre-identity-collapse), `dashwise dev` mints a `dwk_` key via `POST /api/installations/:id/api-keys`, rewrites the entry, and tells you. If the install is gone, it points you to `dashwise init <slug> --force`.
91
93
 
92
94
  If the manifest's slug isn't in `auth.json#modules`, `dashwise dev` still starts (SSO + watcher work fine), but it prints a one-line warning that data-plane calls (db, qb) will 401 — pointing you to re-run `dashwise init <slug> --force` to provision.
93
95
 
@@ -95,13 +97,13 @@ Output from both children is line-prefixed (`[sdk]` cyan, `[next]` magenta) so a
95
97
 
96
98
  Flags:
97
99
  - `--dir <path>` — Project root (default: current directory).
98
- - `--installation-id <id>` — Override `DASHWISE_INSTALLATION_ID` (default `"sandbox"` if a runtime token is found, else `"local"`). Useful for prod-mode SSO testing against a real installed module.
100
+ - `--installation-id <id>` — Run against an already-installed module (skips dev-session registration and uses prod-mode SSO against a real installed module). The id is used by `dashwise dev` internally, not injected into the child env.
99
101
  - `--api-url <url>` — Override `DASHWISE_API_URL` (default: saved config value).
100
102
  - `--port <number>` — Port for `next dev` (default: Next picks 3000 / next free).
101
103
  - `--sdk-only` — Run only the manifest watcher; skip `next dev`.
102
104
  - `--next-only` — Run only `next dev`; skip the watcher.
103
105
 
104
- Requires `dashwise login` first — the command refuses to start if no token is resolvable. The runtime-token injection requires `dashwise init <slug>` (LDI-6); the dev session SSO setup works without it.
106
+ Requires `dashwise login` first — the command refuses to start if no token is resolvable. The `DASHWISE_API_KEY` injection requires `dashwise init <slug>` (or `dashwise module register`); the dev session SSO setup works without it.
105
107
 
106
108
  ### `dashwise init [slug]` / `dashwise module init [slug]`
107
109
 
@@ -123,7 +125,7 @@ Flags:
123
125
  - `--no-provision` — Skip the `POST /api/installations/dev` step (HH-CC-LDI-6). Local scaffold is still written, but the module won't have a backing dev installation — `dashwise dev` data-plane calls will fail with `UnauthenticatedError` until you re-run `dashwise init --force` or (once it ships) `dashwise module provision-dev-install`.
124
126
  - `--deploy-target railway|vercel|fly` (HH-CC-P1-D2) — Emit a platform deploy config file alongside the scaffold (`railway.json`, `vercel.json`, or `fly.toml`). Default: none (clean repo). Pair with `dashwise env export --format <platform>` to push env vars after provisioning. See [`docs/deploying.md`](../../docs/deploying.md) for the full deploy walkthrough.
125
127
 
126
- **Provisioning behavior (HH-CC-LDI-6):** by default, after the local scaffold is written, `dashwise init` provisions a real local-dev installation by calling `POST /api/installations/dev`. The returned runtime token (90-day JWT) is cached in `~/.config/dashwise/auth.json#modules[<slug>]` where `@dashai/sdk`'s `createDevClient({ moduleSlug })` reads it on every dev-server boot. Provisioning failures are surfaced as warnings; the local scaffold remains usable so you can retry later.
128
+ **Provisioning behavior (HH-CC-LDI-6):** by default, after the local scaffold is written, `dashwise init` provisions a real local-dev installation by calling `POST /api/installations/dev`. The returned scoped `dwk_` API key (default 90-day expiry) is cached in `~/.config/dashwise/auth.json#modules[<slug>]` where `@dashai/sdk`'s `createDevClient({ moduleSlug })` reads it on every dev-server boot. On a `kind='local'` install the key grants self-access to your own tables, so a table you create is instantly queryable. Provisioning failures are surfaced as warnings; the local scaffold remains usable so you can retry later.
127
129
 
128
130
  ### `dashwise module connect-git <repo-url>`
129
131
 
@@ -195,7 +197,7 @@ Tear down the local-dev installation for the current module. Calls `DELETE /api/
195
197
  dashwise module destroy-dev-install
196
198
  ```
197
199
 
198
- Local `module.json` is preserved — only the cached runtime token + install id get cleared. Re-run `dashwise init <slug> --force` to provision a fresh install.
200
+ Local `module.json` is preserved — only the cached API key + install id get cleared. Re-run `dashwise init <slug> --force` to provision a fresh install.
199
201
 
200
202
  Flags:
201
203
  - `--dir <path>` — Project root (default: current directory).
@@ -209,7 +211,7 @@ Errors map to user-friendly messages: `INSTALLATION_NOT_FOUND` (already destroye
209
211
  Destroy + re-provision the local-dev install in a single command. Useful for:
210
212
 
211
213
  - Nuking accumulated test data
212
- - Refreshing a compromised runtime token
214
+ - Refreshing a compromised API key
213
215
  - Recovering from a wedged schema state
214
216
 
215
217
  ```bash
@@ -297,7 +299,7 @@ Flags:
297
299
  - `--json` — Compact JSON output (no pretty-print).
298
300
  - `--dir <path>` — Project root (default: current directory).
299
301
 
300
- Pre-reqs: `dashwise module generate` must have run (the SDK reads `node_modules/@dashai/generated`). `DASHWISE_API_URL`, `DASHWISE_INSTALLATION_ID`, `DASHWISE_API_TOKEN` must be set (the scaffold's `.env.local` is loaded automatically).
302
+ Pre-reqs: `dashwise module generate` must have run (the SDK reads `node_modules/@dashai/generated`). `DASHWISE_API_URL` + `DASHWISE_API_KEY` must be set (the scaffold's `.env.local` is loaded automatically; `dashwise dev` injects both).
301
303
 
302
304
  ### `dashwise pull` (HH-CC-P1-E2)
303
305
 
@@ -317,36 +319,50 @@ Flags:
317
319
 
318
320
  ### `dashwise api-keys list|create|revoke` (HH-CC-P1-C4)
319
321
 
320
- Mint long-lived production credentials for a `production` install. Used by self-hosted deploys (Railway / Vercel / Fly / your own infra) instead of the per-CLI-session runtime token. The key format is `dwk_<43-char-base64url>`; the backend stores only a `sha256` hash so the plaintext is shown exactly once at `create`.
322
+ Mint long-lived credentials for an install. Used by self-hosted deploys (Railway / Vercel / Fly / your own infra). Same `dwk_` key kind `dashwise init` mints for local dev, but you mint a separate one per environment. The key format is `dwk_<43-char-base64url>`; the backend stores only a `sha256` hash so the plaintext is shown exactly once at `create`.
321
323
 
322
324
  ```sh
323
- # Mint a key. Plaintext appears on stdout — copy it now.
325
+ # Mint a full-access key. Plaintext appears on stdout — copy it now.
324
326
  dashwise api-keys create production
325
327
 
326
328
  # Script-friendly: capture the raw key with no other chatter.
327
329
  KEY=$(dashwise api-keys create ci --raw-only)
328
330
 
329
- # List all keys for the current install.
331
+ # Least-privilege (scoped) key any --grant flips it to default-deny.
332
+ dashwise api-keys create readonly --grant module:tasks:read
333
+ dashwise api-keys create writer \
334
+ --grant module:tasks:read,create \
335
+ --grant datahub:table=12:read
336
+
337
+ # Expire the key after N days.
338
+ dashwise api-keys create temp --grant module:tasks:read --expires 30
339
+
340
+ # List all keys for the current install (shows each key's grants).
330
341
  dashwise api-keys list
331
342
 
332
343
  # Revoke. Irreversible. Effective on the next request.
333
344
  dashwise api-keys revoke <key-id>
334
345
  ```
335
346
 
347
+ Create flags:
348
+ - `--grant <spec>` (repeatable) — `module:<tableSlug>:<ops>`, `datahub:table=<id>:<ops>`, `datahub:app=<id>:<ops>`, or `datahub:workspace:<ops>`, where `<ops>` is a comma list of `create,read,update,delete`. Any grant mints a scoped (default-deny) key; minting scoped keys (or any key on a non-local install) requires workspace-admin.
349
+ - `--expires <days>` — days until the key expires (default: never).
350
+ - `--raw-only` — print only the plaintext key on stdout (for shell capture).
351
+
336
352
  The install is resolved from the cached entry in `~/.config/dashwise/auth.json#modules[<slug>]` (keyed by `module.json#module.slug`). Run from inside the module directory, or pass `--dir <path>`.
337
353
 
338
354
  Pairs with `dashwise env export --key dwk_...` to push the key into a deploy platform without ever copy-pasting it through a shell history.
339
355
 
340
356
  ### `dashwise env export` (HH-CC-P1-D1)
341
357
 
342
- Emit the four env vars every self-hosted deploy needs:
358
+ Emit the Phase 2 two-var contract every self-hosted deploy needs:
343
359
 
344
360
  | Var | Source |
345
361
  |---|---|
346
362
  | `DASHWISE_API_URL` | CLI config (`apiUrl`). |
347
- | `NEXT_PUBLIC_DASHWISE_API_URL` | Same. |
348
- | `DASHWISE_INSTALLATION_ID` | `~/.config/dashwise/auth.json#modules[<slug>].installationId` (the dev install cached at `dashwise init`). |
349
- | `DASHWISE_API_KEY` | Placeholder by default; pass `--key dwk_...` to inline an existing key, or `--new-key <name>` to mint one in-flight. |
363
+ | `DASHWISE_API_KEY` | Placeholder by default; pass `--key dwk_...` to inline an existing key, or `--new-key <name>` to mint one in-flight. Resolves the install server-side. |
364
+
365
+ `DASHWISE_INSTALLATION_ID` is NOT emitted (the key binds the install) and `NEXT_PUBLIC_DASHWISE_API_URL` is NOT emitted (the scaffold's `next.config` derives it from `DASHWISE_API_URL`).
350
366
 
351
367
  Five output formats — `env`, `json`, `railway`, `vercel`, `fly`. The platform-specific ones wrap each var in the matching CLI invocation (`railway variables set …`, `vercel env add …`, `fly secrets set …`) so the output is directly pipeable into `sh`.
352
368
 
@@ -369,6 +385,22 @@ Flags:
369
385
  - `--new-key <name>` — Mint a new API key and inline its plaintext.
370
386
  - `--dir <path>` — Project root.
371
387
 
388
+ ### `dashwise origins list|add|remove`
389
+
390
+ Manage the current module install's SSO callback-origin allowlist. The scaffold's `/api/auth/sign-in` route sends its own public origin as the SSO `callback_url`; the backend only accepts an origin that's on the install's allowlist. `kind='local'` installs (what `dashwise init` provisions) allow `http://localhost:<any-port>` implicitly, so **local dev needs no config**. A deployed custom domain must be added here.
391
+
392
+ ```sh
393
+ dashwise origins list # show the allowlist
394
+ dashwise origins add https://app.example.com # add a deployed domain (https:// for non-localhost)
395
+ dashwise origins remove https://app.example.com
396
+ ```
397
+
398
+ Workspace-admin operation (uses your CLI login bearer). The install id is resolved from `~/.config/dashwise/auth.json#modules[<slug>]` — you never type it. `add` / `remove` do a client-side read-modify-write against the backend's `GET` (read) + `PUT` (full-replace) endpoints.
399
+
400
+ Flags:
401
+ - `--dir <path>` — Project root.
402
+ - `--json` (list only) — Emit a JSON array of origins.
403
+
372
404
  ### `dashwise module query-log`
373
405
 
374
406
  Read the per-module SDK query log (each cross-module / `.execute()` / `.stream()` / `.explain()` dispatch writes a row to `module_query_log` server-side). Workspace-member-scoped on the backend.
@@ -419,7 +451,7 @@ Flags:
419
451
 
420
452
  Schema-mutation commands for the current module — add or drop tables, add/remove/retype fields in the dev installation. Each command:
421
453
 
422
- 1. Calls the LDI-4 REST endpoint at `/api/installations/:id/schema/...` using the runtime token cached by `dashwise init` (LDI-6).
454
+ 1. Calls the LDI-4 REST endpoint at `/api/installations/:id/schema/...` using the scoped `dwk_` API key cached by `dashwise init` (on a `kind='local'` install the backend allows the developer to mutate their own schema; C3).
423
455
  2. Asks the backend to apply the change (manifest + physical Postgres tables update atomically server-side).
424
456
  3. Calls `GET /api/installations/:id/schema` to fetch the new manifest.
425
457
  4. Writes the response to local `module.json`.
@@ -507,18 +539,47 @@ Errors: 404 `TABLE_NOT_FOUND` / `FIELD_NOT_FOUND`.
507
539
 
508
540
  ### `dashwise deps`
509
541
 
510
- Manage cross-module dependencies declared in `module.json#dependencies`.
542
+ Manage cross-module dependencies both the declaration (in `module.json#dependencies`) and the **connect lifecycle** (whether each declared dep is actually wired to a provider installation).
511
543
 
512
- **`dashwise deps add <provider-slug>`**Add a dep. Interactive in a TTY (fetches the provider's published `exposes` block, multi-select tables + fields, prompts for the required `purpose` string). Non-interactive when `--table` + `--fields` + `--purpose` are all passed.
544
+ #### Park-not-rejecthow deps connect
513
545
 
514
- Flags: `--table <slug>`, `--fields <comma-list>`, `--purpose <text>`, `--version <semver-range>`, `--dir <path>`.
546
+ A dependency is *declared* in your manifest but only *connected* once a compatible provider is installed in the workspace and bound. When a dep can't bind — no provider installed yet, a version mismatch, a table/action the provider doesn't expose — the backend **parks** it (`status='unbound'`) instead of failing the install. Runtime reads against a parked dep return `DEP_UNBOUND` (the SDK surfaces this as `DepUnboundError` so your app can render a "connect your provider" state).
515
547
 
516
- **`dashwise deps list`** Print the local manifest's declared deps as column-aligned text (provider slug, version range, tables, purpose). `--json` for piping.
548
+ **Both install orders work.** You can install the consumer first (its deps park until the provider shows up) or the provider first (the consumer's deps resolve immediately at install). A parked dep auto-connects when its provider is later installed; you can also connect it by hand with `dashwise deps bind`.
517
549
 
518
- **`dashwise deps remove <provider-slug>`**Drop a dep entry from the manifest. Honours `--table <slug>` to remove a single table from a multi-table dep without dropping the whole entry.
550
+ **`dashwise deps`** (no subcommand)Show the **live connect status** of every declared dep on your installation: a `resolved` / `unbound` chip, the version range, the bound provider's install id + version (when resolved), and the park reason (when unbound). This is the backend's per-installation truth the thing that decides whether a runtime dep read succeeds or 409s. Requires a provisioned dev install (`dashwise init`). Workspace-member. `--json` for piping.
551
+
552
+ ```
553
+ $ dashwise deps
554
+ ℹ Cross-module dependencies for my-app (2 declared, 1 unbound):
555
+
556
+ ● resolved crm@^1.2.0
557
+ provider: install #88 @ v1.3.0
558
+
559
+ ○ unbound billing@^2.0.0
560
+ reason: MISSING_DEPENDENCY
561
+
562
+ ℹ Connect a parked dep with `dashwise deps bind <provider-slug>` once its provider is installed.
563
+ ```
564
+
565
+ **`dashwise deps add <provider-slug>`** — Add a dep to `module.json`. Interactive in a TTY (fetches the provider's published `exposes` block, multi-select tables + fields, prompts for the required `purpose` string); non-interactive when `--table` + `--fields` + `--purpose` are all passed. You can declare a dependency on an **unpublished same-workspace provider** — the backend resolves it against the provider's draft manifest; the CLI surfaces whatever answer the backend gives (it doesn't pre-validate beyond the manifest's schema shape). After writing the manifest, `deps add` shows the resulting **resolve / park status** for the new dep (the manifest is reconciled server-side on the next push, e.g. via `dashwise dev`).
566
+
567
+ Flags: `--table <slug>`, `--fields <comma-list>`, `--purpose <text>`, `--version <semver-range>`, `-y/--yes`, `--dir <path>`.
568
+
569
+ **`dashwise deps bind <provider-slug> [--installation <id>]`** — (Re-)connect a parked dependency to its provider installation. Re-runs single-dep resolution and snapshots the contract; on success, runtime reads succeed. If it can't bind, the command exits 1 with the **park reason** (`MISSING_DEPENDENCY`, `DEPENDENCY_VERSION_MISMATCH`, `EXPOSES_MISSING`, `EXPOSES_PRIVATE`, `ACTION_NOT_EXPOSED`, `PROVIDER_NOT_PRODUCTION`, …) and the dep stays unbound. Pass `--installation <id>` to disambiguate when the workspace has more than one candidate install of the provider. Workspace-admin.
570
+
571
+ **`dashwise deps unbind <provider-slug>`** — Disconnect a bound dependency: NULLs the provider ref + contract snapshot and parks the dep (reason `MANUALLY_UNBOUND`). Runtime reads then return `DEP_UNBOUND` until you bind it again. Workspace-admin.
572
+
573
+ **`dashwise deps list`** — Print the *local manifest's* declared deps as column-aligned text (provider slug, version range, tables, purpose). Offline; reads `module.json` only (contrast with the bare `dashwise deps`, which shows live backend connect status). `--json` for piping.
574
+
575
+ **`dashwise deps remove <provider-slug>`** — Drop a dep entry from the manifest (local edit).
519
576
 
520
577
  **`dashwise deps check`** — Verify each declared dep still resolves against a currently-published provider (online command; requires backend reachability). Exits 1 on drift. Reported codes: `PROVIDER_NOT_FOUND`, `NO_MATCHING_VERSION`, `FIELD_NOT_EXPOSED`, `OP_NOT_ALLOWED`. `--json` available for CI integrations.
521
578
 
579
+ > **`deps` vs `deps list` vs `deps check`:** bare `dashwise deps` reads the **backend's live connect state** for your install (resolved/unbound). `deps list` reads your **local manifest** offline. `deps check` probes **published providers** for manifest drift. Use `deps` to answer "will my cross-module reads work right now?"; `bind` / `unbind` to change that.
580
+
581
+ Auth + install resolution for the online subcommands (`deps`, `bind`, `unbind`) mirror `dashwise origins`: they use your CLI login bearer (bind/unbind need workspace-admin) and resolve the install id from `~/.config/dashwise/auth.json#modules[<slug>]` — you never type it.
582
+
522
583
  ### Module dev loop
523
584
 
524
585
  ```bash