@lotics/cli 0.316.24 → 0.316.26
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 +4 -5
- package/dist/src/cli.js +8092 -3153
- package/dist/src/client.d.ts +33 -0
- package/docs/building_an_app.md +15 -7
- package/docs/cli_reference.md +1 -1
- package/package.json +8 -8
package/dist/src/client.d.ts
CHANGED
|
@@ -234,6 +234,17 @@ export declare class LoticsClient {
|
|
|
234
234
|
/** The instance this client sends every request to. */
|
|
235
235
|
readonly baseUrl: string;
|
|
236
236
|
constructor(options: LoticsClientOptions);
|
|
237
|
+
/**
|
|
238
|
+
* The id is appended to the MESSAGE rather than carried on a field, because
|
|
239
|
+
* the only thing that reliably reaches a person is what got printed: the CLI's
|
|
240
|
+
* top-level handler writes `error.message` to stderr, and the telemetry tail
|
|
241
|
+
* captures the same bytes. A field would have to be read by every one of ~60
|
|
242
|
+
* exit sites to be worth anything.
|
|
243
|
+
*
|
|
244
|
+
* `requestId` is optional because one caller legitimately has none — a
|
|
245
|
+
* presigned CDN download is not a request of ours and appears in none of our
|
|
246
|
+
* logs. Every call against the API passes the id its own headers carry.
|
|
247
|
+
*/
|
|
237
248
|
private throwResponseError;
|
|
238
249
|
/**
|
|
239
250
|
* The backend's `log()` middleware registers `user-agent`,
|
|
@@ -324,6 +335,10 @@ export declare class LoticsClient {
|
|
|
324
335
|
}): Promise<{
|
|
325
336
|
email: string;
|
|
326
337
|
url?: string;
|
|
338
|
+
/** The sign-in removes the account's password: its address was never confirmed. */
|
|
339
|
+
password_cleared: boolean;
|
|
340
|
+
/** Where "Forgot password" sets a new one; present with `password_cleared`. */
|
|
341
|
+
login_url?: string;
|
|
327
342
|
}>;
|
|
328
343
|
listTools(): Promise<{
|
|
329
344
|
tools: string[];
|
|
@@ -467,6 +482,24 @@ export declare class LoticsClient {
|
|
|
467
482
|
uploadFiles(filePaths: string[], options?: {
|
|
468
483
|
filenames?: string[];
|
|
469
484
|
}): Promise<FileUploadResult>;
|
|
485
|
+
/**
|
|
486
|
+
* Store one file by handing its bytes straight to object storage.
|
|
487
|
+
*
|
|
488
|
+
* The API mints presigned part URLs and registers the row; the bytes go
|
|
489
|
+
* directly from disk to the bucket and never enter the API process. That is
|
|
490
|
+
* what makes the ceiling "what a workspace may store" rather than "what one
|
|
491
|
+
* request may cost the server" — the reason `POST /v1/files` and the ticketed
|
|
492
|
+
* upload are both capped far lower, and the reason this exists at all.
|
|
493
|
+
*
|
|
494
|
+
* Parts are read one at a time rather than streamed as a request body: a part
|
|
495
|
+
* is bounded (the server picks 5–10 MiB), a `fetch` body needs a length the
|
|
496
|
+
* bucket will check anyway, and reading a slice keeps peak memory at one part
|
|
497
|
+
* instead of one file.
|
|
498
|
+
*
|
|
499
|
+
* The PUT loop itself — retry, backoff, part timeout, ordering — is
|
|
500
|
+
* `@lotics/shared/multipart_parts`, shared with the browser client so the
|
|
501
|
+
* hardening cannot exist on one side only.
|
|
502
|
+
*/
|
|
470
503
|
private uploadLargeFile;
|
|
471
504
|
/**
|
|
472
505
|
* Store files from bytes the caller already holds — the path for a caller that
|
package/docs/building_an_app.md
CHANGED
|
@@ -24,9 +24,9 @@ you tried it, stay where they are. Try a write on a throwaway record.
|
|
|
24
24
|
compiles each app and draws it with the runtime every such app shares, so how each piece looks is
|
|
25
25
|
the platform's, one way per concept, and no prop changes it. Every write the app makes is a
|
|
26
26
|
generated workflow that re-checks on the server what the tables state.
|
|
27
|
-
- **A custom-code app** —
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
- **A custom-code app** — your own project, for a surface the tree's components cannot state. Its UI
|
|
28
|
+
library, components, styling, state, bundler, tests and lint are yours; it reads and writes through
|
|
29
|
+
`@lotics/app-sdk` (queries, workflows, files, AI).
|
|
30
30
|
|
|
31
31
|
## 2 — Clarify what is being asked, before designing it
|
|
32
32
|
|
|
@@ -137,14 +137,22 @@ agent_surface`, § Declaring more.
|
|
|
137
137
|
## 5 — A custom-code app
|
|
138
138
|
|
|
139
139
|
```
|
|
140
|
-
lotics app create "<name>" --custom
|
|
141
|
-
cd <dir>
|
|
142
|
-
# edit src/App.tsx — node_modules/@lotics/app-sdk/AGENTS.md is the reference
|
|
143
|
-
npm run typecheck && npm run lint && npm test
|
|
140
|
+
lotics app create "<name>" --custom . # registers the app and links this directory (package.json#lotics)
|
|
144
141
|
lotics app deploy -m "<what changed>" # build, upload, a new version live
|
|
145
142
|
lotics app pull <app_id> # the live version's source, on a machine without the project or after another deploy
|
|
146
143
|
```
|
|
147
144
|
|
|
145
|
+
The contract, and nothing more:
|
|
146
|
+
|
|
147
|
+
- `@lotics/app-sdk` is React hooks and needs React 19 (`react` and `react-dom` `^19.2`); `react-router`
|
|
148
|
+
(`^7` or `^8`) only for `@lotics/app-sdk/router`. Its reference is `node_modules/@lotics/app-sdk/AGENTS.md`.
|
|
149
|
+
- `lotics app deploy` writes the app's declarations into `.lotics/` (`app_queries.d.ts` types
|
|
150
|
+
`useQuery` by its aliases; include the directory in the project's TypeScript config), runs
|
|
151
|
+
`npm run typecheck` when the project declares it, then `npm run build`, which must write a static
|
|
152
|
+
`dist/` with an `index.html`. It uploads `dist/` and the source (without `node_modules`, `dist`,
|
|
153
|
+
`.git`).
|
|
154
|
+
- The app is a static bundle on the app host and reaches data only through the SDK.
|
|
155
|
+
|
|
148
156
|
What the app reads and writes is bound on the app, never in the project: `lotics run
|
|
149
157
|
set_app_queries` binds named queries, `lotics run set_workflow` a workflow body, and each mints a
|
|
150
158
|
version. `useQuery("<alias>")` and `useWorkflow("<alias>")` call them. A deploy uploads the build
|
package/docs/cli_reference.md
CHANGED
|
@@ -46,7 +46,7 @@ Per-command syntax, flags, contracts, and gotchas for the public `lotics` CLI. S
|
|
|
46
46
|
| `lotics knowledge hide <id...>` / `lotics knowledge unhide <id...>` | `PATCH /v1/knowledge_docs` with `{ knowledge_doc_ids, hidden }`. Hiding takes docs out of every **listing** — the Library's list, and the corpus `get_workspace` lists and its `search` searches — while leaving IAM untouched and keeping them readable **by id** (`read_knowledge` with an id, a code run staging one, an app agent's declared set). So it can never silently break an app that depends on a doc, and unhiding costs nothing. Refuses the no-argument form rather than reading it as "everything". |
|
|
47
47
|
| `lotics knowledge rm <id...>` | Delete docs via `set_knowledge` (`{ knowledge_doc_id, delete: true }`), one call per id: each must be a live doc you manage that no app or workflow names, and the command reports which succeeded and which failed. Sharing and history are kept. The REST execute path does not gate `needsApproval`, so this runs unattended. |
|
|
48
48
|
| `lotics knowledge restore <id...>` | Bring deleted docs back via `set_knowledge` (`{ knowledge_doc_id, restore: true }`), one call per id: each must be a deleted doc you manage, and the command reports which succeeded and which failed. Refuses the no-argument form. |
|
|
49
|
-
| `lotics app create <name> --custom [path]` | **A custom-code app**: creates the app (`POST /v1/apps`)
|
|
49
|
+
| `lotics app create <name> --custom [path]` | **A custom-code app**: creates the app (`POST /v1/apps`) and links `[path]` (default `./<name>`, made when absent) to it by writing `package.json#lotics` (the app and its workspace, which is how `app deploy` in that directory finds both) — into the project's own `package.json` when it has one, every other key kept, otherwise a `package.json` holding the link alone. Nothing else is written and nothing is installed: the project is yours. `app deploy` needs `@lotics/app-sdk` (React 19) and an `npm run build` that writes a static `dist/` with an `index.html`. A directory already linked to an app is refused before the app row exists. The app has no version until the first `lotics app deploy`. `--custom` is required: a JSON app is made by `lotics app apply` (the tool `set_app`). The SDK's reference is `node_modules/@lotics/app-sdk/AGENTS.md` once the project installs it. |
|
|
50
50
|
| `lotics app pull [app_id] [path]` | **An app's live definition, pulled.** The app is read with `get_app`: its author is shown a body, a JSON **tree** (with the addresses of its nodes) pulled as that tree and a custom-code app (the identity alone) as its source; an app whose stored tree cannot be read is refused with the reason `get_app` gives, and so is an app that shows the caller no body (the caller does not author it, or it has no version). **A JSON app**: the tree is written as JSON to `[path]` (a directory receives `./<name>.app.json`; default `./<name>.app.json`), and the app's id and current version beside it in `<file>.lotics.json` (`{ "app_id": "…", "version_id": "…", "digest": "…" }`, the digest being the file as the CLI last left it; a record without all three is refused by `app apply`, naming `lotics app pull`) — a custom-code project records both in `package.json#lotics`, a single file has no such place — so the next `lotics app apply <file>` edits that app, and is refused if the app has since moved past that version. An existing file is overwritten only when its `.lotics.json` names this same app and the file is unchanged since it was last pulled or applied; a file with edits, or whose record names the app but no digest, is refused, naming `--force`, which overwrites it. `--force` is a flag of `app pull` alone. `--json` prints `{workspace_id, app_id, path, outcome}`. **A custom-code app**: its live source, as a project ready to deploy on it. The target is `[path]`, else this directory when it is the app's project (or no app is named), else `./<name>`. **The app's own project is brought up to date in place** — but only when it holds no edit since the version `package.json#lotics.current_version_id` names: its source (packed as a deploy packs it) is compared with that version's archive, ignoring `.lotics/` and `package.json#lotics`, and any difference refuses the pull, naming the changed files; a pull never merges, so local work is never lost. Already at the live version is a no-op that says so. **An empty or new directory receives the source whole**; any other directory is refused. Downloads come from `GET /v1/apps/{id}/versions/{version_id}/source`. `package.json#lotics` is then set to exactly the app, its workspace and the pulled version, dropping every other key an older CLI wrote there, so the next `lotics app deploy` builds on the live version; then `.lotics/` is written (`GET /v1/apps/{id}/types`) and dependencies installed (`npm ci` with a lockfile, else `npm install`, both `--ignore-scripts`). |
|
|
51
51
|
| `lotics app apply <file> [--app <app_id>] [--check] [--json]` | **A JSON app, one at a time, through the tool `set_app`.** The file is an app's tree (`lotics docs reference`: the App node with the nodes under it, its tables and templates named by alias). **No app known** (no `--app`, no `<file>.lotics.json` beside it): the file is sent as `{ "body": … }` to make a new app, named by the body's `name`, and the id made is printed, then recorded with its version in `<file>.lotics.json`. **An app known**: its current tree is read (`get_app`) and the **RFC 7396 merge patch** that turns it into the file is sent as `body` with the `app_id` and the version the patch was computed against as `base_version_id` — a key the file drops is `null` in the patch, a list is replaced whole, what the file leaves alone is left alone, and a tree the app already has sends nothing and says so. **A file pulled at a version the app has since left is refused** before anything is sent, naming both versions and `lotics app pull <app_id> <path>`: pull the current tree into another path, carry the edits over, apply that file. `--app` naming an app other than the sibling file's is refused too. A write that races the apply is refused by the server, which names the app's current version. A file that sets an object key to `null` is refused naming the key, since a merge patch reads `null` as a removal. `--app` names the app, else the sibling file does; an app whose `get_app` shows no body (it is not yours) or whose stored tree cannot be read is refused, and a custom-code app takes its identity (name, description, icon, colour) only, and no `<file>.lotics.json` records it, since an identity write makes no version — its source deploys with `app deploy`. **The file is never rewritten**: a node the tree leaves out draws as the rows call for it and is stored nowhere (`lotics docs design`). It prints what `set_app` answered and reports its `outcome`: its notes and findings, every finding at once, and the saves, adds and Deletes the write grants and removes. **`--check` sends `dry_run`** (a flag of `app apply` alone, as is `--app`) — what would refuse the tree, and what it would grant and remove — and writes nothing, the file and its sibling included; an `outcome` of `refused` exits 1. A tree the app already has, or one `set_app` answers `unchanged`, records the version the app stays at in the sibling. **To go back to an earlier version, apply the earlier tree again.** Resolves and ANNOUNCES its workspace first. `--json` prints `{workspace_id, app_id, outcome, write}`, `outcome` being `set_app`'s: `created`, `updated`, `unchanged` or `refused` (with `--check`, what it would be). |
|
|
52
52
|
| `lotics app deploy [-m <message>]` | **Build this directory and upload it as a new version of the live app.** Rewrites `.lotics/` with the declarations of the app's live bindings (`GET /v1/apps/{id}/types`, replacing each file there), then runs the project's `npm run typecheck` (warned about when absent; a type error in the app's own files stops the deploy, one inside an installed package is reported and does not) and `npm run build`, tars the source (without `node_modules`, `dist`, `.git`, `*.tsbuildinfo`) and `dist/`, and posts both to `POST /v1/apps/{id}/versions` on the version `package.json#lotics.current_version_id` names, then stamps the new one there. The version carries the app's queries, workflows, agents and capabilities forward unchanged — those are written through their tools. A project with no `build` script, or whose `package.json#lotics` still declares `queries`, `workflows`, `agents` or `capabilities`, is refused before anything is built; the refusal names the tool that sets each. **A 409 because another version went live since this directory's last deploy** (a deploy from elsewhere, a rollback) prints the server's sentence and the version that is live, and names `lotics app pull`: run in this directory, it brings an unedited project up to date, and lists the files a project with edits changed, to carry over into a fresh pull. `-m` (or a bare positional) is the version's message, optional. The workspace comes from `package.json#lotics.workspace_id` unless `--workspace` / `LOTICS_WORKSPACE` names another. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/cli",
|
|
3
|
-
"version": "0.316.
|
|
3
|
+
"version": "0.316.26",
|
|
4
4
|
"description": "Lotics SDK and CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,28 +16,28 @@
|
|
|
16
16
|
"docs"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "
|
|
19
|
+
"build": "tsrs -p tsconfig.build.json && node scripts/build_cli.mjs",
|
|
20
20
|
"build:binaries": "node scripts/build_binaries.mjs",
|
|
21
21
|
"publish:binaries": "node scripts/build_binaries.mjs && node scripts/publish_binaries.mjs",
|
|
22
22
|
"generate:model-reference": "node --import tsx dev/model_reference_gen.ts",
|
|
23
23
|
"generate:examples": "node --import tsx dev/examples_gen.ts",
|
|
24
24
|
"generate:snapshots": "node --import tsx dev/factory_snapshots.ts",
|
|
25
25
|
"generate:agent-surface": "node --import tsx dev/agent_surface_gen.ts",
|
|
26
|
-
"typecheck": "
|
|
26
|
+
"typecheck": "tsrs --noEmit",
|
|
27
27
|
"lint": "oxlint",
|
|
28
28
|
"test": "vitest run",
|
|
29
29
|
"prepublishOnly": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@aws-sdk/client-s3": "^3.
|
|
32
|
+
"@aws-sdk/client-s3": "^3.1156.0",
|
|
33
33
|
"@lotics/docx": "*",
|
|
34
34
|
"@lotics/ooxml": "*",
|
|
35
35
|
"@lotics/shared": "*",
|
|
36
36
|
"@lotics/xlsx": "*",
|
|
37
|
-
"@types/node": "^
|
|
38
|
-
"esbuild": "^0.28.
|
|
39
|
-
"
|
|
40
|
-
"vitest": "^5.0.
|
|
37
|
+
"@types/node": "^24.19.2",
|
|
38
|
+
"esbuild": "^0.28.2",
|
|
39
|
+
"tsrs": "0.18.0-ts7.1.0-dev.20260929",
|
|
40
|
+
"vitest": "^5.0.5"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"lotics",
|