@lotics/cli 0.241.0 → 0.242.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
@@ -14,7 +14,7 @@ conventions are, and where the traps are.
14
14
  | [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. |
15
15
  | [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. |
16
16
  | [docs/document_templates.md](./docs/document_templates.md) | Generating PDF/Excel/Word/email from reusable templates. |
17
- | [docs/knowledge_docs.md](./docs/knowledge_docs.md) | Authoring the workspace facts an agent can't guess; access-vs-activation; catalog-then-stage retrieval. |
17
+ | [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. |
18
18
  | [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 plan is `app.json` rendered by `@lotics/app-runtime`, not a tree of TSX. |
19
19
  | [README.md](./README.md) | Install, auth, and worked examples. |
20
20
 
package/README.md CHANGED
@@ -39,7 +39,7 @@ package (reachable at `node_modules/@lotics/cli/docs/*.md` once installed):
39
39
  - [`docs/migration.md`](docs/migration.md) — what to do when a release changes the shape of a
40
40
  project this CLI owns; read once, when something on disk no longer matches what the CLI writes.
41
41
  - [`docs/knowledge_docs.md`](docs/knowledge_docs.md) — the AI's rulebook layer: authoring the
42
- workspace facts an agent can't guess, the access-vs-activation model, and the
42
+ workspace facts an agent can't guess, who can read a doc, and the
43
43
  catalog-then-stage retrieval model agents use to pull only the lines they need.
44
44
 
45
45
  ## Start from the library, in one command
package/dist/src/cli.js CHANGED
@@ -45538,6 +45538,18 @@ var resourceAccessSchema = zod_default.object({
45538
45538
  resource_id: zod_default.string().describe("ID of the resource these principals can reach"),
45539
45539
  principals: zod_default.array(accessPrincipalSchema).describe("Members, groups and the organization that have been granted access")
45540
45540
  });
45541
+ var contentResourceTypeSchema = zod_default.enum(["knowledge_doc", "document_template"]);
45542
+ var contentItemSchema = zod_default.object({
45543
+ id: zod_default.string().describe("ID of the knowledge doc or template"),
45544
+ name: zod_default.string().describe("Its name"),
45545
+ owner_member_id: zod_default.string().nullable().describe("Member who owns it; null when nobody does"),
45546
+ created_at: zod_default.string().describe("When it was created"),
45547
+ updated_at: zod_default.string().nullable().describe("When a knowledge doc was last changed; null for a template"),
45548
+ hidden_at: zod_default.string().nullable().describe("When a knowledge doc was hidden; null when it is in use, and for a template"),
45549
+ template_type: zod_default.string().nullable().describe("A template's type (html, email, word, excel, pdf-form); null for a knowledge doc"),
45550
+ can_open: zod_default.boolean().describe("Whether the caller may open it; false means it is not shared with them"),
45551
+ principals: zod_default.array(accessPrincipalSchema).describe("Members, groups and the organization it has been shared with, besides its owner")
45552
+ });
45541
45553
  var resourceRoleBindingSchema = zod_default.object({
45542
45554
  id: zod_default.string().describe("Unique identifier for the role binding"),
45543
45555
  principal: principalSchema.describe(
@@ -51401,7 +51413,7 @@ function resultSideEffects(result) {
51401
51413
 
51402
51414
  // src/version.ts
51403
51415
  init_define_LOTICS_KIT_VERSIONS();
51404
- var VERSION = "0.241.0";
51416
+ var VERSION = "0.242.0";
51405
51417
 
51406
51418
  // src/timezone.ts
51407
51419
  init_define_LOTICS_KIT_VERSIONS();
@@ -35,7 +35,7 @@ Per-command syntax, flags, contracts, and gotchas for the public `lotics` CLI. S
35
35
  | `lotics run <tool> --cleanup` | Implies `--print-created`, then runs those deletes — harvested records **only**, never files / external calls / notifications. **Not a rollback**; a rollback is structurally impossible here. A partial cleanup exits non-zero so a script cannot read it as success. |
36
36
  | `lotics file upload <file\|dir...>` (alias `lotics upload`) · `--stdin` · `--base64` · `--url <url>` | Upload files/directories. **The transport is chosen by size and is not a flag**: under 8 MiB the file is POSTed to `/v1/files` in one request, and several such files go in the same one; at or above it the CLI takes presigned part URLs and PUTs the bytes straight to object storage, so they never pass through the API. That threshold matches the AWS CLI's own `multipart_threshold`, and the number matters less than there being nothing to choose — one verb, any size, up to the 2 GiB a workspace may store. A large upload reads one part at a time, so memory stays flat regardless of file size, and a failure part-way abandons the parts already sent rather than leaving them billable and invisible. A directory expands to its immediate files; `--as <name>` renames a single upload. **Three alternative byte sources, for a caller that never had the bytes on disk** — an attachment decoded in memory, a generated document, a signed download link — each mutually exclusive with the others and with a path argument: `--stdin` takes raw bytes on stdin, `--base64` takes base64 on stdin (the shape attachments arrive in), `--url <url>` fetches the URL first. `--stdin`/`--base64` REQUIRE `--as`, because stdin carries no filename and the mime type is derived from it; `--url` falls back to `Content-Disposition` then the URL's last path segment. `--base64` decodes STRICTLY — `Buffer.from(s, "base64")` silently skips invalid characters and truncates on bad padding, so a corrupted pipe would otherwise store a short file that only fails when a human opens it. The `--url` fetch happens in the CLI, not the server: the URL comes from the operator running the command, so routing it through the backend would add an SSRF surface to buy what `curl` already does. |
37
37
  | `lotics file download <file_id> [<path>]` · `-o <dir>` | (alias `lotics download`) Download a stored file: `GET /v1/files/{id}/signed_url` → fetch the presigned URL and write it where you asked. **The two spellings mean two different things, and neither is read by shape: the positional `<path>` is the FILE to write, `-o <dir>` is the DIRECTORY to save into.** That is `cp` and `curl -o`, so nothing here consults an extension. A named file is written as named, its parent created, overwriting what is there — the point of naming it is that the next command opens that exact path. A directory is created if missing and written into under the stored filename (the response's `Content-Disposition`), taking a free spelling beside a file of that name already there so a repeat download never clobbers the first; with no destination at all, that filename lands in cwd. Give the destination once — a positional and `-o` together is refused, as is a positional that names an existing directory or ends in a separator (`a directory goes in -o`). The first argument is a **file id**, so a path in that slot is refused rather than sent as an id. The written path goes to **stdout** (under `--json`, `{file_id, path, filename, stored_filename}`) and the narration to stderr, so a download pipes into whatever opens it. `lotics file download record <record_id> <field_key> [-o <dir>]` spreads every file on a record's file field over a DIRECTORY — there is no single file for N files to be. |
38
- | `lotics file list [--limit <n>] [--cursor <token>]` | The workspace's files, newest first — id, upload time, bytes, MIME type, filename on stdout, one per line (`--json` for the object). `GET /v1/files` with no `file_ids`, which used to answer `[]`. **A page, not a dump**: the store only ever grows, so the last line prints the command for the next page and `next_cursor` is null on the last one. The cursor is opaque and keyset — pass it back as given — so an upload landing mid-sweep cannot make a walk skip or repeat a row. Every other file verb takes an id, so this is the only answer to "what is in here" short of reading Postgres. |
38
+ | `lotics file list [--limit <n>] [--cursor <token>]` | The workspace's files, newest first — id, upload time, bytes, MIME type, filename on stdout, one per line (`--json` for the object). `GET /v1/files` with no `file_ids`. A file holding the content of a knowledge doc or template you cannot use is left out. **A page, not a dump**: the store only ever grows, so the last line prints the command for the next page and `next_cursor` is null on the last one. The cursor is opaque and keyset — pass it back as given — so an upload landing mid-sweep cannot make a walk skip or repeat a row. Every other file verb takes an id, so this is the only answer to "what is in here" short of reading Postgres. |
39
39
  | `lotics file delete <file_id>` | Archive a stored file, over the `delete_file` tool. **Refused while a record cell, a comment, a knowledge doc, a document template or a voice session still references it** — the refusal names the referents, so this is safe to try. The bytes are left in object storage; the row no longer serves them, which is what "deleted" means here. There is no `lotics delete`: the verb needs its noun. |
40
40
  | `lotics knowledge list [--include-hidden]` | `GET /v1/knowledge_docs` — a table of id, name, tags, description (`--json` for the docs). **REST, not the `list_knowledge` tool**: the tool answers what the ASSISTANT may browse, and a hidden doc is out of that corpus by definition, so a tool-backed listing could never show one and the person who hid it would have no way back to it. Hidden docs are left out unless `--include-hidden` asks; those rows are marked `(hidden)`. |
41
41
  | `lotics knowledge create --name <n> [--description <d>] [--tags <a,b>] (--from <file.md> \| --content <str>)` | Read the body client-side (a file XOR an inline string — exactly one required), then call `create_knowledge` with `{ name, description, content, tags? }` (description defaults to `""`). `--tags` files the doc as it is made, which is the only moment a corpus reliably gets labelled. Prints the new id to stdout. Large files ride the POST body fine. |
@@ -49,26 +49,19 @@ A new doc is created **owned by you, active in your own agent context, and priva
49
49
  else can see it yet. Changing a doc's default-active state is done through the web app / REST
50
50
  surface, not this tool.
51
51
 
52
- ## Access vs. activation — both are required
52
+ ## Access
53
53
 
54
- A knowledge doc reaches an agent only when **two** conditions hold for that member:
55
-
56
- 1. **Access** — the member can `use` the doc. New docs are private to the owner; share them
57
- with other members or groups using the IAM tool `share_resource` (category **Admin**;
58
- `unshare_resource` to revoke).
59
- 2. **Activation** — the doc is *active* in that member's agent context. Activation is
60
- **separate from sharing**: a member can have access to a doc that is switched off in their
61
- context, and it won't reach their agent. Each doc has a default-active state (on by default),
62
- and each member can override it on or off for themselves. Activation is toggled in the web
63
- app, per member — there is no CLI tool for it.
64
-
65
- So: shared + active → the agent can find and read it. Shared but deactivated → invisible to
66
- that member's agent.
54
+ A knowledge doc reaches an agent only when the member can `use` the doc. New docs are private
55
+ to the owner; share them with other members or groups using the IAM tool `share_resource`
56
+ (category **Admin**; `unshare_resource` to revoke). An organization admin who is a person is no
57
+ exception: they read a doc only once it is shared with them, or once they share it with
58
+ themselves, which the audit log records. An API key acting as itself keeps the access set on the
59
+ key.
67
60
 
68
61
  ## How an agent uses a doc — ls, grep, cat
69
62
 
70
63
  Docs are not injected wholesale. The agent works the corpus like a filesystem, and all three
71
- verbs respect access + activation, so only docs the caller may use ever surface.
64
+ verbs respect access, so only docs the caller may use ever surface.
72
65
 
73
66
  1. **`list_knowledge`** — `ls`. The corpus as a flat list: id, name, tags, and a truncated
74
67
  description; `tag` narrows it. No bodies.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.241.0",
3
+ "version": "0.242.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {