@lotics/cli 0.263.0 → 0.266.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/AGENTS.md CHANGED
@@ -7,14 +7,14 @@ conventions are, and where the traps are.
7
7
  |---|---|
8
8
  | `lotics --help` | The verb inventory (§ COMMANDS) and global flags. The verb LIST is generated and never stale; the prose beside each verb is hand-written, so where it disagrees with `docs/cli_reference.md`, the reference wins. |
9
9
  | `lotics tools` · `lotics tools <name>` | The agent tool registry and one tool's full JSON Schema. |
10
- | `lotics docs` · `lotics docs <area>[/<section>]` | Every reference the packages installed beside the project actually ship — `@lotics/app-runtime`, `@lotics/ui` and the document engines each carry their own, and this index only covers THIS package. Discovered by looking, not by a list, so it reports the installed VERSION of each: a doc always describes the code that is really there. Capped at a page: a doc that does not fit hands back its opening and the addresses into it, so the next call is smaller than the last. |
11
- | `lotics docs model` · `lotics docs model/<section>` | How to write a `model.json` — the file a workspace is built from: its tables, how a row of each is recognised (`records`), and the apps stated over them. Carried inside this CLI at its own version, since it describes this CLI's model checker, and paged like every doc: the first page is the working order and the section addresses. Two forms: `{"from": "<preset-slug>", "variants", "rename", "entities", "rows", "apply"}`, which names a preset by slug and carries only what this business differs by, and the full one, spelled out, for when no preset is the trade: every top-level key, every field type with the config it needs, the row format, the rules, and a worked example. Offline. It also covers the `apply` list — published packages copied in after the model's own tables, each with an optional `bind` onto them — and the `preset` block a published model carries. `lotics scaffold check` then proves the file — offline, except for the one read a `from` file's preset needs — including every branch of a preset merged onto its base; `lotics setup` applies it and refuses a table name the workspace already has, `lotics scaffold apply` adopts that table and adds what is missing, and the WORKSPACE remembers what each alias became, so every later run binds by id and a relabel on either side is a rename it reports rather than a second table it adds. `lotics scaffold export` goes the other way — a workspace printed as one of these files, to edit into another business's model; a starting point, never a source of truth. |
12
- | [docs/building_an_app.md](./docs/building_an_app.md) | The SEQUENCE — scaffold, model, types, queries, workflows, screens, ship — and the deploy-free inner loop. The other references describe contracts; this one is the order they go in and why. Read it once before starting an app. |
13
- | [docs/cli_reference.md](./docs/cli_reference.md) | Per-command contracts, flags, exit codes, and gotchas — the detail `--help` compresses. Read it before hand-building a `set_app_*` payload: several tools REPLACE rather than patch, and a CLI verb already owns the safe assembly. |
10
+ | `lotics docs` · `lotics docs <area>[/<section>]` | This CLI's references, carried inside it so each describes the binary answering. Capped at a page: a doc that does not fit hands back its opening and the addresses into it, so the next call is smaller than the last. A custom-code app's SDK reference is `node_modules/@lotics/app-sdk/AGENTS.md` inside the app. |
11
+ | `lotics docs model` · `lotics docs model/<section>` | How to write a `model.json` — the file a workspace is built from: its tables, how a row of each is recognised (`records`), and the apps stated over them. Every top-level key, every field type with the config it needs, the row format, the rules, and a worked example; complete example models of several trades are listed at `https://lotics.ai/presets/index.json`. `lotics model apply` checks the file (every problem in one run), applies its tables and mints a version of each app; the WORKSPACE remembers what each alias became, so every later apply binds by id and a relabel on either side is a rename it reports rather than a second table it adds. `lotics model pull` goes the other way — the workspace's model, rebuilt from what owns each part. |
12
+ | [docs/building_an_app.md](./docs/building_an_app.md) | The SEQUENCE — clarify, model, apply or build, prove, look — for an app stated in a model and for a custom-code app. Read it once before starting an app. |
13
+ | [docs/cli_reference.md](./docs/cli_reference.md) | Per-command contracts, flags, exit codes, and gotchas — the detail `--help` compresses. |
14
14
  | [docs/data_model.md](./docs/data_model.md) | How tables RELATE — one entity per table and the NAME-OVERLAP probe that says when a split has broken, one vocabulary wherever values are copied between tables, a copy boundary that accounts for every source field, provenance as a link rather than a flag, a declared natural key so find-or-create never compares rendered text, and why derived DEPTH costs more than row count. Separate from building_an_app because every workspace starts with tables and many never get an app. The within-table half (one fact, one column) is stated at `create_table` / `update_table`, where you meet it while deciding. |
15
15
  | [docs/document_templates.md](./docs/document_templates.md) | Generating PDF/Excel/Word/email from reusable templates. |
16
16
  | [docs/knowledge_docs.md](./docs/knowledge_docs.md) | Authoring the workspace facts an agent can't guess; who can read a doc; catalog-then-stage retrieval. |
17
- | [docs/migration.md](./docs/migration.md) | What to DO when a release changes the shape of a project this CLI owns. Read once, when something already on disk no longer matches what the CLI writes — for one, an app built from a model is `app.json` version 2, rendered by `@lotics/app-runtime`. |
17
+ | [docs/migration.md](./docs/migration.md) | What to DO when something an earlier CLI wrote to disk no longer matches what it does — local app projects are gone, and each verb that read one has a replacement. |
18
18
  | [README.md](./README.md) | Install, auth, and worked examples. |
19
19
 
20
20
  ## Two surfaces, and the trap between them
@@ -23,26 +23,25 @@ conventions are, and where the traps are.
23
23
 
24
24
  - **Tools** (`lotics tools`, `lotics run <tool>`) — the *agent tool registry*: what an agent, workflow,
25
25
  or automation may call. Workspace data, templates, knowledge, admin.
26
- - **Commands** (`lotics --help` § COMMANDS) — the CLI's *own verbs*. Auth and org/workspace scoping,
27
- file upload/download/preview, and the whole custom-code app loop (scaffold, pull, deploy, codegen,
28
- dev, and running a bound workflow or agent end to end).
26
+ - **Commands** (`lotics --help` § COMMANDS) — the CLI's *own verbs*: auth and org/workspace scoping,
27
+ file upload and download, and the four that touch a local file for an app — `model apply`,
28
+ `model pull`, `app create --custom` and `app deploy`.
29
29
 
30
30
  Several capabilities exist **only** as commands and appear nowhere in `lotics tools` — downloading a
31
- file and running a bound app agent are the two that most often get mistaken for missing. Concluding
32
- "the platform can't do X" from the tool list alone is a mistake; check both.
31
+ file is the one most often mistaken for missing. Concluding "the platform can't do X" from the tool
32
+ list alone is a mistake; check both.
33
33
 
34
- ⚠️ **`lotics <subcommand> --help` prints the generic top-level help**, except `lotics report --help`
35
- and a bare `lotics auth`, which print their own. It does not describe the subcommand, so an unhelpful
36
- response there is *not* evidence the subcommand is absent. To find out whether something exists, read
37
- `lotics --help` § COMMANDS — the whole section, not a narrow grep.
34
+ **`lotics <verb> --help` prints that verb's entries from § COMMANDS** (`lotics model --help`,
35
+ `lotics file download --help`); `lotics report --help` prints the report frame. To find out whether
36
+ something exists, read `lotics --help` § COMMANDS — the whole section, not a narrow grep.
38
37
 
39
38
  ## Conventions that hold across every command
40
39
 
41
40
  - **Scope is resolved per invocation.** `LOTICS_ORG` / `LOTICS_WORKSPACE` (or `LOTICS_API_KEY`) scope a
42
41
  single call without changing the active org or a directory pin — the safe way to touch one tenant
43
42
  from a shell serving many. Every command that resolves a workspace echoes its target to **stderr**
44
- (`lotics → <org> / <workspace>`); read it back before trusting a write. `file preview <fil_…>` is
45
- the one credentialed command with no echo. Resolution precedence is in README § Organizations.
43
+ (`lotics → <org> / <workspace>`); read it back before trusting a write. Resolution precedence is
44
+ in README § Organizations.
46
45
  - **A machine with no key can still sign in, and the sign-in never blocks you.** `lotics auth
47
46
  login <email>` prints the page a person opens (also mailed) and the code that page must show,
48
47
  records the request, and EXITS. They press Confirm whenever they get to it; the next command that
@@ -65,11 +64,11 @@ response there is *not* evidence the subcommand is absent. To find out whether s
65
64
  profile to remove at all. Nothing is ever revoked on a guess — between two, the destructive one is
66
65
  wrong. `lotics auth whoami` prints the kind, asking the server when the store cannot say.
67
66
  - **A key created in Settings never administers the organization, whatever its access.** The verbs
68
- `docs/cli_reference.md` marks *admin only* split in two under a key: the ones that BUILD inside a workspace
69
- the key reaches — `scaffold apply`, `library init`, `app upgrade`, `workspace doctor`, `scaffold
70
- export`, `field rename` — run as before, while managing people, sharing or ownership, creating or
71
- deleting a workspace, changing workspace settings, setting credit limits, reading the access log
72
- and publishing a starter or an app's API answer `403` and name the remedy: an admin signed in, so
67
+ `docs/cli_reference.md` marks *admin only* split in two under a key: `workspace doctor`, which only
68
+ reads inside a workspace the key reaches, runs as before, while applying or pulling a model
69
+ (`model apply`, `model pull`, `setup`), managing people, sharing or
70
+ ownership, creating or deleting a workspace, changing workspace settings, setting credit limits,
71
+ reading the access log and publishing an app's API answer `403` and name the remedy: an admin signed in, so
73
72
  `lotics auth login <email>` and run it again. A sign-in acts as that person and is refused none of
74
73
  them. Do not retry a `403` with the same credential and do not ask for a wider key — no answer on
75
74
  the key's own screen grants this.
@@ -81,30 +80,33 @@ response there is *not* evidence the subcommand is absent. To find out whether s
81
80
  the generic "Invalid or disabled API key", and that one is generic on purpose, so re-sending it
82
81
  teaches nothing. A `reason` rides on the body for a script to branch on, since the code stays
83
82
  `unauthorized` for every 401.
84
- - **Large payloads bypass `ARG_MAX`** — `lotics run <tool> @args.json` or piped stdin. A leading `@` is
83
+ - **Large payloads bypass `ARG_MAX`** — `lotics run <tool> @args.json`, or piped stdin behind `-`. A leading `@` is
85
84
  unambiguously a file path (JSON args start with `{`).
86
85
  - **stdout is the payload, stderr is the narration.** Progress, status lines, and the target echo go to
87
86
  stderr; the result goes to stdout, so piping stays clean. `--json` switches stdout from the
88
87
  agent-readable summary to the full structured object.
89
88
  - **Every tool is invoked one way — `lotics run <tool>`.** Including the ones that RUN something
90
- (`run_app_workflow`, `run_app_agent`, `run_app_query`). An `app` command exists only for work no
91
- tool call can do: scaffold, build, typecheck, serve, or read and push a local file.
89
+ (`run_app_workflow`, `run_app_agent`, `run_app_query`) and every one that changes an app
90
+ (`set_app_query`, `set_app_workflow`, `set_app_agent`, `update_app`, `rollback_app`, the
91
+ `sandbox_*` tools). A command exists only for work that touches a local file.
92
+ - **Every change to an app mints a version of it, and rolling back is the undo.** An apply, a
93
+ deploy, a single `set_app_*` or `remove_app_*`: each is a new version, and `rollback_app` makes an
94
+ earlier one current again. It restores the app — never a table change or a row a workflow wrote,
95
+ so try a write on a throwaway record.
92
96
  - **Exit codes are assertable, and they report the WORK rather than the call.** `lotics run` exits
93
97
  non-zero when a `run_app_workflow` or `run_app_agent` result's own envelope carries a failed
94
98
  `status` (`error`/`failed`/`cancelled`) — any other tool's top-level status is data and exits 0 —
95
99
  so `lotics run … && next-step` cannot walk past a refused run; `workspace doctor` exits non-zero on
96
100
  findings. An unrecognized status exits 0 — the list is an allowlist of failure, so a status added
97
101
  later never turns a working script red — and a parked run (`awaiting_input`) is not a failure.
98
- - **An app that PUBLISHES an API turns every later manifest write into a release.** `lotics app api
99
- publish` snapshots what the app's queries, workflows and agents promise to callers outside it — a
102
+ - **An app that PUBLISHES an API turns every later binding write into a release.** Publishing
103
+ snapshots what the app's queries, workflows and agents promise to callers outside it — a
100
104
  customer's own site or server, which nobody here can redeploy. From then on an additive change
101
- re-snapshots silently and a breaking one is REFUSED, naming each change;
102
- `--acknowledge-breaking-api` (on `app deploy`, `app query set`, `app workflow set`,
103
- `app agent set`, `app upgrade`) is the answer that carries it out and snapshots the break as a new
104
- contract version. An app that publishes nothing is untouched by any of it.
105
+ re-snapshots silently and a breaking one is REFUSED, naming each change; the tool's
106
+ `acknowledge_breaking_api_change` carries it out and snapshots the break as a new contract version.
105
107
  - **Exposure is per app, all or nothing** — a public share or a key reaches every alias an app
106
- declares, so what outsiders may call is a second app over the same tables, scaffolded with
107
- `lotics app create "<name>" --api` ([docs/building_an_app.md](./docs/building_an_app.md) § 9).
108
+ declares, so what outsiders may call is a second app over the same tables
109
+ ([docs/building_an_app.md](./docs/building_an_app.md) § 7).
108
110
  - **`--print-created` / `--cleanup` on any call that reports `side_effects`.** The first prints the
109
111
  records created plus a paste-ready cleanup plan and what cannot be auto-undone; the second runs
110
112
  those deletes (records only — never files, integrations or notifications). Neither is a rollback.
@@ -126,29 +128,7 @@ response there is *not* evidence the subcommand is absent. To find out whether s
126
128
 
127
129
  ## Where this CLI is not the answer
128
130
 
129
- - **Authoring a binding via `app deploy`.** Deploy ships code, queries, and capabilities — it never
130
- authors bindings. Workflow bodies go through `app workflow set`, agent instructions through
131
- `app agent set` (both edit a file on disk that `app pull` wrote from the live row). A binding's TYPED
132
- half — an agent's `tool_names`/`inputs`/`outputs`, a workflow's schemas — is authored by the
133
- `set_app_*` tools. A manifest alias with no server binding only fails at the app's first call, so
134
- deploy warns about the mismatch.
135
-
136
- **The four `lotics.*` keys look alike and point in three different directions.** Before editing one,
137
- know which you are touching — this is the single most expensive thing to get wrong in a manifest:
138
-
139
- | `lotics.<key>` | Owned by | Your edit reaches the app via |
140
- |---|---|---|
141
- | `queries`, `capabilities` | the manifest | `app deploy` — re-synced on every one (an absent `capabilities` block turns them all OFF) |
142
- | `knowledge` | the manifest | nothing, until the app is published as a starter — it declares which docs ship with it |
143
- | `workflows` | the workflow row's verified contract | `app workflow set`, which type-checks the BODY against your declaration and refuses a schema the body cannot satisfy |
144
- | `agents` | `inputs`/`outputs`: the manifest. Everything else: the app row | `app agent set`, and `app deploy`, which pushes a diverged `inputs`/`outputs` before it ships. The rest is a mirror `app codegen` re-silvers, changed with `set_app_agent` |
145
-
146
- `agents` is the one that bites, because the two halves of the same block behave differently:
147
- editing `tool_names` or `knowledge_doc_ids` still retypes `useAgentRun` — green locally, unchanged
148
- in production, and reverted by the next `app codegen`. An agent's PROSE is not in the manifest at
149
- all: it lives in `src/agents/<alias>.md` and is pushed by `app agent set`.
150
- - **Editing a local `.xlsx` / `.docx`.** A library call, not a verb: script `@lotics/xlsx` /
151
- `@lotics/docx` (`lotics docs xlsx` / `lotics docs docx` prints the installed copy's API guide).
152
- `lotics file preview` draws the result so it can be looked at.
131
+ - **Editing an app in a local directory.** An app stated in a model is changed by applying the
132
+ model again; its bindings through their `set_app_*` tools; a custom-code app's code by deploying
133
+ its directory again. Nothing reads a local copy of a live app back.
153
134
  - **OAuth connections.** Attaching a connected account is web-only; the CLI can list them.
154
- - **Anything needing a browser.** `app dev` and `file preview` shell out to a local Chrome.
package/README.md CHANGED
@@ -12,13 +12,11 @@ Lotics is an AI-powered operations platform. Through this CLI you can:
12
12
 
13
13
  ## Capability guides
14
14
 
15
- **`lotics docs`** lists every reference the packages installed beside your project ship — each
16
- `@lotics/*` package carries its own, so a doc always describes the version you actually have.
17
- `lotics docs <area>` prints one (`lotics docs ai`); `lotics docs ui` prints a package's index.
18
- Every answer is capped at a page, so a long doc hands back its section list and
19
- `lotics docs <area>/<section>` — or, for a doc that is one table, `lotics docs ui/catalog/Button`.
20
- Nothing is copied here — the packages, the areas, the titles and the versions are all read at run
21
- time, so a doc or a whole package added upstream shows up without upgrading this CLI.
15
+ **`lotics docs`** lists this CLI's references, carried inside it — the model reference and every
16
+ doc below — so a doc always describes the binary answering. `lotics docs <area>` prints one
17
+ (`lotics docs model`). Every answer is capped at a page, so a long doc hands back its section list
18
+ and `lotics docs <area>/<section>`. A custom-code app's SDK reference ships inside
19
+ `@lotics/app-sdk`, in the app's own `node_modules`.
22
20
 
23
21
  **Driving this CLI from an agent? Start at [AGENTS.md](./AGENTS.md)** (`node_modules/@lotics/cli/AGENTS.md`)
24
22
  — which surface answers which question, the conventions that hold across every command, and the traps.
@@ -28,86 +26,57 @@ Per-command contracts, flags, exit codes and gotchas are in
28
26
  The platform's primary surfaces have dedicated usage guides that ship inside this
29
27
  package (reachable at `node_modules/@lotics/cli/docs/*.md` once installed):
30
28
 
31
- - [`docs/building_an_app.md`](docs/building_an_app.md) — building a custom-code app end to end:
32
- the sequence the steps go in (clarify → model → types → queries → workflows → screens → ship)
33
- and the deploy-free inner loop. Read it once before starting an app.
29
+ - [`docs/building_an_app.md`](docs/building_an_app.md) — building an app end to end: an app
30
+ stated in a model and applied with `lotics model apply`, or a custom-code app on
31
+ `@lotics/app-sdk`. Read it once before starting an app.
34
32
  - [`docs/document_templates.md`](docs/document_templates.md) — generate finished documents
35
33
  (PDF, Excel, Word, email) by filling reusable templates: the five template types, the
36
34
  create → generate → chain lifecycle, and the marker capabilities.
37
- - [`docs/migration.md`](docs/migration.md) — what to do when a release changes the shape of a
38
- project this CLI owns; read once, when something on disk no longer matches what the CLI writes.
35
+ - [`docs/migration.md`](docs/migration.md) — what to do when a release changes what the CLI once
36
+ wrote to disk; read once, when something on disk no longer matches what the CLI does.
39
37
  - [`docs/knowledge_docs.md`](docs/knowledge_docs.md) — the AI's rulebook layer: authoring the
40
38
  workspace facts an agent can't guess, who can read a doc, and the
41
39
  catalog-then-stage retrieval model agents use to pull only the lines they need.
42
40
 
43
- ## Start from the library, in one command
41
+ ## Start in one command
44
42
 
45
- Install the CLI, then copy a starter — it creates the account, copies the starter into its
46
- workspace, deploys its apps, and prints a one-time sign-in link:
43
+ Install the CLI, write a `model.json` and apply it; `setup` creates the account, applies the model
44
+ to its workspace, and prints a one-time sign-in link:
47
45
 
48
46
  ```bash
49
47
  curl -fsSL https://lotics.ai/install.sh | bash
50
- lotics setup <starter_id> --email you@company.com
48
+ lotics setup model.json --email you@company.com
51
49
  ```
52
50
 
53
- The installer downloads one compiled executable — no Node.js, no npm, and copying a starter needs
54
- neither: every app is deployed on Lotics and nothing is written here. Node 18+ comes in only when
55
- you pull an app's source to change it (`lotics app pull <app_id>`) and build it to deploy again.
51
+ The installer downloads one compiled executable — no Node.js, no npm: every app is deployed on
52
+ Lotics. Node 18+ comes in only for a custom-code app, which is built on this machine to deploy.
56
53
 
57
54
  Add `--json` for one machine-readable object instead of progress — the organization, the
58
- workspace, the app ids, what was created, the sign-in link, and any warnings. Already signed in?
59
- Drop `--email` and `lotics setup <apg_id>` copies into the account you have.
60
-
61
- `lotics library list` reads both shelves with no account at all. **Presets** are a trade's model,
62
- named by a slug: read one and write a `model.json` from it — nothing is copied. **Packages** are
63
- apps and the tables they stand on, named `apg_…`, copied in whole; each row names how many tables
64
- and which apps a copy creates, so what you actually manage can be matched against them.
65
- `lotics library show <slug|apg_id>` reads one, with no account either — each table as
66
- `alias · label`, each field as `alias:type`, and a preset's variants beside the sentence that
67
- selects each.
55
+ workspace, the app ids, the sign-in link, and any warnings. Already signed in? Drop `--email` and
56
+ `lotics setup model.json` applies to the account you have.
68
57
 
69
- ## Or describe your own workspace
58
+ ## Describe your workspace
70
59
 
71
60
  A `model.json` names the tables, fields, options, links, views, roles and first rows, how a row
72
- of each table is recognised, and the apps over them; `lotics setup` builds the tables. **Start from a preset when one is your trade** — name it instead of
73
- restating it:
74
-
75
- ```json
76
- {
77
- "from": "field_service",
78
- "variants": ["crews"],
79
- "rename": { "job": { "label": "Jobs", "fields": { "code": "Job no." } } },
80
- "rows": { "job": [{ "ref": "j1", "fields": { "code": "J-1" } }] }
81
- }
82
- ```
83
-
84
- No preset is your trade? Write the full form instead, spelling the tables out. Either way:
61
+ of each table is recognised, and the apps over them. Complete example models of several trades are
62
+ listed at `https://lotics.ai/presets/index.json` (each at `https://lotics.ai/presets/<slug>.json`) —
63
+ read the closest one and write your own in your business's words.
85
64
 
86
65
  ```bash
87
- lotics docs model # how to write one, both forms, with a worked example. Offline
88
- lotics scaffold check model.json # prove it — every problem at once, offline unless it names a preset
89
- lotics setup model.json --email you@company.com
66
+ lotics docs model # how to write one, with a worked example
67
+ lotics setup model.json --email you@company.com # a new account, then the model applied to it
68
+ lotics model apply model.json # an account you already have: the tables, then every app
90
69
  ```
91
70
 
92
- It creates no apps of its own: build one in the workspace afterwards
93
- (`lotics docs building_an_app`), or name a published package in the file's `apply` list and it is
94
- copied in — bound onto the tables the model just made — as part of the same run.
95
- For a model that declares apps, `lotics workspace build model.json` is the whole path in one
96
- command — check, apply where the workspace differs, then create or regenerate each app beside the
97
- model file and check it, deploying each under `--deploy`. An app that conflicts or checks red is
98
- named and the rest still run; `--dry-run` writes nothing anywhere.
99
- `setup` refuses a table name your workspace already has; `lotics scaffold apply model.json` is the
100
- additive verb — it adopts that table, adds what the model declares beyond it, and deletes nothing.
101
- Because it never renames, the file and the workspace drift: `lotics scaffold diff model.json`
102
- prints exactly where and exits non-zero on any difference, and
103
- `lotics field rename <table> <field> "<new label>"` moves a label on the platform, in the file
104
- (`--model`) and in every app bound to it (`--apps`) at once. `lotics scaffold apply model.json
105
- --documents` writes only the file cells of the model's rows, onto the records the first run made —
106
- rows land only into empty tables, so it is the one way back for attachments that were missed.
107
-
108
- The other direction, once a workspace works: `lotics scaffold export > model.json` prints its
109
- tables (or only `--tables tbl_a,tbl_b`) as that same file, findings on stderr. It is a starting
110
- point for the next business, never a source of truth — the labels are this one's.
71
+ `model apply` checks the whole file first — every problem at once — then adopts or creates each
72
+ table (the table this workspace bound it to, else one of the same label, is adopted and given
73
+ what it lacks; no stored value changes), writes the first rows only where every bound table is empty, and mints a new version of
74
+ each app the model declares. Change the file and apply it again; `--app <alias>` applies only the
75
+ apps named. Rolling an app back (`lotics run rollback_app`) restores its earlier version — table
76
+ changes and data writes stay.
77
+
78
+ The other direction: `lotics model pull -o model.json` writes this workspace's model as that same
79
+ file, rebuilt from what owns each part — its tables, how rows are recognised, and each app's body.
111
80
 
112
81
  ## Install
113
82
 
@@ -207,9 +176,11 @@ Every command that resolves a workspace names its target before it acts — `lot
207
176
 
208
177
  **`LOTICS_ORG` is resolved once, before any command runs.** A value matching no saved credential refuses every verb with one sentence — a read, a write, and a check that needs no credential alike — and the refusal lists the orgs this machine does hold, so it is answerable without another command. It refuses even when a key arrives another way: a variable that scopes the command must not go unread while `LOTICS_API_KEY` sends the write somewhere else. When the variable resolves AND a key is supplied, the key decides the org and the command says so. An org keeps the name it was saved under: a server-side rename never moves it, the new name resolves as well, and `lotics org` prints both when they differ.
209
178
 
179
+ In a custom-code app's directory, `lotics app deploy` derives the credential from the directory when nothing explicit chose one: the manifest names the app's workspace, and when exactly one saved profile owns that workspace, that profile is used — the machine-wide default is never consulted. An explicit flag, env var, or directory pin still wins, and an org whose profile remembers a different workspace simply falls through to the announced default.
180
+
210
181
  ### Diagnostics
211
182
 
212
- Every request identifies the CLI (`user-agent: lotics-cli/<version> node/<v> <platform>`) and names the command that made it (`x-lotics-cli-command: app.workflow.set`), so a failure in the server's logs can be traced to the verb and version that produced it. Neither header carries arguments: the command chain stops before any id, path, `@file`, or JSON payload.
183
+ Every request identifies the CLI (`user-agent: lotics-cli/<version> node/<v> <platform>`) and names the command that made it (`x-lotics-cli-command: model.apply`), so a failure in the server's logs can be traced to the verb and version that produced it. Neither header carries arguments: the command chain stops before any id, path, `@file`, or JSON payload.
213
184
 
214
185
  **`LOTICS_TELEMETRY=1` additionally records the session.** Off by default. Set it in your shell profile rather than per command — each invocation is its own process. When set:
215
186
 
@@ -257,8 +228,6 @@ needs an opt-in for), posts immediately, and tells you if it did not land.
257
228
 
258
229
  Do not paste records, file contents, or credentials.
259
230
 
260
- In an app project, `lotics app *` commands derive the credential from the directory when nothing explicit chose one: the manifest names the app's workspace, and when exactly one saved profile owns that workspace, that profile is used — the machine-wide default is never consulted. An explicit flag, env var, or directory pin still wins, and an org whose profile remembers a different workspace simply falls through to the announced default.
261
-
262
231
  ## Workspaces
263
232
 
264
233
  Workspaces live inside the active org. If the org has more than one, select before running tools:
@@ -311,21 +280,6 @@ LOTICS_API_KEY=ltk_... lotics run query_tables '{}'
311
280
  LOTICS_ORG=acme LOTICS_WORKSPACE=wks_... lotics run query_tables '{}'
312
281
  ```
313
282
 
314
- ## Local .xlsx and .docx files
315
-
316
- Edit them by scripting Lotics's own engines, published as `@lotics/xlsx` and `@lotics/docx`. Prefer them over `xlsx` / `exceljs` / `docx` from npm: they round-trip faithfully with the Lotics editor, template engine, and formula engine. `npm i @lotics/xlsx @lotics/docx`, then read the API guide that ships inside the package: `lotics docs xlsx` / `lotics docs docx` prints the installed copy (`node_modules/@lotics/<pkg>/AGENTS.md` on a machine with no CLI). Both packages ship `.ts` sources as their entry, so run the script under a TypeScript-aware runner.
317
-
318
- ```ts
319
- import { readFile, writeFile } from "node:fs/promises";
320
- import { parseDocx, replaceText, serializeDocx } from "@lotics/docx";
321
-
322
- const { doc, count } = replaceText(await parseDocx(await readFile("in.docx")), "{{name}}", "Acme Ltd.");
323
- if (count === 0) throw new Error("{{name}} is not in this document — nothing written");
324
- await writeFile("out.docx", await serializeDocx(doc));
325
- ```
326
-
327
- `lotics preview <file.xlsx|.docx>` draws one of those files to a PNG with the same engines, so a script's output can be looked at rather than guessed at.
328
-
329
283
  ## Knowledge docs
330
284
 
331
285
  A file-native surface over the workspace's knowledge docs — the AI's rulebook layer. The body is a Markdown file: `create`/`update` read it from your filesystem, `get` writes it back. See [`docs/knowledge_docs.md`](docs/knowledge_docs.md) for the model.
@@ -345,135 +299,25 @@ lotics knowledge rm kdc_... # archive
345
299
  ## Custom-code apps
346
300
 
347
301
  ```bash
348
- # SEE THE APP BEFORE IT EXISTS. The model's app is compiled against the workspace the
349
- # model WOULD become and every read is answered from the model's own `rows` (a few
350
- # synthesized for an entity that states none), so there is no app row, no table and
351
- # no credential in the run — then the same headless walk `app check --screens` takes,
352
- # at the same two widths, measured by the same probes: the register, the record in
353
- # its door, the add. Exits 1 on any finding and prints what each phase cost. It
354
- # renders in the cached project of its dependency set (`~/.lotics/render/`, shared
355
- # with `app check --screens`), installed once from the registry — the kit and the
356
- # runtime are published packages — and rewritten from the model on every run.
357
- lotics app preview model.json#sales_desk
358
- lotics app preview model.json#sales_desk --shots ./shots --width 375
359
-
360
- # Scaffold / pull / deploy a Vite+React+TS app project
361
- lotics app create "Sales Desk" # scaffold + deploy v1
362
- lotics app create "Sales Desk" --from model.json#sales_desk # the model's app, as app.json
363
- lotics app create "Orders API" --api # no screens: its declarations are the whole surface,
364
- # so nothing is built and nothing is deployed
365
- lotics app pull app_... # bootstrap an existing app locally (incl. .lotics/*)
366
- lotics app deploy -m "Add quote drawer" # typecheck + build + upload a new version
367
- lotics app versions # deploy history: version, when, who, -m message (* = live)
368
- lotics app versions app_... # ...for any app, without pulling it first
369
-
370
- # Apply the latest version of the package this app was COPIED from. Additive on
371
- # the schema; a workflow or agent you have edited here is kept and named, and so
372
- # is a field the new version stopped declaring. Redeploys from the published
373
- # dist, so nothing local is sent — pull afterwards to edit the new code.
374
- lotics app upgrade # the app this directory's manifest names
375
- lotics app upgrade app_... # ...for any app, without pulling it first
376
-
377
- # The app's API: what its declared queries, workflows and agents promise to a
378
- # caller OUTSIDE the app — a customer's own site or server. Publishing snapshots
379
- # that promise as a numbered contract; from then on a manifest write that would
380
- # break it is refused and every breaking change is named, unless the write carries
381
- # --acknowledge-breaking-api (app deploy / app query set / app workflow set /
382
- # app agent set / app upgrade). A query that does not name the columns it returns
383
- # is refused at publish: those field names are the table's, not the app's to
384
- # promise.
385
- lotics app api publish # snapshot the contract; prints the version + warnings
386
- lotics app api status # is one published, and which version callers hold
387
- lotics app api spec -o api.openapi.json # the OpenAPI 3.1 document, for the consumer's generator
388
- lotics app api unpublish # end the promise
389
-
390
- # Regenerate .lotics/* WITHOUT a deploy: the .d.ts type companions (always) +
391
- # the runtime app_fields.ts (when authenticated) — F/OPT maps that address
392
- # fields + select options by stable display-name aliases instead of opaque ids.
393
- # `app pull` writes both too (a deploy archive can never carry app_fields.ts);
394
- # use this after a rename, or when a pull ran offline.
395
- lotics app codegen # import { F, OPT } from "../.lotics/app_fields"
396
-
397
- # Install @lotics/app-runtime from your CHECKOUT, to prove a change on a real app
398
- # before it is published: build, pack into .lotics/kit/, install by file
399
- # specifier, then hash one built file on both sides — a repack under the same name
400
- # is otherwise served from the lockfile's first tarball. `app check` warns and
401
- # `app deploy` refuses (--allow-local-kit ships it anyway), because that tarball is
402
- # not in the source archive. --published puts back the range the app listed before
403
- # the first checkout (an app with none recorded moves to the registry's). Either
404
- # keeps @lotics/ui at the runtime's range and migrates an app on @lotics/app-sdk.
405
- lotics app kit ../lotics/packages/app-runtime
406
- lotics app kit --published
407
-
408
- # Every deploy pre-flight, WITHOUT the build or the version row: the app's own
409
- # typecheck over regenerated .lotics types, agent schemas vs the live app, aliases
410
- # the code calls that nothing bound, undeclared capabilities, query drift. Exits 1 on what a deploy refuses, so CI can gate on it.
411
- lotics app check
412
-
413
- # --screens adds the rendered surface: each screen the app's navigation reaches,
414
- # and the first record each one opens onto a PAGE, headless in Chrome at 1280
415
- # and 375, measured against @lotics/ui docs/reviewing.md. --screen and --width
416
- # narrow it while you iterate on one screen. It renders in the cached project of the
417
- # app's dependency set (~/.lotics/render/, shared with app preview) and prints what
418
- # each phase cost. --changed walks only when something the screens read has moved
419
- # since this checkout's last walk (.lotics/screens_check.json), and otherwise
420
- # reprints that walk's report.
421
- lotics app check --screens
422
- lotics app check --screens --screen "Đơn hàng" --width 375
423
- lotics app check --screens --changed
302
+ lotics app create "Sales Desk" --custom # the app, plus a Vite + React + TS project on @lotics/app-sdk
303
+ cd "Sales Desk"
304
+ npm run typecheck && npm run lint && npm test
305
+ lotics app deploy -m "Add quote drawer" # build + upload a new version of the live app
306
+
307
+ # What the app reads and writes is bound on the app, and each change mints a version:
308
+ lotics run set_app_query '{"app_id":"app_...","alias":"openInvoices","declaration":{...}}'
309
+ lotics run set_app_workflow '{"app_id":"app_...","alias":"issueInvoice","source":"..."}'
310
+ lotics run rollback_app '{"app_id":"app_...","version_id":"apv_..."}' # the undo; data stays
424
311
 
425
312
  # Running an app's alias is a TOOL call, like every other tool. Exits non-zero
426
313
  # when the RUN failed, not only when the call did.
427
- lotics run run_app_workflow '{"app_id":"app_...","alias":"issueInvoice","inputs":{"record_id":"rec_..."}}'
428
- cat args.json | lotics run run_app_workflow - # bulk inputs bypass ARG_MAX
429
- # Honest post-run harvest: created records + a paste-ready cleanup plan + the
430
- # caveat (external/notification calls can't be auto-undone; sub-workflows may run).
431
- lotics run run_app_workflow '{...}' --print-created
432
- lotics run run_app_workflow '{...}' --cleanup # also deletes created records (NOT a rollback)
433
-
434
- # Edit workflow bodies as files. `app pull` writes src/workflows/<alias>.ts (the
435
- # faithful server source, wrapped + referencing its .lotics/workflows/<alias>.globals.d.ts);
436
- # edit the body, then push it back through set_app_workflow — the server verifies it
437
- # (a deploy calls the same verb for every alias the project holds ahead of the app, so
438
- # this is the one-alias spelling of it). `app workflow check` runs the server's own
439
- # JS-subset parser + type-check locally, then asks the server for every check `set`
440
- # makes (`verify_only`, nothing written), so a body that passes is one `set` will accept:
441
- lotics app workflow pull # rewrite src/workflows/*.ts + globals from the server
442
- lotics app workflow check # every check `set` makes, writing nothing ([alias...] for some)
443
- lotics app workflow diff issueInvoice # how the local body differs from the one the server runs
444
- lotics app workflow set issueInvoice # push the edited src/workflows/issueInvoice.ts
445
-
446
- # Iterate on a named query WITHOUT a deploy: push package.json#lotics.queries.<alias>
447
- # to apps.queries (server-validated like a deploy). apps.queries is manifest-
448
- # authoritative, so the next `app deploy` re-syncs it — keep the manifest current.
449
- lotics app query set openInvoices # push package.json#lotics.queries.openInvoices
450
- lotics app query run openInvoices --params '{"customer":"rec_..."}' # the bound query's rows, as a screen gets them
451
-
452
- # Move one alias out of this app and into another of the same workspace — the
453
- # declaration (and a workflow's body) land in the target project, the target is
454
- # bound to its OWN row, and the source is unbound and undeclared here. No release
455
- # either side. Run it in the source project; refused while anything here still
456
- # calls the alias, unless you say --even-if-invoked.
457
- lotics app query move openInvoices --to ../ke-toan
458
- lotics app workflow move issueInvoice --to ../ke-toan
459
-
460
- # Run a bound app agent end-to-end (no deployed UI needed — app row + declaration
461
- # + member auth). Streams progress to stderr; reports the SETTLED run (structured
462
- # output / final text) to stdout; exits 0 only when the run completed.
314
+ lotics run run_app_query '{"app_id":"app_abc","alias":"openInvoices","params":{}}'
315
+ lotics run run_app_workflow '{"app_id":"app_abc","alias":"issueInvoice","inputs":{...}}'
316
+ lotics run run_app_workflow '{...}' --print-created # the records it created + a cleanup plan
463
317
  lotics run run_app_agent '{"app_id":"app_abc","alias":"recognize","input":{"image_file_id":"fil_..."}}'
464
- cat input.json | lotics run run_app_agent - # inputs via stdin/@file
465
- lotics run run_app_agent '{...}' --json # full run summary to stdout
466
- # A run that outlives the call's bounded wait keeps going server-side; read it with
467
- lotics run get_app_agent_run '{"app_id":"app_abc","run_id":"run_..."}'
468
-
469
- # Dev-link @lotics/ui to a monorepo checkout for ONE command — nothing hand-written is touched
470
- # (the matching tsc `paths` land in the CLI's own .lotics/tsconfig.link.json)
471
- LOTICS_UI_SRC=/abs/monorepo/packages/ui/src lotics app dev
472
- LOTICS_UI_SRC=/abs/monorepo/packages/ui/src lotics app deploy -m "..." # warns: bundles YOUR kit copy
473
- lotics app dev # unset ⇒ @lotics/ui resolves from node_modules again
474
318
  ```
475
319
 
476
- `app codegen` puts a table in `app_fields.ts` when a declared query reads it or a bound workflow writes it — the queries come from `package.json#lotics.queries`, the written tables from each binding's own `table_ids`, so a table only a workflow body touches is addressable by alias with nothing to declare.
320
+ The app's SDK reference is `node_modules/@lotics/app-sdk/AGENTS.md` inside the project. `lotics tools <name>` prints any tool's input.
477
321
 
478
322
  ## SDK
479
323