@lotics/cli 0.315.18 → 0.316.1
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 +37 -24
- package/README.md +42 -37
- package/dist/src/cli.js +27346 -49103
- package/dist/src/client.d.ts +12 -88
- package/dist/src/client.js +10 -74
- package/dist/src/invocation.d.ts +1 -1
- package/docs/agent_surface.md +121 -114
- package/docs/app_architecture.md +39 -0
- package/docs/app_bindings.md +24 -62
- package/docs/building_an_app.md +97 -81
- package/docs/cli_reference.md +18 -19
- package/docs/data_model.md +129 -183
- package/docs/design.md +156 -170
- package/docs/document_templates.md +135 -99
- package/docs/examples.md +1599 -1238
- package/docs/field_values.md +14 -11
- package/docs/filters.md +47 -9
- package/docs/first_build.md +180 -0
- package/docs/getting_started.md +15 -0
- package/docs/knowledge_docs.md +73 -209
- package/docs/migration.md +22 -26
- package/docs/workflow_steps.md +22 -579
- package/docs/workflows.md +409 -209
- package/docs/working_in_a_workspace.md +60 -0
- package/package.json +1 -1
package/dist/src/client.d.ts
CHANGED
|
@@ -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 `
|
|
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 `
|
|
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 `
|
|
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,
|
|
462
|
+
downloadRecordFiles(recordId: string, fieldAlias: string, outputDir?: string): Promise<Array<{
|
|
539
463
|
path: string;
|
|
540
464
|
filename: string;
|
|
541
465
|
file_id: string;
|
package/dist/src/client.js
CHANGED
|
@@ -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 `
|
|
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 `
|
|
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,
|
|
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?.[
|
|
531
|
+
const files = record?.data?.[fieldAlias];
|
|
596
532
|
if (!Array.isArray(files)) {
|
|
597
|
-
throw new Error(`Field ${
|
|
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}.${
|
|
537
|
+
throw new Error(`Invalid file entry [${i}] in ${recordId}.${fieldAlias}: ${JSON.stringify(f)}`);
|
|
602
538
|
}
|
|
603
539
|
return f.id;
|
|
604
540
|
});
|
package/dist/src/invocation.d.ts
CHANGED
|
@@ -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 — `
|
|
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`,
|