@lotics/cli 0.315.17 → 0.316.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.
@@ -75,11 +75,6 @@ export interface LoticsClientOptions {
75
75
  * instance's key to production (`docs/on_premise.md` section 8). */
76
76
  apiUrl: string;
77
77
  workspaceId?: string;
78
- /** Admin "View as": when set, every request carries `x-view-as-member-id`, so
79
- * the backend evaluates IAM scoping (and `is_current_member`) as this member.
80
- * Admin-only — the server rejects a non-admin key. Writes stay attributed to
81
- * the key's owner. */
82
- viewAsMemberId?: string;
83
78
  }
84
79
  export interface WorkspaceInfo {
85
80
  id: string;
@@ -139,7 +134,7 @@ export interface ToolInfo {
139
134
  * A single knowledge doc with its HYDRATED body — the shape of
140
135
  * `GET /v1/knowledge_docs/{id}`, and the one content-read path a non-sandbox
141
136
  * client has. `content_sha` is the concurrency token the REST PATCH echoes back
142
- * as `expected_content_sha`; the `update_knowledge` TOOL CASes internally, so a
137
+ * as `expected_content_sha`; the `set_knowledge` TOOL CASes internally, so a
143
138
  * CLI caller never needs to pass it.
144
139
  */
145
140
  /** A doc's metadata, as every listing serves it — no body. */
@@ -171,42 +166,6 @@ export interface FileUploadResult {
171
166
  error: string;
172
167
  }>;
173
168
  }
174
- /** One bound alias: the live thing it names, and what the workspace calls that now. */
175
- export interface BoundTarget {
176
- id: string;
177
- /** Null when this workspace no longer holds it — deleted or archived. */
178
- live_label: string | null;
179
- }
180
- /** One column of a bound entity, with the options of a select under it. */
181
- export interface BoundField extends BoundTarget {
182
- options: Record<string, BoundTarget>;
183
- }
184
- /** One entity of the model, as this workspace holds it. */
185
- export interface BoundEntity {
186
- table: string;
187
- live_label: string | null;
188
- fields: Record<string, BoundField>;
189
- }
190
- /**
191
- * WHAT EACH ALIAS OF A WORKSPACE MODEL BECAME IN ONE WORKSPACE.
192
- *
193
- * The join a model file cannot carry, kept by the workspace rather than beside
194
- * the file — a file is never committed, so memory next to it is one author's
195
- * disk. Every bound thing arrives with the label the workspace gives it NOW,
196
- * which is the half a client cannot compute without a read per table, and the
197
- * half that turns "one missing, one extra" into "renamed".
198
- */
199
- export interface ModelBinding {
200
- entities: Record<string, BoundEntity>;
201
- templates: Record<string, BoundTarget>;
202
- roles: Record<string, BoundTarget>;
203
- /** `<entity-alias>:<row-ref>` → the record that first row became. */
204
- rows: Record<string, string>;
205
- /** The path a row attached a document by → the file it was uploaded as. */
206
- documents: Record<string, string>;
207
- /** A model's app alias → the app created for it here. Absent from an instance too old to bind one. */
208
- apps?: Record<string, BoundTarget>;
209
- }
210
169
  /**
211
170
  * A request the API refused. The message carries the status and the server's
212
171
  * sentence, which is what reaches a person; `status` and `body` are for the
@@ -272,8 +231,6 @@ export declare function pollCliLogin(apiUrl: string, request_id: string, secret:
272
231
  export declare class LoticsClient {
273
232
  private apiKey;
274
233
  private workspaceId;
275
- /** The active "View as" target member id, if any; sent on every request. */
276
- private readonly viewAsMemberId;
277
234
  /** The instance this client sends every request to. */
278
235
  readonly baseUrl: string;
279
236
  constructor(options: LoticsClientOptions);
@@ -341,27 +298,6 @@ export declare class LoticsClient {
341
298
  timezone?: string;
342
299
  default_currency?: string;
343
300
  }): Promise<WorkspaceInfo>;
344
- /**
345
- * What each alias of a workspace model became here, with what this workspace
346
- * calls every one of them now.
347
- *
348
- * ONE request for a join a client would otherwise make by label — and by label
349
- * a renamed table is one thing missing and one thing extra, which is what an
350
- * additive verb creates a second table over. Admin-only, like the scaffold it
351
- * is the memory of.
352
- */
353
- getModelBinding(): Promise<ModelBinding>;
354
- /**
355
- * Record which file each of a model's document PATHS was uploaded as.
356
- *
357
- * The one part of a binding the server cannot observe — a path is this
358
- * machine's filesystem and never reaches the wire — so the uploader states the
359
- * pair. It is what makes re-attaching a model's documents upload nothing a
360
- * second time.
361
- */
362
- recordModelDocuments(documents: Record<string, string>): Promise<{
363
- recorded: number;
364
- }>;
365
301
  /** Renames (and re-settings) the CURRENT workspace — the endpoint reads the
366
302
  * target from the request's workspace, never a path id. */
367
303
  updateWorkspace(body: {
@@ -403,13 +339,13 @@ export declare class LoticsClient {
403
339
  }): Promise<ToolExecuteResult>;
404
340
  /**
405
341
  * Fetch one knowledge doc with its hydrated `content` — the single content-read
406
- * path for a non-sandbox client (the `list_knowledge` tool returns metadata
342
+ * path for a non-sandbox client (the `get_workspace` tool returns metadata
407
343
  * only, and the sandbox-staging read path is unavailable here). Works for both
408
344
  * the file-model and legacy parked-column rows. Mirrors GET /v1/knowledge_docs/{id}.
409
345
  */
410
346
  getKnowledgeDoc(knowledge_doc_id: string): Promise<KnowledgeDocDetail>;
411
347
  /**
412
- * Every doc's metadata — REST rather than the `list_knowledge` tool, because
348
+ * Every doc's metadata — REST rather than the `get_workspace` tool, because
413
349
  * this is the surface a PERSON manages the corpus from and the tool is bound
414
350
  * to what the assistant may browse. That distinction is the whole point of
415
351
  * `include_hidden`: a hidden doc is out of the assistant's corpus by design,
@@ -446,6 +382,14 @@ export declare class LoticsClient {
446
382
  workspace_id: string;
447
383
  current_version_id: string | null;
448
384
  }>;
385
+ /** The `.lotics/` declarations that type a custom-code app's hooks by its live bindings — `GET /v1/apps/{app_id}/types`. */
386
+ getAppTypes(appId: string, sdk: "@lotics/app-sdk" | "@lotics/app-runtime/sdk"): Promise<{
387
+ app_id: string;
388
+ files: Array<{
389
+ path: string;
390
+ content: string;
391
+ }>;
392
+ }>;
449
393
  /** A built app version's project source, as the `source.tar.gz` its deploy uploaded. */
450
394
  downloadAppVersionSource(appId: string, versionId: string): Promise<Buffer>;
451
395
  /**
@@ -462,26 +406,6 @@ export declare class LoticsClient {
462
406
  name: string;
463
407
  };
464
408
  }>>;
465
- /**
466
- * Resolve the display name + fields (incl. select options) of the given tables.
467
- * One `get_table` call per id (the tool surface has no batch variant); a
468
- * missing/inaccessible table is dropped rather than throwing, so a stale id in
469
- * the scope set never fails the caller.
470
- */
471
- getWorkspaceSchema(tableIds: string[]): Promise<Array<{
472
- id: string;
473
- name: string;
474
- fields: Array<{
475
- id: string;
476
- name: string;
477
- type: string;
478
- multi?: boolean;
479
- options?: Array<{
480
- id: string;
481
- label: string;
482
- }>;
483
- }>;
484
- }>>;
485
409
  /**
486
410
  * Run a named query declared in the app's manifest, scoped to the app's IAM
487
411
  * principal. Mirrors POST /v1/apps/{app_id}/query.
@@ -535,7 +459,7 @@ export declare class LoticsClient {
535
459
  filename: string;
536
460
  stored_filename: string;
537
461
  }>;
538
- downloadRecordFiles(recordId: string, fieldKey: string, outputDir?: string): Promise<Array<{
462
+ downloadRecordFiles(recordId: string, fieldAlias: string, outputDir?: string): Promise<Array<{
539
463
  path: string;
540
464
  filename: string;
541
465
  file_id: string;
@@ -210,14 +210,11 @@ function unparsedAnswer(text) {
210
210
  var LoticsClient = class {
211
211
  apiKey;
212
212
  workspaceId;
213
- /** The active "View as" target member id, if any; sent on every request. */
214
- viewAsMemberId;
215
213
  /** The instance this client sends every request to. */
216
214
  baseUrl;
217
215
  constructor(options) {
218
216
  this.apiKey = options.apiKey;
219
217
  this.workspaceId = options.workspaceId;
220
- this.viewAsMemberId = options.viewAsMemberId;
221
218
  this.baseUrl = options.apiUrl;
222
219
  }
223
220
  /**
@@ -277,9 +274,6 @@ var LoticsClient = class {
277
274
  if (this.workspaceId) {
278
275
  headers["x-workspace-id"] = this.workspaceId;
279
276
  }
280
- if (this.viewAsMemberId) {
281
- headers["x-view-as-member-id"] = this.viewAsMemberId;
282
- }
283
277
  if (invocation2) {
284
278
  headers["x-lotics-cli-command"] = invocation2.command;
285
279
  headers[CLI_CAPABILITIES_HEADER] = CLI_CAPABILITIES.join(",");
@@ -341,29 +335,6 @@ var LoticsClient = class {
341
335
  async createWorkspace(body) {
342
336
  return this.request("POST", "/v1/workspaces", body);
343
337
  }
344
- /**
345
- * What each alias of a workspace model became here, with what this workspace
346
- * calls every one of them now.
347
- *
348
- * ONE request for a join a client would otherwise make by label — and by label
349
- * a renamed table is one thing missing and one thing extra, which is what an
350
- * additive verb creates a second table over. Admin-only, like the scaffold it
351
- * is the memory of.
352
- */
353
- async getModelBinding() {
354
- return this.request("GET", "/v1/workspaces/model/binding");
355
- }
356
- /**
357
- * Record which file each of a model's document PATHS was uploaded as.
358
- *
359
- * The one part of a binding the server cannot observe — a path is this
360
- * machine's filesystem and never reaches the wire — so the uploader states the
361
- * pair. It is what makes re-attaching a model's documents upload nothing a
362
- * second time.
363
- */
364
- async recordModelDocuments(documents) {
365
- return this.request("PUT", "/v1/workspaces/model/binding/documents", { documents });
366
- }
367
338
  /** Renames (and re-settings) the CURRENT workspace — the endpoint reads the
368
339
  * target from the request's workspace, never a path id. */
369
340
  async updateWorkspace(body) {
@@ -411,7 +382,7 @@ var LoticsClient = class {
411
382
  // --- Knowledge docs ---
412
383
  /**
413
384
  * Fetch one knowledge doc with its hydrated `content` — the single content-read
414
- * path for a non-sandbox client (the `list_knowledge` tool returns metadata
385
+ * path for a non-sandbox client (the `get_workspace` tool returns metadata
415
386
  * only, and the sandbox-staging read path is unavailable here). Works for both
416
387
  * the file-model and legacy parked-column rows. Mirrors GET /v1/knowledge_docs/{id}.
417
388
  */
@@ -419,7 +390,7 @@ var LoticsClient = class {
419
390
  return this.request("GET", `/v1/knowledge_docs/${encodeURIComponent(knowledge_doc_id)}`);
420
391
  }
421
392
  /**
422
- * Every doc's metadata — REST rather than the `list_knowledge` tool, because
393
+ * Every doc's metadata — REST rather than the `get_workspace` tool, because
423
394
  * this is the surface a PERSON manages the corpus from and the tool is bound
424
395
  * to what the assistant may browse. That distinction is the whole point of
425
396
  * `include_hidden`: a hidden doc is out of the assistant's corpus by design,
@@ -441,6 +412,10 @@ var LoticsClient = class {
441
412
  async getApp(appId) {
442
413
  return this.request("GET", `/v1/apps/${encodeURIComponent(appId)}`);
443
414
  }
415
+ /** The `.lotics/` declarations that type a custom-code app's hooks by its live bindings — `GET /v1/apps/{app_id}/types`. */
416
+ async getAppTypes(appId, sdk) {
417
+ return this.request("GET", `/v1/apps/${encodeURIComponent(appId)}/types?sdk=${encodeURIComponent(sdk)}`);
418
+ }
444
419
  /** A built app version's project source, as the `source.tar.gz` its deploy uploaded. */
445
420
  async downloadAppVersionSource(appId, versionId) {
446
421
  const { url } = await this.request(
@@ -459,45 +434,6 @@ var LoticsClient = class {
459
434
  async getWorkspaceDanglingReferences() {
460
435
  return this.request("GET", "/v1/workspaces/dangling-references");
461
436
  }
462
- /**
463
- * Resolve the display name + fields (incl. select options) of the given tables.
464
- * One `get_table` call per id (the tool surface has no batch variant); a
465
- * missing/inaccessible table is dropped rather than throwing, so a stale id in
466
- * the scope set never fails the caller.
467
- */
468
- async getWorkspaceSchema(tableIds) {
469
- const tables = await Promise.all(
470
- tableIds.map(async (table_id) => {
471
- const res = await this.execute("get_table", { table_id });
472
- if (res.error || res.result === null || typeof res.result !== "object") return null;
473
- const table = res.result;
474
- if (typeof table.id !== "string" || typeof table.name !== "string" || !Array.isArray(table.fields)) {
475
- return null;
476
- }
477
- const fields = table.fields.flatMap((field) => {
478
- if (field === null || typeof field !== "object") return [];
479
- const f = field;
480
- if (typeof f.key !== "string" || typeof f.name !== "string" || typeof f.type !== "string") return [];
481
- const options = Array.isArray(f.options) ? f.options.flatMap((opt) => {
482
- if (opt === null || typeof opt !== "object") return [];
483
- const o = opt;
484
- return typeof o.key === "string" && typeof o.name === "string" ? [{ id: o.key, label: o.name }] : [];
485
- }) : void 0;
486
- return [
487
- {
488
- id: f.key,
489
- name: f.name,
490
- type: f.type,
491
- ...f.multi === true ? { multi: true } : {},
492
- ...options && options.length > 0 ? { options } : {}
493
- }
494
- ];
495
- });
496
- return { id: table.id, name: table.name, fields };
497
- })
498
- );
499
- return tables.filter((t) => t !== null);
500
- }
501
437
  // ── App iframe RPC endpoints ──────────────────────────────────────────────
502
438
  // These mirror the two ops handled by frontend/features/app_ui/app_iframe_host.tsx.
503
439
  // The deployed iframe sends postMessage to the parent frontend, which calls
@@ -588,17 +524,17 @@ var LoticsClient = class {
588
524
  await fs.promises.writeFile(absolutePath, buffer);
589
525
  return { path: absolutePath, filename, stored_filename: originalFilename };
590
526
  }
591
- async downloadRecordFiles(recordId, fieldKey, outputDir) {
527
+ async downloadRecordFiles(recordId, fieldAlias, outputDir) {
592
528
  const result = await this.execute("get_record", { record_id: recordId }, { format: "text" });
593
529
  if (result.error) throw new Error(result.error);
594
530
  const record = result.result;
595
- const files = record?.data?.[fieldKey];
531
+ const files = record?.data?.[fieldAlias];
596
532
  if (!Array.isArray(files)) {
597
- throw new Error(`Field ${fieldKey} on ${recordId} is not a file field or has no value`);
533
+ throw new Error(`Field ${fieldAlias} on ${recordId} is not a file field or has no value`);
598
534
  }
599
535
  const fileIds = files.map((f, i) => {
600
536
  if (typeof f !== "object" || f === null || !("id" in f) || typeof f.id !== "string") {
601
- throw new Error(`Invalid file entry [${i}] in ${recordId}.${fieldKey}: ${JSON.stringify(f)}`);
537
+ throw new Error(`Invalid file entry [${i}] in ${recordId}.${fieldAlias}: ${JSON.stringify(f)}`);
602
538
  }
603
539
  return f.id;
604
540
  });
@@ -1,7 +1,7 @@
1
1
  /** Enabled by an explicit `LOTICS_TELEMETRY=1`. Anything else, including unset, is off. */
2
2
  export declare function telemetryEnabled(env?: NodeJS.ProcessEnv): boolean;
3
3
  /**
4
- * The verb path of an invocation — `model.apply`, `run.query_records` —
4
+ * The verb path of an invocation — `app.apply`, `run.query_records` —
5
5
  * from the raw argv positionals.
6
6
  *
7
7
  * Positionals stop at the first token that is a flag, a JSON blob, an `@file`,