@lotics/cli 0.244.0 → 0.245.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/dist/src/cli.js +2 -2
- package/docs/cli_reference.md +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -52,7 +52,7 @@ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__
|
|
|
52
52
|
var define_LOTICS_KIT_VERSIONS_default;
|
|
53
53
|
var init_define_LOTICS_KIT_VERSIONS = __esm({
|
|
54
54
|
"<define:__LOTICS_KIT_VERSIONS__>"() {
|
|
55
|
-
define_LOTICS_KIT_VERSIONS_default = { runtime: "0.
|
|
55
|
+
define_LOTICS_KIT_VERSIONS_default = { runtime: "0.31.0" };
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
@@ -55352,7 +55352,7 @@ function resultSideEffects(result) {
|
|
|
55352
55352
|
|
|
55353
55353
|
// src/version.ts
|
|
55354
55354
|
init_define_LOTICS_KIT_VERSIONS();
|
|
55355
|
-
var VERSION = "0.
|
|
55355
|
+
var VERSION = "0.245.0";
|
|
55356
55356
|
|
|
55357
55357
|
// src/timezone.ts
|
|
55358
55358
|
init_define_LOTICS_KIT_VERSIONS();
|
package/docs/cli_reference.md
CHANGED
|
@@ -51,7 +51,7 @@ Per-command syntax, flags, contracts, and gotchas for the public `lotics` CLI. S
|
|
|
51
51
|
| `lotics app deploy [--prune] [--prune-invoked <alias>] [-m <message>] [--acknowledge-breaking-api]` | `-m` is OPTIONAL — omitted, the deploy derives the version message from what it pushed. Runs the app's `npm run typecheck` and `npm run build`, tars source + dist, POST /v1/apps/{id}/versions multipart. **One command ships everything.** Before the bundle moves it pushes every binding the project has ahead of the app — an edited workflow body or declaration, edited agent prose, a changed query — through `set_app_query`, then `set_app_workflow`, then `set_app_agent`, and FAILS the release if any push is refused. That order is required: an agent declares the query and workflow aliases it may call, so pushing it before its own new query is refused. A workflow's `description` rides that push and is compared against the recorded baseline, not the live app — it lives on the workflow ROW, which `getApp` does not carry. Editing `lotics.agents.<alias>.inputs`/`outputs` is pushed the same way, and only those two fields (`set_app_agent` merges, so anything the manifest does not model is left untouched). The deploy never AUTHORS a binding itself, and each push carries the fingerprint the project last saw live (`lotics.synced`), so a stale checkout is refused rather than overwriting another author's edit. It regenerates the `.lotics/*.d.ts` companions and `.lotics/app_fields.ts` before building — the build INLINES the latter — and then typechecks against them; a `package.json` with no `typecheck` script is warned about, never passed in silence. `lotics app check` reports the same set without pushing; neither has a `--strict`. The aliases the version RECORDS as called — the set `remove_app_workflow` / `remove_app_query` / `remove_app_agent` consult to refuse unbinding one the served version still reaches — are read by the SERVER out of the uploaded source archive, never reported by the client that is also what unbinds. **After the ship it unbinds what the GENERATOR retired, unasked**: an alias `.lotics/generated/manifest.json` records as retired by `app regenerate` that this checkout bound and no longer declares — the same transition rule as below, so an alias another checkout bound, or one declared again by hand, is never touched. Every unbind — this one and `--prune`'s — carries the fingerprint the project last saw live, so an alias rewritten since (by chat, by another checkout) is refused and left bound, and a retired one is then dropped from the record, named. Beside that fingerprint alone the invocation guard is lifted, since the recorded runs are then the generated app's own calls to a body nobody rewrote; one that will not unbind for any other reason stays recorded and the next deploy retries it. **Beyond those it reports two things and removes nothing.** Aliases the source CALLS that nothing bound. And bindings this project has RETIRED, which is two transitions, each with its own evidence: an alias the previous bundle called and this one does not (`package.json#lotics.bundle_calls`, recorded by each deploy), and an alias still bound live that this checkout holds a `lotics.synced.<kind>.<alias>` baseline for and no longer DECLARES. Neither piece of evidence present is an alias this checkout has never seen — bound by chat, by another operator, or after this tree was pulled — which is not a removal and is never a prune target. With no `bundle_calls` the first transition reports nothing; that deploy records it and the next can compare. The `bundle_calls` baseline is STICKY: it advances only once the call-site half is settled, so the `--prune` a warning names still finds the transition on a later run. **`--prune` unbinds them, and only when passed.** It runs AFTER the version is live, because the removal tools refuse an alias the SERVED version still declares. When the source computes an alias at run time, the call-site half is left in place with a warning (the scan cannot tell which binding that call reaches); the declaration-removed half is unbound anyway, since deleting a declaration here states the removal outright. A removal DELETES the local declaration too — `package.json#lotics.<kind>.<alias>` and its `synced` baseline — or the next plain deploy would push it straight back; what it deleted is written to `.lotics/pruned/<kind>/<alias>.json` and the ✓ names that file plus the `set` verb that re-binds it. (These trees are never committed, so `lotics app pull --from-version <apv_…>` is the only other route back.) The generated companions are then regenerated from the narrowed manifest; a table named ONLY by a pruned query leaves `F`/`OPT`, which is reported — a workflow that still writes it keeps it, since the codegen set is the queries' tables plus every bound workflow's own `table_ids`. A binding that will not unbind is reported and never fails the release, and neither does a local write that fails: the version is already live, and the report names which aliases were unbound server-side. The server refuses to unbind a WORKFLOW this workspace has actually run — a recorded execution means a caller the source cannot name — printed as `✗ could not unbind …` with the date it last ran. **`--prune-invoked <alias>` lifts that guard for the alias you name** (repeatable, comma-separated; needs `--prune`, and is refused as a no-op without it), keeping the prune's report, undo file and manifest cleanup that a raw `lotics run remove_app_workflow` loses. Finally it refreshes `.lotics/workflows/<alias>.globals.d.ts` for any alias whose `// lotics:declaration` stamp says this deploy moved its declaration — from the manifest, re-wrapping the SAME on-disk body, so local edits survive. Non-fatal: the release has shipped, and stale types never fail it. **`--acknowledge-breaking-api`** carries this write out even though it breaks what the app's published API promises, snapshotting the broken contract as a new version; without it such a write is refused and every breaking change is named (see `app api`). It rides every write the release makes — the bindings pushed ahead of the bundle, the version itself, and a `--prune`'s unbinds — because the answer is about the RELEASE. |
|
|
52
52
|
| `lotics app versions [app_id]` | `GET /v1/apps/{id}/versions` — print deploy history newest-first (version number, timestamp, deployer name, build status, the `-m` message; `*` marks the currently-served version). app_id from the local manifest, or pass one to inspect any app without pulling it. Server-side it is the app's owner or an org admin, the same gate deploy and source download take — a `manager` share on somebody else's app does not reach it. Answers "what shipped, when, by whom" — e.g. whether a fix was live at an incident's time. Title → stderr, table → stdout (pipeable). |
|
|
53
53
|
| `lotics app upgrade [app_id] [--connection <alias>=<cac_id> ...]` | `POST /v1/apps/{id}/upgrade` — apply the latest version of the package this app was COPIED from. A copy records its provenance (`apps.origin`: package, version, app alias and the `bind` it was made under) and this is the only thing that reads it — a hand-built app, or one copied before the column existed, has no package to offer one and answers 400. Run it once per app: a package's apps each carry their own provenance. **The schema is additive** — fields, options and views the new version declares are created under the recorded bind, so they land on the same tables the copy did; nothing is renamed, retyped or deleted, and a field the new version stopped declaring keeps its column and its data and is REPORTED. **An artifact is replaced only while it is still byte-for-byte what was delivered**: a workflow or agent you have edited here is kept as it is and named, so the offer is partial by design and every part it declined to touch is printed. Queries are replaced outright (generated from the contract, no edit to lose) and only a knowledge doc the new version ADDS is created. The app is then redeployed from the new version's prebuilt dist — **nothing local is read or sent**, so a checkout on this machine is behind afterwards and the report ends at `lotics app pull <app_id>`. app_id from the local manifest, or pass one to upgrade any app without pulling it. **Already on the latest version prints that one line and exits 0** — it is a refusal before the first write, not a failure, and re-applying the version it is on would re-stamp your edits as delivered. Every other refusal (an unpublished package, a contract that no longer validates, a bind the new version broke) is a package that cannot be applied: the app is untouched, the server's sentence is printed, and the exit is 1. Anything else — no provenance to read, not an admin, no such app — exits 1. Admin-only. Audited as `app.upgrade`. **`--acknowledge-breaking-api`** carries this write out even though it breaks what the app's published API promises, snapshotting the broken contract as a new version; without it such a write is refused and every breaking change is named (see `app api`). **`--connection <alias>=<cac_id>`** (repeatable) names the account a connection pushes through, where you can use more than one account of its provider — two accounts of one service are two sets of books, so the refusal that asks for it names each and nothing is ever picked for you; the account must be this workspace's, of the provider the model declares, and one you can use. |
|
|
54
|
-
| `lotics app api publish [app_id]` \| `unpublish` \| `status` \| `spec [-o <file.json>]` | **The app's API — what its declared queries, workflows and agents promise to a caller OUTSIDE it** (a customer's own site or server, an integration, another system). An agent is in the contract as its alias plus whichever of `inputs` / `outputs` it declares, and the ABSENCE of either half is part of the promise: no `inputs` means a run accepts any object, no `outputs` means it answers free text. So declaring an input schema where there was none is breaking (a caller's own keys start being refused), dropping one is additive, and declaring or dropping `outputs` is breaking either way, because the result changes kind. `publish` (`POST /v1/apps/{id}/api/publish`) snapshots that promise as a numbered contract version and prints the version, when it was taken, and every warning about what the published surface exposes — a query anyone holding the public link can reach, a field an owner may not have meant to hand out. It is REFUSED (400) while a query does not name the columns it returns: those field names come from the table and would change under the consumer whenever the table does, so they are not the app's to promise — the refusal names each such alias and the `project` that fixes it, and nothing is written. **From the publish onward a manifest write is a release**: additive changes re-snapshot silently, and one that breaks what is promised is refused with every breaking change named, unless the write carries the acknowledgment (`--acknowledge-breaking-api` on `app deploy` / `app query set` / `app workflow set` / `app agent set` / `app upgrade`). There is no `app agent remove` verb, so removing a published agent is `app deploy --prune --acknowledge-breaking-api` once the bundle stops naming it. `unpublish` ends the promise; the superseded snapshot stays, so a later publish continues the numbering rather than reusing a version. `status` says whether one is published and which version its callers hold. `spec` prints the OpenAPI 3.1 document — rendered by the server from the SNAPSHOT rather than from the manifest, so it describes what the app has promised — to stdout, or to the file `-o` names; 404 while nothing is published. It carries one operation per query, per workflow and per agent, plus, whenever the contract holds an agent at all, the single `GET /v1/apps/{id}/agent-runs/{run_id}` where every run's result is read: an agent's own operation answers a `text/event-stream`, so its `output` shape is stated there and nowhere else. The app_id comes from the local manifest, or pass one to act on any app in the workspace. **`--json` answers `publish` / `unpublish` / `status` with one object on stdout and nothing else**, every warning carried in it rather than printed away; `spec` already prints a document there. **Who may run which**: starting and ending the promise is an organization ADMIN's — what an app hands outside the workspace is the same capability that declared it. `status` is the app's AUTHOR's (its owner, or an admin): whether their own app publishes anything is theirs to see. `spec` is reachable by whoever may USE the app, which on a publicly-shared app is anyone holding the link — it is the document a consumer generates their client from. |
|
|
54
|
+
| `lotics app api publish [app_id]` \| `unpublish` \| `status` \| `spec [-o <file.json>]` | **The app's API — what its declared queries, workflows and agents promise to a caller OUTSIDE it** (a customer's own site or server, an integration, another system). An agent is in the contract as its alias plus whichever of `inputs` / `outputs` it declares, and the ABSENCE of either half is part of the promise: no `inputs` means a run accepts any object, no `outputs` means it answers free text. So declaring an input schema where there was none is breaking (a caller's own keys start being refused), dropping one is additive, and declaring or dropping `outputs` is breaking either way, because the result changes kind. `publish` (`POST /v1/apps/{id}/api/publish`) snapshots that promise as a numbered contract version and prints the version, when it was taken, and every warning about what the published surface exposes — a query anyone holding the public link can reach, a field an owner may not have meant to hand out. It is REFUSED (400) while a query does not name the columns it returns: those field names come from the table and would change under the consumer whenever the table does, so they are not the app's to promise — the refusal names each such alias and the `project` that fixes it, and nothing is written. **From the publish onward a manifest write is a release**: additive changes re-snapshot silently, and one that breaks what is promised is refused with every breaking change named, unless the write carries the acknowledgment (`--acknowledge-breaking-api` on `app deploy` / `app query set` / `app workflow set` / `app agent set` / `app upgrade`). There is no `app agent remove` verb, so removing a published agent is `app deploy --prune --acknowledge-breaking-api` once the bundle stops naming it. `unpublish` ends the promise; the superseded snapshot stays, so a later publish continues the numbering rather than reusing a version. `status` says whether one is published and which version its callers hold. `spec` prints the OpenAPI 3.1 document — rendered by the server from the SNAPSHOT rather than from the manifest, so it describes what the app has promised — to stdout, or to the file `-o` names; 404 while nothing is published. It carries one operation per query, per workflow and per agent, plus, whenever the contract holds an agent at all, the single `GET /v1/apps/{id}/agent-runs/{run_id}` where every run's result is read: an agent's own operation answers a `text/event-stream`, so its `output` shape is stated there and nowhere else. Every `description` the contract declares — on an input, a param, an output, and every nested field and array item — is that node's `description` in the document, so a generated client carries what each value means. The app_id comes from the local manifest, or pass one to act on any app in the workspace. **`--json` answers `publish` / `unpublish` / `status` with one object on stdout and nothing else**, every warning carried in it rather than printed away; `spec` already prints a document there. **Who may run which**: starting and ending the promise is an organization ADMIN's — what an app hands outside the workspace is the same capability that declared it. `status` is the app's AUTHOR's (its owner, or an admin): whether their own app publishes anything is theirs to see. `spec` is reachable by whoever may USE the app, which on a publicly-shared app is anyone holding the link — it is the document a consumer generates their client from. |
|
|
55
55
|
| `lotics app codegen [path]` | Regenerate `.lotics/*` from the manifest + workspace schema **without a deploy**. The three `.d.ts` companions (`app_{workflows,queries,agents}.d.ts`) are always rewritten (synchronous, no network). When credentials resolve, also rewrites the **runtime** `.lotics/app_fields.ts` — a real `.ts` exporting `F` (table→field→`"fld_…"`), `OPT` (table→select-field→option→`"opt_…"`), `TBL` (table→`"tbl_…"`) and `GRP` (member group→`"grp_…"`) keyed by display-name aliases, for every table the app's queries READ plus every table its bound workflows WRITE (each binding's recorded `table_ids`, so a table no screen reads is still addressable by alias and a rename fails `tsc` instead of the body), plus every member group in the organization — which one a screen names is not knowable from the manifest, and a `GRP` narrowed by a failed read is a map that is wrong rather than absent, so a failure writes nothing. **There is one form, and that is what makes a starter's source portable**: the keys are slugified DISPLAY NAMES and a starter carries its labels verbatim, so running codegen in a copy's own workspace emits the same keys pointing at that workspace's ids — no binding fetched at load, no prebuilt bundle to keep in step. Beside it, `.lotics/app_siblings.ts` exports `APP` — every app the workspace's model declares, by the plan's alias for it (`APP.<alias>` → `"app_…"`), read off the workspace's model binding — so a hand-written app opens a sibling as `openApp(APP.<alias>, route)` and holds no `app_` id; an app archived here is absent, so a call naming it fails `tsc`. The binding is an admin's read: a key that cannot read it keeps the file on disk and says so. Also refreshes each bound workflow's `.lotics/workflows/<alias>.globals.d.ts` + re-wraps its EXISTING `src/workflows/<alias>.ts` body in the current envelope (strips + re-wraps; never re-fetches the body, so local edits survive). **`.lotics/` is reconciled to the manifest, not merely added to** — a `<alias>.globals.d.ts` whose alias the manifest no longer declares is DELETED. Only that exact filename shape is removed; anything else in the directory is left alone. The reconcile runs before the credential branch, so it happens offline too. The authored counterpart is never deleted — a `src/workflows/<alias>.ts` the manifest does not declare is NAMED instead (`check` and `set` both take their alias set from the manifest, so editing an undeclared body is a silent no-op). A getApp / binding / schema / dts-fetch failure is non-fatal (warns, keeps the last-generated files). **Re-silvers `package.json#lotics.agents`** from the live app row whenever its `inputs`/`outputs` disagree, then rewrites the agent `.d.ts` from the refreshed block: that block is a mirror AND the offline seed for `useAgentRun` typings, so a stale copy types the app against an agent that does not exist. The write is surgical and order-preserving, so it changes only the fields that actually differ. A hand edit to that block is therefore reverted — it never changed the agent anyway; to change one, `set_app_agent`. **Types are written for every DECLARED alias, body file or not** — the dts is rendered from the declaration, which is the whole point of the declare → codegen → write → check loop — and codegen NAMES each alias it wrote types for without a body, with the next step. It used to return early on a missing or blank `src/workflows/<alias>.ts` and say nothing, which left `workflow check` pointing at `app workflow pull` (which writes nothing for an alias the server has never bound) on one path and at `app codegen` — the command that had just declined to write them — on the other. **A body whose helper sits ABOVE the `__workflow` wrapper is refused by name and line** rather than wrapped a second time: the strip peels a wrapper only when it is the first line after the header, so a top-level declaration between the two used to leave the whole file read as the body and the re-wrap nested an envelope per run. Move the helper inside the wrapper — the body is one expression sequence. |
|
|
56
56
|
| `lotics app kit <path-to-packages/app-runtime>` \| `lotics app kit --published` | **The app's ONE range, `@lotics/app-runtime`, from a CHECKOUT, or back to the range it listed before**, so a runtime change is proven on a real app before it is published. Run inside the app directory; the path names `packages/app-runtime` in your clone (a kit change reaches an app through the runtime that depends on it — `LOTICS_UI_SRC` links the kit's source into `app dev`). It runs the runtime's own `npm run build`, `npm pack`s it into the app's `.lotics/kit/` (replacing any earlier pack, so the directory holds one tarball), removes the installed copy and the lockfile's entry for it, installs by file specifier, and then **proves the install**: the first non-dot file of the directory the package's own `package.json#files` publishes is hashed on both sides, and a mismatch — or an absence — is a refusal naming the file. That proof is the point. A repack under the SAME name is the normal case (a working copy's version does not move between builds) and the lockfile pins the first tarball's integrity, so on some npm versions the install is a no-op that reports success while the app keeps building against an hour-old kit; nothing else in the loop can tell. **Either way the rest of the manifest follows the runtime**: `@lotics/ui`, where the app lists it, moves to the range the runtime depends on — two ranges are two copies of the kit — and an app still on `@lotics/app-sdk` (the package the SDK was before it moved into the runtime) is MIGRATED: every `@lotics/app-sdk` and `@lotics/app-sdk/router` specifier under `src/` and in the Vite config becomes `@lotics/app-runtime/sdk` and `@lotics/app-runtime/router`, the dependency is dropped, and the summary names each file rewritten. **Refusals, all before anything is written**: a directory that is not a Lotics app (no `package.json#lotics.workspace_id`); a path that is not `@lotics/app-runtime`; a runtime that declares no `@lotics/ui` range while the app lists the kit; `--published` given a path; a build that failed, named with npm's own output tail. **It records what it did in `package.json#lotics.kit.@lotics/app-runtime`** (`version`, `tarball`, the `source` checkout, `packed_at`), which is what `lotics app check` warns about and what `lotics app deploy` REFUSES on unless you pass `--allow-local-kit` — `.lotics/` is excluded from the source archive a deploy uploads, so an app whose manifest names `file:.lotics/kit/…` ships a bundle that runs and a source tree the next clone cannot install. The record also keeps what the app listed before the FIRST checkout went in (`replaced` — a repack keeps it), and `--published` undoes the trial: the runtime and the kit go back to exactly those specs, so a kit trial never moves an app to another line on its way out; an app with no such record (never given a checkout, or given one by a CLI that recorded none, which the run says) moves to `^<latest>` from the registry and the kit to the range that published version depends on. Either way the tarball and the record are deleted, the lock entry and the installed copy are cleared, and a plain install resolves the published package. Needs no credential — npm, the filesystem and (for `--published` with no prior range) the registry. |
|
|
57
57
|
| `lotics setup <apg_id \| model.json> [--email <addr>] [--json]` | **The whole first run, in one command.** Creates an account when this machine has no credential (the same call `auth signup` makes — `--name` and `--timezone` apply), then fills its workspace, then prints the one-time sign-in link. **When that email already has an account it hands over to the `lotics auth login` flow** — it prints the sign-in page to open and the code it must show, and **exits 1 having created nothing**; the person presses Confirm and runs the same command again, which collects the key and carries on into the copy or the model. (`--wait` holds the terminal through the Confirm instead, finishing in one command.) The re-run is not refused for naming an `--email` it is now signed in as — that address IS the account it holds, not a second one. **The argument decides which of the two forms this is, by SHAPE**: a `*.json` file is a workspace MODEL — in either of ITS two forms, spelled out or `{"from": "<preset-slug>", …}` — and anything else is a package id copied through `library init`. The suffix decides it alone — asking the filesystem would answer a long library id with `ENAMETOOLONG` instead of with a verdict — and a model is checked OFFLINE before an account is created, because a file with a typo in it must not leave an organization behind. The model form creates no apps, so its sign-in link lands on the first table it made. It sends no `adopt`: an entity whose `label` already names a table in the workspace is REFUSED with every collision named, and the refusal adds the line the server cannot — `lotics scaffold apply <model.json>`, the verb that adds to the workspace you already have. It exists because the two-command form has a seam where the FIRST command exists only to produce a credential for the second, and a caller pasting a prompt has to get both right. **`--email` is only for creating an account**: with a credential already resolvable it is REFUSED rather than obeyed, because the two can name different organizations and preferring either one silently copies a package into an org the caller did not name — the message says how to do each thing on purpose. Without it, `setup` copies into the account you already have and is a pure alias for `library init`. A path positional is accepted and IGNORED with a warning — `setup` writes nothing to disk — so a prompt that passes one still runs. **`--json` prints one object on stdout and nothing else** — `organization_id`, `workspace_id`, `app_ids` (alias → id), `apps` (each app's `version_number`, or its `error`), `signin_url`, and `created` — which NAMES what landed (`tables`, `templates` and `knowledge_docs` are alias arrays; `sample_records` is a row count, since rows are not named things). Aliases rather than counts because the next question is about a particular artifact: a copied template carries the publisher's wording and a copied knowledge doc describes how they work, so "which of these should be mine?" is the conversation a copy starts, and a count cannot begin it. **The model form emits `entities`, `roles`, `record_ids` and `rows_skipped`** in place of `app_ids` / `apps` / `created` — a model creates no apps and nothing named for a copier to review. **The model form also runs the file's `apply` list** — each named package copied in after the tables exist, with that entry's `bind`, in order, stopping at a refusal with everything before it kept — and emits `applied: [{package, apps}]` beside them; **the sign-in link then lands on the FIRST app any applied package created**, falling back to the first table when the model applied none. Plus a `warnings` array carrying everything the prose form would have said out of band — an unbindable knowledge doc, a sign-in link that could not be minted, the publisher's-code disclosure, an app that landed without a version. A warning is never merely silenced: when the command fails with an error before it can emit, the ones it had collected go to stderr alongside it. Reachable with no install: `npx -y @lotics/cli setup …`. |
|