@lotics/cli 0.134.0 → 0.135.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/cli.js CHANGED
@@ -72048,23 +72048,28 @@ async function appAgentSet(client, args) {
72048
72048
  noteScope({ alias: args.alias });
72049
72049
  const app = await client.getApp(meta3.app_id);
72050
72050
  const live = app.agents?.[args.alias];
72051
- if (!live) {
72052
- console.error(
72053
- `App ${meta3.app_id} has no bound agent "${args.alias}". Bind it first (set_app_agent), then 'lotics app pull' to write its instructions.`
72054
- );
72055
- process.exit(1);
72056
- }
72051
+ const isNew = live === void 0;
72057
72052
  const declared = meta3.agents?.[args.alias];
72058
72053
  const hasDeclaration = declared?.inputs !== void 0 || declared?.outputs !== void 0;
72059
72054
  const file2 = agentFilePath2(projectDir, args.alias);
72060
- if (!fs6.existsSync(file2) && !hasDeclaration) {
72061
- console.error(
72062
- `No instructions at ${path7.relative(projectDir, file2)}, and nothing declared in package.json#lotics.agents.${args.alias} to push instead.
72055
+ const hasProse = fs6.existsSync(file2);
72056
+ if (!hasProse) {
72057
+ if (isNew) {
72058
+ console.error(
72059
+ `Agent "${args.alias}" is not bound yet, so this would create it \u2014 and an agent cannot be created without instructions.
72060
+ Write ${AGENTS_DIR}/${args.alias}.md, then re-run this.`
72061
+ );
72062
+ process.exit(1);
72063
+ }
72064
+ if (!hasDeclaration) {
72065
+ console.error(
72066
+ `No instructions at ${path7.relative(projectDir, file2)}, and nothing declared in package.json#lotics.agents.${args.alias} to push instead.
72063
72067
  Run 'lotics app pull ${meta3.app_id}' to write ${AGENTS_DIR}/${args.alias}.md, then edit it.`
72064
- );
72065
- process.exit(1);
72068
+ );
72069
+ process.exit(1);
72070
+ }
72066
72071
  }
72067
- const instructions = fs6.existsSync(file2) ? stripAgentHeader(fs6.readFileSync(file2, "utf-8")) : void 0;
72072
+ const instructions = hasProse ? stripAgentHeader(fs6.readFileSync(file2, "utf-8")) : void 0;
72068
72073
  if (instructions === "") {
72069
72074
  console.error(
72070
72075
  `${path7.relative(projectDir, file2)} is empty after stripping the header \u2014 refusing to push an empty prompt.`
@@ -72094,7 +72099,10 @@ async function appAgentSet(client, args) {
72094
72099
  ...declared?.outputs !== void 0 ? ["outputs"] : []
72095
72100
  ];
72096
72101
  console.error(
72097
- `Set agent "${args.alias}" (${pushed.join(", ")}` + (live.model_tier ? `, ${live.model_tier}` : "") + `). Everything else left as it is.`
72102
+ // "Created" is not decoration: an alias that only LOOKS like the one the
72103
+ // author meant reaches here with a matching file and writes a second agent.
72104
+ // The verb is the one line that says which of the two just happened.
72105
+ `${isNew ? "Created" : "Set"} agent "${args.alias}" (${pushed.join(", ")}` + (live?.model_tier ? `, ${live.model_tier}` : "") + (isNew ? `). Nothing else is declared yet \u2014 set tool_names / knowledge_doc_ids / query_aliases with set_app_agent.` : `). Everything else left as it is.`)
72098
72106
  );
72099
72107
  }
72100
72108
  async function appWorkflowSet(client, args) {
@@ -36,7 +36,7 @@ Per-command syntax, flags, contracts, and gotchas for the public `lotics` CLI. S
36
36
  | `lotics app check` | Every pre-flight `deploy` runs, WITHOUT building or shipping: the manifest's agent schemas against the live app row, every binding a deploy would push, aliases the source calls that nothing bound (queries, workflows AND agents), bindings the app serves that the source names nowhere, capability-gated SDK calls the manifest doesn't declare, a missing icon/theme, and a notice for any alias the source computes at runtime (invisible to every check here and to the deploy's unbind guard). Adds no rule of its own — each finding is the same helper `deploy` calls, so a green check means a deploy will not complain. **Exits 1 on what a `deploy` would REFUSE or PUSH** — an agent schema that disagrees with the live app, and any binding the project has ahead of the app (an edited workflow body or declaration, edited agent prose, a changed query). Both are things a deploy would act on, so CI gating on a green check means a deploy has nothing left to do; genuine advisories (capabilities, branding, a runtime-computed alias, orphaned bindings) stay advisory and never fail it. The point is the question being ASKABLE: these checks used to cost a build, a tar, an upload and a version row in the audit trail, which is expensive enough that the honest move was to skip them and find out in production. |
37
37
  | `lotics app workflow run <alias> '<json>'` | Execute a bound app workflow end-to-end via `appWorkflow`. `app_id` comes from the local manifest; the alias must be bound (`set_app_workflow`). Inputs ingest exactly like `lotics run` (inline JSON / `@file` / stdin — bulk inputs bypass `ARG_MAX`). Prints the full `{status,message,data,files,side_effects}` JSON to stdout + a one-line summary to stderr; exits non-zero on `status:"error"` (assertable). `--print-created` (alias `--report-effects`) renders the honest post-run harvest: created records grouped by table, a paste-ready `lotics run delete_records …` per table, then the **mandatory caveat** naming what cannot be auto-undone (external integrations + notifications) and that sub-workflows may have run. `--cleanup` (DEFAULT OFF, implies the report) additionally runs the deletes for harvested records ONLY — never files / external / notifications. Neither is a rollback — a rollback is structurally impossible here. |
38
38
  | `lotics app workflow set <alias>` | Push the edited `src/workflows/<alias>.ts` body through `set_app_workflow` (the single author of `apps.workflows`). Reads the body from disk (header + `/// <reference>` + `export {};` marker + the `__workflow` wrapper all stripped) + the typed `inputs`/`outputs` **and the `description`** from `package.json#lotics.workflows.<alias>`; the **server** re-verifies the body and echoes the bound `outputs` (declared, else DERIVED from `return({ data })`). The `description` is the one line an agent reads when choosing between the app's aliases (the workflow counterpart to a query's) — authored in the manifest so it lives beside the body in version control and rides every push; omit it and the workflow keeps whatever description it already has, so a push can never blank one set elsewhere. When the manifest declared NO `outputs`, the DERIVED echo is written back into `package.json#lotics.workflows.<alias>.outputs` (a SURGICAL write — preserves `knowledge`/`config` and every other manifest field) and that alias's types are refreshed in place, so `useWorkflow("<alias>")`'s `result.data` is typed immediately with no hand-copy and no second `lotics app codegen`; an explicitly-declared `outputs` is authoritative and never overwritten. Deploy still never authors workflows — this is a CLI convenience over the existing tool. Clear error + non-zero exit on a missing file, an alias absent from the manifest, or a verify failure. |
39
- | `lotics app agent set <alias>` | Push the edited `src/agents/<alias>.md` instructions back through `set_app_agent` the agent mirror of `app workflow set`, and the deploy-free authoring path for an agent's PROSE. It sends the instructions and nothing else: the server merges against the stored declaration, so every typed field keeps exactly what is bound. This is deliberate and it is the opposite of what the symmetry with `app workflow set` suggests **the manifest is a snapshot from the last `app pull`, so replaying its typed half would silently revert whatever was bound since** (the chat authoring agent adding `knowledge_doc_ids`, another operator granting `query_aliases`), and the CLI would print success while the agent quietly lost its knowledge and its read surface. To change a typed field, call `set_app_agent` with just that field (`lotics run set_app_agent '{"app_id":…,"alias":…,"outputs":{…}}'` — it merges), then `app pull` to bring the manifest back in step. Editing `package.json#lotics.agents` by hand pushes nothing, and since that block is what types `useAgentRun`, `codegen` warns and `deploy` REFUSES while it disagrees with the live app. Clear error + non-zero exit on a missing file, an alias absent from the manifest, or a file that is empty once the header is stripped (refusing to push an empty prompt). `app pull` writes the file; edit, then `set`. |
39
+ | `lotics app agent set <alias>` | Push `src/agents/<alias>.md` plus `inputs`/`outputs` when `package.json#lotics.agents.<alias>` declares them — through `set_app_agent`. The agent mirror of `app workflow set`, and the deploy-free authoring path for an agent's prose and its typed edges. **It sends only those fields.** Everything else is absent, and absent means unchanged, so a declaration this CLI does not model cannot be reverted by a push from a checkout that predates it the chat authoring agent's `knowledge_doc_ids`, another operator's `query_aliases` grant. To change one of those, call `set_app_agent` with just that field (`lotics run set_app_agent '{"app_id":…,"alias":…,"tool_names":[]}'` — it merges), then `app pull` to bring the manifest back in step. **CREATES the alias when the app has not bound one yet**, so a new agent is authored the same way a new workflow is: write the prose, declare the typed half, push. A create needs the prose file (an agent without instructions is not an agent); it is gated on nothing else, because what keeps a binding alive is a `useAppAgentRun("<alias>")` call site in the shipped bundle — a deploy prunes an agent the bundle never names, manifest entry or not. The prose push is a conditional write against the fingerprint this project last saw, so it is refused rather than allowed to overwrite prose someone else changed. Clear error + non-zero exit when there is no prose file and nothing declared to push instead, when a create has no prose to create from, or when the file is empty once the header is stripped. |
40
40
  | `lotics app query set <alias>` \| `--all` | Push `package.json#lotics.queries` (`{ ast, params? }` per alias) to `apps.queries` through `set_app_query` — **the only author of a query binding**, the mirror of `app workflow set`. A deploy ships code and binds nothing. The **server** validates each one exactly as it always did (alias identifier, workspace-only tables, resolvable fields, declared params). `--all` pushes every declared alias, alias-sorted, stopping at the first failure and naming what already landed. Clear error + non-zero exit on an alias absent from the manifest or a validation failure. |
41
41
  | `lotics app agent run <app_id> <alias> ['<json>'\|@file\|stdin]` | Run a bound app agent end-to-end. A run needs no deployed UI bundle — just the app row + the bound agent declaration + member auth — so the **`app_id` is explicit** (not read from a local manifest). Inputs ingest exactly like `lotics run` (inline JSON / `@file` / stdin; empty = `{}`). Opens the run's SSE (`appAgentRunStream`), streams `text-delta` prose to **stderr** as live progress, then reports from the **settled run RECORD** (`listAgentRuns`, polled to a terminal status — the client stream can close a beat before the run settles, or drop while it runs on server-side): default prints the run's structured `output` (JSON) or final text to **stdout** + a status line to stderr; `--json` prints the full run summary to stdout. Selects THIS run by the `x-app-agent-run-id` header (ordering-independent). Exits 0 **only** when the settled status is `completed`; otherwise non-zero with the run's error surfaced. A settled run that never appears fails loudly (never a silent success). A fresh `session_id` is minted per run (self-contained); `--session <id>` continues an existing thread (prior runs become the agent's context). |
42
42
  | `lotics app workflow pull` | Rewrite every `src/workflows/<alias>.ts` from the server (faithful body per bound alias via `get_app_workflow`) **+ its `.lotics/workflows/<alias>.globals.d.ts`** (via `getAppWorkflowDts`, so the body is locally typecheckable via `lotics app workflow check`) without a full `app pull` (no source archive, no npm install). A legacy alias with no rendered source warns and is skipped; a dts-fetch failure is non-fatal (body still written with the fallback wrapper, typecheck degraded). Each alias's `description` is folded back into `package.json#lotics.workflows.<alias>` from the same read — the alias binding the manifest is otherwise stamped from carries `inputs`/`outputs` but not the description, which lives on the workflow ROW, so without this a pull would erase an authored one. The server's GENERATED default is skipped, so an app that never described its workflows gains no manifest noise. Also idempotently patches the main `tsconfig.json` `exclude` to cover `src/workflows` + `.lotics/workflows` so a pre-existing app's `npm run typecheck` never loads the bodies or the colliding per-alias globals. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.134.0",
3
+ "version": "0.135.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {