@lotics/cli 0.245.0 → 0.247.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 +2 -3
- package/README.md +12 -13
- package/dist/probe_page.js +104 -219
- package/dist/src/cli.js +26776 -38022
- package/dist/src/client.d.ts +9 -63
- package/dist/src/client.js +2 -4
- package/docs/building_an_app.md +55 -57
- package/docs/cli_reference.md +13 -14
- package/docs/migration.md +49 -58
- package/package.json +1 -1
- package/docs/clauses.md +0 -135
package/dist/src/client.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface AppQueryBody {
|
|
|
37
37
|
keyset?: boolean;
|
|
38
38
|
/** The previous page's `next_cursor`, in keyset mode. */
|
|
39
39
|
cursor?: string;
|
|
40
|
+
/** Answer the groups of the filtered set in place of its rows: by up to two output columns and the day of an
|
|
41
|
+
* output date, each group's `__count` and `__sum` of one output number. */
|
|
42
|
+
aggregate?: {
|
|
43
|
+
by?: string[];
|
|
44
|
+
day?: string;
|
|
45
|
+
sum?: string;
|
|
46
|
+
};
|
|
40
47
|
}
|
|
41
48
|
/** Result of the app query RPC. `total` answers a `count` read; `truncated` and
|
|
42
49
|
* `next_cursor` a rows read — `next_cursor` in keyset mode only, null on the
|
|
@@ -370,8 +377,6 @@ export interface ScaffoldWorkspaceRequest {
|
|
|
370
377
|
}>>;
|
|
371
378
|
/** Bind an entity whose label already names a table here. Absent, a colliding label is refused. */
|
|
372
379
|
adopt?: boolean;
|
|
373
|
-
/** The account each connection alias pushes through, by `cac_` id — where more than one of its provider is usable. */
|
|
374
|
-
connections?: Record<string, string>;
|
|
375
380
|
}
|
|
376
381
|
export interface ScaffoldWorkspaceResult {
|
|
377
382
|
/** Every entity the model declares, in contract order. */
|
|
@@ -423,48 +428,6 @@ export interface ScaffoldWorkspaceResult {
|
|
|
423
428
|
* whose rule nobody named is the one a reader has to be told about.
|
|
424
429
|
*/
|
|
425
430
|
carried_rules?: string[];
|
|
426
|
-
/**
|
|
427
|
-
* Every table automation the model declares that this run wrote, with the
|
|
428
|
-
* table workflow it is: created by this run, or rewritten in place — found
|
|
429
|
-
* through the binding (`id`) or by its label on its table — or `restored`
|
|
430
|
-
* from archived. `enabled` is whether it fires — a rewrite keeps the owner's
|
|
431
|
-
* switch — and is absent from an instance too old to say. Absent from an
|
|
432
|
-
* instance too old to provision one, which also refuses a model that declares
|
|
433
|
-
* any.
|
|
434
|
-
*/
|
|
435
|
-
automations?: Array<{
|
|
436
|
-
alias: string;
|
|
437
|
-
entity: string;
|
|
438
|
-
table_workflow_id: string;
|
|
439
|
-
bound_by: "created" | "id" | "label" | "restored";
|
|
440
|
-
enabled?: boolean;
|
|
441
|
-
}>;
|
|
442
|
-
/**
|
|
443
|
-
* Automations this run neither created nor rewrote: deployed app workflows
|
|
444
|
-
* write the table each keeps (`keeps`, an entity alias) themselves, and both
|
|
445
|
-
* writing it would land every row twice. `live` is the table workflow already
|
|
446
|
-
* writing it beside them — twice NOW — else null. Absent from an instance too
|
|
447
|
-
* old to hold one back.
|
|
448
|
-
*/
|
|
449
|
-
held_automations?: Array<{
|
|
450
|
-
alias: string;
|
|
451
|
-
entity: string;
|
|
452
|
-
keeps: string;
|
|
453
|
-
writers: AppTableWriter[];
|
|
454
|
-
live: string | null;
|
|
455
|
-
}>;
|
|
456
|
-
/**
|
|
457
|
-
* The account each connection the model declares pushes through: named by the
|
|
458
|
-
* caller (`chosen`), kept from the binding this workspace held (`id`), or the
|
|
459
|
-
* only account of its provider the applier can use (`only`). Absent from an
|
|
460
|
-
* instance too old to bind one, which also refuses a model that declares any.
|
|
461
|
-
*/
|
|
462
|
-
connections?: Array<{
|
|
463
|
-
alias: string;
|
|
464
|
-
connected_account_id: string;
|
|
465
|
-
provider: string;
|
|
466
|
-
bound_by: "chosen" | "id" | "only";
|
|
467
|
-
}>;
|
|
468
431
|
/**
|
|
469
432
|
* Options of an ADOPTED field that kept a mark other than the model's, by
|
|
470
433
|
* binding key — every surface draws the live one. Absent from an instance too
|
|
@@ -514,22 +477,11 @@ export interface BoundTarget {
|
|
|
514
477
|
export interface BoundField extends BoundTarget {
|
|
515
478
|
options: Record<string, BoundTarget>;
|
|
516
479
|
}
|
|
517
|
-
/** One app workflow alias whose deployed body writes a table's rows. */
|
|
518
|
-
export interface AppTableWriter {
|
|
519
|
-
app_id: string;
|
|
520
|
-
app_name: string;
|
|
521
|
-
workflow_alias: string;
|
|
522
|
-
}
|
|
523
480
|
/** One entity of the model, as this workspace holds it. */
|
|
524
481
|
export interface BoundEntity {
|
|
525
482
|
table: string;
|
|
526
483
|
live_label: string | null;
|
|
527
484
|
fields: Record<string, BoundField>;
|
|
528
|
-
/**
|
|
529
|
-
* The deployed app workflows that write this table — only on an entity the
|
|
530
|
-
* read named in `app_writers`, and absent from an instance too old to say.
|
|
531
|
-
*/
|
|
532
|
-
app_writers?: AppTableWriter[];
|
|
533
485
|
}
|
|
534
486
|
/**
|
|
535
487
|
* WHAT EACH ALIAS OF A WORKSPACE MODEL BECAME IN ONE WORKSPACE.
|
|
@@ -544,16 +496,12 @@ export interface ModelBinding {
|
|
|
544
496
|
entities: Record<string, BoundEntity>;
|
|
545
497
|
templates: Record<string, BoundTarget>;
|
|
546
498
|
roles: Record<string, BoundTarget>;
|
|
547
|
-
/** Automation alias → the table workflow it became. Absent from an instance too old to bind one. */
|
|
548
|
-
automations?: Record<string, BoundTarget>;
|
|
549
499
|
/** `<entity-alias>:<row-ref>` → the record that first row became. */
|
|
550
500
|
rows: Record<string, string>;
|
|
551
501
|
/** The path a row attached a document by → the file it was uploaded as. */
|
|
552
502
|
documents: Record<string, string>;
|
|
553
|
-
/** A
|
|
503
|
+
/** A model's app alias → the app created for it here. Absent from an instance too old to bind one. */
|
|
554
504
|
apps?: Record<string, BoundTarget>;
|
|
555
|
-
/** A connection alias → the connected account an apply chose for it. Absent from an instance too old to bind one. */
|
|
556
|
-
connections?: Record<string, BoundTarget>;
|
|
557
505
|
}
|
|
558
506
|
/**
|
|
559
507
|
* A workspace read BACK as a model — the inverse of the scaffold above.
|
|
@@ -884,9 +832,7 @@ export declare class LoticsClient {
|
|
|
884
832
|
* additive verb creates a second table over. Admin-only, like the scaffold it
|
|
885
833
|
* is the memory of.
|
|
886
834
|
*/
|
|
887
|
-
getModelBinding(
|
|
888
|
-
app_writers?: readonly string[];
|
|
889
|
-
}): Promise<ModelBinding>;
|
|
835
|
+
getModelBinding(): Promise<ModelBinding>;
|
|
890
836
|
/**
|
|
891
837
|
* Forget what one alias is bound to here — the escape from a binding whose
|
|
892
838
|
* target has been deleted. Forgets everything addressed under it too: an
|
package/dist/src/client.js
CHANGED
|
@@ -396,10 +396,8 @@ var LoticsClient = class {
|
|
|
396
396
|
* additive verb creates a second table over. Admin-only, like the scaffold it
|
|
397
397
|
* is the memory of.
|
|
398
398
|
*/
|
|
399
|
-
async getModelBinding(
|
|
400
|
-
|
|
401
|
-
const qs = writers.length === 0 ? "" : `?${new URLSearchParams({ app_writers: writers.join(",") }).toString()}`;
|
|
402
|
-
return this.request("GET", `/v1/workspaces/model/binding${qs}`);
|
|
399
|
+
async getModelBinding() {
|
|
400
|
+
return this.request("GET", "/v1/workspaces/model/binding");
|
|
403
401
|
}
|
|
404
402
|
/**
|
|
405
403
|
* Forget what one alias is bound to here — the escape from a binding whose
|
package/docs/building_an_app.md
CHANGED
|
@@ -13,43 +13,49 @@ using one to find out whether something works is the slowest possible way to lea
|
|
|
13
13
|
## 1 — Scaffold, or pull what exists
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
lotics app preview model.json#<app> --shots shots/ # the
|
|
16
|
+
lotics app preview model.json#<app> --shots shots/ # the model's app rendered: no workspace
|
|
17
17
|
lotics app create "<name>" # new: a real Vite + React + TS project, deps installed
|
|
18
|
-
lotics app create "<name>" --from model.json#<app> # new, from a checked
|
|
18
|
+
lotics app create "<name>" --from model.json#<app> # new, from a checked model: app.json, rendered
|
|
19
19
|
lotics app create "<name>" --api # new, no screens: its declarations are the whole surface
|
|
20
|
-
cd <dir> && lotics app regenerate # existing + generated:
|
|
20
|
+
cd <dir> && lotics app regenerate # existing + generated: recompile it from the model
|
|
21
21
|
cd <dir> && lotics app pull <app_id> # existing: refresh to the latest first, then read its README
|
|
22
|
-
lotics workspace build model.json # the whole
|
|
22
|
+
lotics workspace build model.json # the whole model: check, apply, then every app in it
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
`lotics workspace build` is the create and regenerate lines above in one, for a model that
|
|
26
26
|
declares several apps: `scaffold check`, `scaffold apply` where this workspace differs from the
|
|
27
|
-
model
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
and
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
model, then per app `create --from` into `<dir-of-model>/<alias-with-dashes>` when that directory
|
|
28
|
+
does not exist and `regenerate` there when it does, then `app check` — and `app deploy` under
|
|
29
|
+
`--deploy`. An app that conflicts or checks red is named and the rest still run, because the one
|
|
30
|
+
you are going to fix is not a reason to leave the others in a state nobody knows. `--dry-run`
|
|
31
|
+
writes nothing and says which apps it would create and which it would regenerate.
|
|
32
|
+
|
|
33
|
+
**An app is stated, not drawn.** In `model.json`, `records` says how a row of each entity is
|
|
34
|
+
recognised — its title, the line under it, its picture, its status, its figure, its deadlines — and
|
|
35
|
+
each entry of `apps` is one register over one entity and the record its rows open: the register's
|
|
36
|
+
columns and filters, the record's fact groups and blocks, the acts and the checks that guard them.
|
|
37
|
+
That is the whole vocabulary (`lotics docs model/apps`). How each piece looks is the runtime's, one
|
|
38
|
+
way per concept, and no key changes it. `lotics scaffold check` prints each app as its reader will
|
|
39
|
+
see it, with every value a default supplied marked `(default)` — review the app there, before
|
|
40
|
+
anything exists.
|
|
41
|
+
|
|
42
|
+
**Look at it before anything exists.** `lotics app preview <model.json>#<app>` compiles the app
|
|
43
|
+
against the model's own ids, answers every read from the model's `rows` (a few synthesized for an
|
|
44
|
+
entity that states none), renders the register, the record in its door and the add dialog at 1280
|
|
45
|
+
and 375, and runs the probes `app check --screens` runs — no workspace, no credential, nothing
|
|
46
|
+
created. `--shots <dir>` writes what it measured as PNGs.
|
|
42
47
|
|
|
43
48
|
`--from` takes the model file `lotics scaffold check` approved and the app in it, and writes
|
|
44
|
-
**`app.json`** — the
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
**`app.json`** — the app compiled against the live `tbl_`/`fld_`/`opt_` ids its entities became —
|
|
50
|
+
beside a `src/main.tsx` that mounts `@lotics/app-runtime` over it, one `src/workflows/<alias>.ts`
|
|
51
|
+
per write and an empty `src/components/index.ts`. Every write — a create, an edit, a remove, each
|
|
52
|
+
act — is a generated workflow that re-checks on the server what the model states: required fields,
|
|
53
|
+
`write_rules`, an act's `when` and `requires`, and the checks that block it. The runtime draws the
|
|
48
54
|
spec; there is no screen source to edit (`node_modules/@lotics/app-runtime/AGENTS.md`). The tables
|
|
49
55
|
have to exist (`lotics scaffold apply` first); a table or field the workspace lacks is refused by
|
|
50
|
-
name. The app is recorded against its
|
|
51
|
-
refused
|
|
52
|
-
|
|
56
|
+
name. The app is recorded against its model alias, so a second `--from` for the same alias is
|
|
57
|
+
refused. The project's `README.md` is the app in prose: its register, its record and the tables
|
|
58
|
+
behind them.
|
|
53
59
|
|
|
54
60
|
`--api` scaffolds the app something OUTSIDE Lotics calls (§ 9): the manifest, `src/workflows/`, a
|
|
55
61
|
CI job and the two briefs, with no `index.html`, no `src/App.tsx`, no Vite config and no kit. Its
|
|
@@ -67,15 +73,15 @@ A pull writes more than source: one `src/workflows/<alias>.ts` per bound workflo
|
|
|
67
73
|
`src/agents/<alias>.md` per bound agent, and the `.lotics/` type companions — so an existing app
|
|
68
74
|
arrives fully editable rather than as an archive you have to reconstruct.
|
|
69
75
|
|
|
70
|
-
**The
|
|
71
|
-
inside the app; the
|
|
72
|
-
another. The generator owns what it emits: `app.json` is
|
|
73
|
-
whole, and a workflow body is rewritten too, with the text it replaced parked in
|
|
74
|
-
`.lotics/regenerate-dropped.patch`.
|
|
75
|
-
absent, never rewritten
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
retired removed, the ones you added kept — then codegen and `app check` run.
|
|
76
|
+
**The model changes after the app is built, and `lotics app regenerate` is how it lands.** Run it
|
|
77
|
+
inside the app; the model is the one `package.json#lotics.plan` remembers, unless `--from` names
|
|
78
|
+
another. The generator owns what it emits: `app.json` is compiled from the model and rewritten
|
|
79
|
+
whole, and a generated workflow body is rewritten too, with the text it replaced parked in
|
|
80
|
+
`.lotics/regenerate-dropped.patch`. Two things are yours and survive every regeneration:
|
|
81
|
+
`src/components/` (seeded where it is absent, never rewritten), and the body of an act's own
|
|
82
|
+
`workflow` — only the guard region at its top, between the `<lotics:guards>` markers, is the
|
|
83
|
+
generator's. The manifest is reconciled by ownership — the generator's aliases replaced, the ones
|
|
84
|
+
it retired removed, the ones you added kept — then codegen and `app check` run.
|
|
79
85
|
|
|
80
86
|
**Nothing is pushed.** What the live app RUNS changes at `lotics app deploy` and nowhere else: the
|
|
81
87
|
bundle production serves was built against the bindings it has, so a regeneration that replaced a
|
|
@@ -275,29 +281,21 @@ Authoring rules for the body itself: `lotics docs workflows`.
|
|
|
275
281
|
|
|
276
282
|
## 7 — Screens
|
|
277
283
|
|
|
278
|
-
**A
|
|
279
|
-
register, the record each row opens, its
|
|
280
|
-
|
|
281
|
-
ruling is about how every app draws, a change to the runtime that reaches every app
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
`
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
**Where the plan has no word for a screen, a section or an act,** the spec names one of the app's
|
|
290
|
-
own components — `lotics app eject` writes it from exactly what the runtime was drawing, one part at
|
|
291
|
-
a time, and `lotics docs components` says what it is handed. A screen no shape fits is declared in
|
|
292
|
-
the plan as `"shape": "custom"` with its slots as roles (`lotics docs model/apps-and-screens`)
|
|
293
|
-
— never a shape bent to fit — and then `lotics report` with `wanted` opening `shape <name>`: a custom
|
|
294
|
-
slot set that recurs becomes a shape, and the report is how the next build gets it. A component
|
|
295
|
-
the kit lacks is built into the kit, never hand-rolled in one app.
|
|
284
|
+
**A model-built app's screens are its `app.json`, and `@lotics/app-runtime` draws them.** The
|
|
285
|
+
register, the record each row opens, its facts and blocks, the acts and the checks are all stated by
|
|
286
|
+
the model, so a correction is a change to the MODEL followed by `lotics app regenerate` — or, where
|
|
287
|
+
the ruling is about how every app draws, a change to the runtime or the kit that reaches every app
|
|
288
|
+
on its next install.
|
|
289
|
+
|
|
290
|
+
**Where the vocabulary has no word for what a record needs,** a `component` block names one of the
|
|
291
|
+
app's own components (`lotics docs components`), and an act whose write `set` cannot say names its
|
|
292
|
+
own `workflow`, which runs after the generated guards. A component the kit lacks is built into the
|
|
293
|
+
kit, never hand-rolled in one app.
|
|
296
294
|
|
|
297
295
|
**A hand-written app composes the kit itself.** Before any JSX, read `lotics docs ui` — the catalog
|
|
298
|
-
and the composition grammar; the
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
and the composition grammar; the pieces the runtime draws a record with (`RecordFrame`,
|
|
297
|
+
`RecordHeader`, `ChecksCallout`, `FactGroups`, `ActPanel`) and the register's (`Table` with a `Row`
|
|
298
|
+
subject, `FilterBand` with `StatusChips` and `GroupByMenu`) are there to compose rather than rebuild.
|
|
301
299
|
|
|
302
300
|
Two rules for any code the app writes itself — a component or a hand-written screen:
|
|
303
301
|
|
|
@@ -424,7 +422,7 @@ Once scaffolded, everything below happens locally:
|
|
|
424
422
|
|
|
425
423
|
```
|
|
426
424
|
edit src/workflows/<alias>.ts # or a component, or a query
|
|
427
|
-
lotics app preview model.json#<app> --shots shots/ # when the
|
|
425
|
+
lotics app preview model.json#<app> --shots shots/ # when the MODEL moved: see it before a table does
|
|
428
426
|
lotics app regenerate --dry-run # then: what the new generation would do to this app
|
|
429
427
|
lotics app codegen # after any schema change
|
|
430
428
|
npm run typecheck # honest, because codegen is current
|