@lotics/cli 0.6.0 → 0.8.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/README.md +19 -1
- package/dist/src/cli.js +147 -14
- package/dist/src/client.d.ts +20 -0
- package/dist/src/client.js +26 -5
- package/dist/src/config.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,13 +25,19 @@ The CLI checks for updates once per day and prompts when a new version is availa
|
|
|
25
25
|
|
|
26
26
|
## Authentication
|
|
27
27
|
|
|
28
|
-
**`lotics signup`** — Creates a new Lotics account, organization, workspace, and API key in one step.
|
|
28
|
+
**`lotics signup`** — Creates a new Lotics account, organization, workspace, and API key in one step. Sends a magic link email so you can access the web app.
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
lotics signup # interactive prompts
|
|
32
32
|
lotics signup --email a@b.com --name "Agent" # non-interactive
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
**`lotics login`** — Send a magic link email to access the web app (requires prior signup or setup).
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
lotics login
|
|
39
|
+
```
|
|
40
|
+
|
|
35
41
|
**`lotics setup`** — Saves an existing API key directly (e.g. one created in the Lotics web app).
|
|
36
42
|
|
|
37
43
|
```bash
|
|
@@ -43,6 +49,18 @@ API key is saved to `~/.lotics/config.json`. Run `lotics logout` to remove saved
|
|
|
43
49
|
|
|
44
50
|
Auth priority: `--api-key` flag > `LOTICS_API_KEY` env > `~/.lotics/config.json`.
|
|
45
51
|
|
|
52
|
+
## Workspaces
|
|
53
|
+
|
|
54
|
+
If your organization has multiple workspaces, select one before running tools:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
lotics workspace # list workspaces (marks current)
|
|
58
|
+
lotics workspace select wks_... # switch to a workspace
|
|
59
|
+
lotics workspace create "Sales" # create a new workspace (admin only)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Single-workspace organizations auto-select on first use.
|
|
63
|
+
|
|
46
64
|
## CLI
|
|
47
65
|
|
|
48
66
|
```bash
|
package/dist/src/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ Lotics is an AI-powered operations platform. Through this CLI you can:
|
|
|
16
16
|
|
|
17
17
|
AUTHENTICATION
|
|
18
18
|
lotics signup Create account, org, workspace, and API key
|
|
19
|
+
lotics login Send a magic link email to access the web app
|
|
19
20
|
lotics setup [api_key] Save an existing API key (e.g. from the web app)
|
|
20
21
|
lotics whoami Show the email of the current account
|
|
21
22
|
lotics logout Remove saved credentials
|
|
@@ -25,21 +26,25 @@ AUTHENTICATION
|
|
|
25
26
|
--name <name> Display name (defaults to email prefix)
|
|
26
27
|
--timezone <timezone> Workspace timezone (defaults to UTC, e.g. Asia/Ho_Chi_Minh)
|
|
27
28
|
|
|
28
|
-
Signup
|
|
29
|
-
Use lotics
|
|
29
|
+
Signup sends a magic link email so you can access the Lotics web app.
|
|
30
|
+
Use lotics login to request a new magic link at any time.
|
|
30
31
|
Setup priority: --api-key flag > LOTICS_API_KEY env > saved config.
|
|
31
32
|
|
|
32
33
|
USAGE
|
|
33
34
|
1. lotics signup (or lotics setup) Create account or set up API key
|
|
34
|
-
2. lotics
|
|
35
|
-
3. lotics tools
|
|
36
|
-
4. lotics
|
|
35
|
+
2. lotics workspace Check current workspace (auto-selects if only one)
|
|
36
|
+
3. lotics tools List available tools by category
|
|
37
|
+
4. lotics tools <name> Show tool description and full input schema
|
|
38
|
+
5. lotics run <tool> '<json>' Execute a tool with JSON arguments
|
|
37
39
|
|
|
38
|
-
Always inspect the schema (step
|
|
40
|
+
Always inspect the schema (step 4) before calling a tool.
|
|
39
41
|
Tools are grouped by category (tables, records, views, etc.).
|
|
40
42
|
Query tools return IDs used as arguments to other tools.
|
|
41
43
|
|
|
42
44
|
COMMANDS
|
|
45
|
+
lotics workspace List workspaces (marks current)
|
|
46
|
+
lotics workspace select <id> Switch to a different workspace
|
|
47
|
+
lotics workspace create <name> Create a new workspace (admin only)
|
|
43
48
|
lotics tools List all available tools
|
|
44
49
|
lotics tools <name> Show tool description and input schema
|
|
45
50
|
lotics run <tool> '<json>' Execute a tool
|
|
@@ -204,12 +209,16 @@ async function handleSignup(flags) {
|
|
|
204
209
|
process.exit(1);
|
|
205
210
|
}
|
|
206
211
|
const existing = loadConfig() ?? {};
|
|
207
|
-
saveConfig({
|
|
212
|
+
saveConfig({
|
|
213
|
+
...existing,
|
|
214
|
+
api_key: data.api_key,
|
|
215
|
+
email: data.email,
|
|
216
|
+
workspace_id: data.workspace_id,
|
|
217
|
+
});
|
|
208
218
|
console.error(`Account created. You can now use the CLI.`);
|
|
209
|
-
console.error(` Email:
|
|
210
|
-
console.error(
|
|
211
|
-
console.error(
|
|
212
|
-
console.error(`Run "lotics whoami" to check the current account email.`);
|
|
219
|
+
console.error(` Email: ${data.email}`);
|
|
220
|
+
console.error(`\nCheck your email for a magic link to access the Lotics web app.`);
|
|
221
|
+
console.error(`Run "lotics login" to request a new link at any time.`);
|
|
213
222
|
}
|
|
214
223
|
async function handleSetup(providedKey) {
|
|
215
224
|
const apiKey = providedKey ?? await prompt("Enter your API key: ");
|
|
@@ -229,7 +238,23 @@ async function handleSetup(providedKey) {
|
|
|
229
238
|
process.exit(1);
|
|
230
239
|
}
|
|
231
240
|
const existing = loadConfig() ?? {};
|
|
232
|
-
|
|
241
|
+
const newConfig = { ...existing, api_key: apiKey, email };
|
|
242
|
+
// Auto-resolve workspace
|
|
243
|
+
try {
|
|
244
|
+
const workspaces = await client.listWorkspaces();
|
|
245
|
+
if (workspaces.length === 1) {
|
|
246
|
+
newConfig.workspace_id = workspaces[0].id;
|
|
247
|
+
}
|
|
248
|
+
else if (workspaces.length > 1) {
|
|
249
|
+
console.error(`\nMultiple workspaces found. Run "lotics workspace select <id>" to choose one:`);
|
|
250
|
+
printWorkspaceList(workspaces);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
255
|
+
console.error(`Warning: could not resolve workspace: ${msg}`);
|
|
256
|
+
}
|
|
257
|
+
saveConfig(newConfig);
|
|
233
258
|
console.error("Authenticated.");
|
|
234
259
|
}
|
|
235
260
|
function requireClient(flags) {
|
|
@@ -238,7 +263,44 @@ function requireClient(flags) {
|
|
|
238
263
|
console.error('Not authenticated. Run "lotics setup" or set LOTICS_API_KEY.');
|
|
239
264
|
process.exit(1);
|
|
240
265
|
}
|
|
241
|
-
|
|
266
|
+
const config = loadConfig();
|
|
267
|
+
return new LoticsClient({ apiKey: auth.apiKey, workspaceId: config?.workspace_id });
|
|
268
|
+
}
|
|
269
|
+
function printWorkspaceList(workspaces, currentId) {
|
|
270
|
+
for (const ws of workspaces) {
|
|
271
|
+
const marker = ws.id === currentId ? " (current)" : "";
|
|
272
|
+
console.error(` ${ws.id} ${ws.name} ${ws.timezone} ${ws.default_currency}${marker}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async function resolveWorkspace(client) {
|
|
276
|
+
const config = loadConfig();
|
|
277
|
+
if (config?.workspace_id)
|
|
278
|
+
return;
|
|
279
|
+
let workspaces;
|
|
280
|
+
try {
|
|
281
|
+
workspaces = await client.listWorkspaces();
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
285
|
+
console.error(`Failed to list workspaces: ${message}`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
if (workspaces.length === 0) {
|
|
289
|
+
console.error("No workspaces found for this organization.");
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
if (workspaces.length === 1) {
|
|
293
|
+
const existing = config ?? {};
|
|
294
|
+
saveConfig({ ...existing, workspace_id: workspaces[0].id });
|
|
295
|
+
client.setWorkspaceId(workspaces[0].id);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
// Multiple workspaces, none selected
|
|
299
|
+
console.error("Multiple workspaces available. Select one with:\n");
|
|
300
|
+
console.error(' lotics workspace select <id>\n');
|
|
301
|
+
printWorkspaceList(workspaces);
|
|
302
|
+
console.error("");
|
|
303
|
+
process.exit(1);
|
|
242
304
|
}
|
|
243
305
|
/**
|
|
244
306
|
* Resolve upload paths: files pass through, directories expand to their immediate files.
|
|
@@ -313,8 +375,14 @@ async function main() {
|
|
|
313
375
|
console.error("Logged out. Credentials removed.");
|
|
314
376
|
return;
|
|
315
377
|
}
|
|
378
|
+
if (command === "login") {
|
|
379
|
+
const client = requireClient(flags);
|
|
380
|
+
const { email } = await client.login();
|
|
381
|
+
console.error(`Magic link sent to ${email}. Check your email to access the Lotics web app.`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
316
384
|
// --- Validate command before auth ---
|
|
317
|
-
if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download") {
|
|
385
|
+
if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace") {
|
|
318
386
|
console.error(`Unknown command: ${command}`);
|
|
319
387
|
console.error('Run "lotics --help" for usage.');
|
|
320
388
|
process.exit(1);
|
|
@@ -335,6 +403,71 @@ async function main() {
|
|
|
335
403
|
process.exit(1);
|
|
336
404
|
}
|
|
337
405
|
const client = requireClient(flags);
|
|
406
|
+
// lotics workspace / lotics workspace list / lotics workspace select <id>
|
|
407
|
+
if (command === "workspace") {
|
|
408
|
+
const workspaces = await client.listWorkspaces();
|
|
409
|
+
const config = loadConfig();
|
|
410
|
+
if (subcommand === "select") {
|
|
411
|
+
const targetId = toolArgs;
|
|
412
|
+
if (!targetId) {
|
|
413
|
+
console.error('Usage: lotics workspace select <workspace_id>');
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
const target = workspaces.find((ws) => ws.id === targetId);
|
|
417
|
+
if (!target) {
|
|
418
|
+
console.error(`Workspace not found: ${targetId}\n\nAvailable workspaces:`);
|
|
419
|
+
printWorkspaceList(workspaces, config?.workspace_id);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
const existing = config ?? {};
|
|
423
|
+
saveConfig({ ...existing, workspace_id: target.id });
|
|
424
|
+
console.error(`Switched to workspace: ${target.name} (${target.id})`);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (subcommand === "create") {
|
|
428
|
+
const name = toolArgs;
|
|
429
|
+
if (!name) {
|
|
430
|
+
console.error('Usage: lotics workspace create <name> [--timezone <tz>]');
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
const timezone = flags.timezone;
|
|
434
|
+
const created = await client.createWorkspace({ name, timezone });
|
|
435
|
+
const existing = config ?? {};
|
|
436
|
+
saveConfig({ ...existing, workspace_id: created.id });
|
|
437
|
+
client.setWorkspaceId(created.id);
|
|
438
|
+
if (flags.json) {
|
|
439
|
+
console.log(JSON.stringify(created, null, 2));
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
console.error(`Created workspace: ${created.name} (${created.id})`);
|
|
443
|
+
console.error(`Switched to ${created.id}`);
|
|
444
|
+
}
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
// Default: list workspaces
|
|
448
|
+
if (subcommand && subcommand !== "list") {
|
|
449
|
+
console.error(`Unknown workspace subcommand: ${subcommand}`);
|
|
450
|
+
console.error('Usage: lotics workspace [list | select <id> | create <name>]');
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
if (flags.json) {
|
|
454
|
+
console.log(JSON.stringify(workspaces, null, 2));
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
if (workspaces.length === 0) {
|
|
458
|
+
console.error("No workspaces found.");
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
for (const ws of workspaces) {
|
|
462
|
+
const marker = ws.id === config?.workspace_id ? " (current)" : "";
|
|
463
|
+
console.log(`${ws.id} ${ws.name} ${ws.timezone} ${ws.default_currency}${marker}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Ensure workspace is resolved for all remaining commands
|
|
470
|
+
await resolveWorkspace(client);
|
|
338
471
|
// lotics tools / lotics tools <name>
|
|
339
472
|
if (command === "tools") {
|
|
340
473
|
if (subcommand) {
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
export interface LoticsClientOptions {
|
|
2
2
|
apiKey: string;
|
|
3
|
+
workspaceId?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface WorkspaceInfo {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
timezone: string;
|
|
9
|
+
default_currency: string;
|
|
10
|
+
organization_id: string;
|
|
11
|
+
created_at: string;
|
|
3
12
|
}
|
|
4
13
|
export interface ToolExecuteResult {
|
|
5
14
|
result: unknown;
|
|
@@ -25,15 +34,26 @@ export interface FileUploadResult {
|
|
|
25
34
|
export declare const API_BASE_URL: string;
|
|
26
35
|
export declare class LoticsClient {
|
|
27
36
|
private apiKey;
|
|
37
|
+
private workspaceId;
|
|
28
38
|
private baseUrl;
|
|
29
39
|
constructor(options: LoticsClientOptions);
|
|
30
40
|
private throwResponseError;
|
|
41
|
+
private buildHeaders;
|
|
31
42
|
private request;
|
|
32
43
|
whoami(): Promise<{
|
|
33
44
|
email: string;
|
|
34
45
|
name: string;
|
|
35
46
|
organization_id: string;
|
|
36
47
|
}>;
|
|
48
|
+
setWorkspaceId(id: string): void;
|
|
49
|
+
listWorkspaces(): Promise<WorkspaceInfo[]>;
|
|
50
|
+
createWorkspace(body: {
|
|
51
|
+
name: string;
|
|
52
|
+
timezone?: string;
|
|
53
|
+
}): Promise<WorkspaceInfo>;
|
|
54
|
+
login(): Promise<{
|
|
55
|
+
email: string;
|
|
56
|
+
}>;
|
|
37
57
|
listTools(): Promise<{
|
|
38
58
|
tools: string[];
|
|
39
59
|
categories: Record<string, {
|
package/dist/src/client.js
CHANGED
|
@@ -24,9 +24,11 @@ function getMimeType(filename) {
|
|
|
24
24
|
export const API_BASE_URL = process.env.LOTICS_API_URL ?? "https://api.lotics.ai";
|
|
25
25
|
export class LoticsClient {
|
|
26
26
|
apiKey;
|
|
27
|
+
workspaceId;
|
|
27
28
|
baseUrl;
|
|
28
29
|
constructor(options) {
|
|
29
30
|
this.apiKey = options.apiKey;
|
|
31
|
+
this.workspaceId = options.workspaceId;
|
|
30
32
|
this.baseUrl = API_BASE_URL;
|
|
31
33
|
}
|
|
32
34
|
async throwResponseError(response) {
|
|
@@ -41,11 +43,18 @@ export class LoticsClient {
|
|
|
41
43
|
}
|
|
42
44
|
throw new Error(`${response.status}: ${message}`);
|
|
43
45
|
}
|
|
44
|
-
|
|
45
|
-
const url = `${this.baseUrl}${path}`;
|
|
46
|
+
buildHeaders() {
|
|
46
47
|
const headers = {
|
|
47
48
|
"x-api-key": this.apiKey,
|
|
48
49
|
};
|
|
50
|
+
if (this.workspaceId) {
|
|
51
|
+
headers["x-workspace-id"] = this.workspaceId;
|
|
52
|
+
}
|
|
53
|
+
return headers;
|
|
54
|
+
}
|
|
55
|
+
async request(method, path, body) {
|
|
56
|
+
const url = `${this.baseUrl}${path}`;
|
|
57
|
+
const headers = this.buildHeaders();
|
|
49
58
|
const init = { method, headers };
|
|
50
59
|
if (body !== undefined) {
|
|
51
60
|
headers["Content-Type"] = "application/json";
|
|
@@ -59,6 +68,18 @@ export class LoticsClient {
|
|
|
59
68
|
async whoami() {
|
|
60
69
|
return this.request("GET", "/v1/cli/whoami");
|
|
61
70
|
}
|
|
71
|
+
setWorkspaceId(id) {
|
|
72
|
+
this.workspaceId = id;
|
|
73
|
+
}
|
|
74
|
+
async listWorkspaces() {
|
|
75
|
+
return this.request("GET", "/v1/workspaces");
|
|
76
|
+
}
|
|
77
|
+
async createWorkspace(body) {
|
|
78
|
+
return this.request("POST", "/v1/workspaces", body);
|
|
79
|
+
}
|
|
80
|
+
async login() {
|
|
81
|
+
return this.request("POST", "/v1/cli/login");
|
|
82
|
+
}
|
|
62
83
|
async listTools() {
|
|
63
84
|
return this.request("GET", "/v1/tools");
|
|
64
85
|
}
|
|
@@ -74,7 +95,7 @@ export class LoticsClient {
|
|
|
74
95
|
const url = `${this.baseUrl}/v1/tools/execute`;
|
|
75
96
|
const response = await fetch(url, {
|
|
76
97
|
method: "POST",
|
|
77
|
-
headers: {
|
|
98
|
+
headers: { ...this.buildHeaders(), "Content-Type": "application/json" },
|
|
78
99
|
body: JSON.stringify(body),
|
|
79
100
|
signal: controller.signal,
|
|
80
101
|
});
|
|
@@ -107,7 +128,7 @@ export class LoticsClient {
|
|
|
107
128
|
async downloadFileById(fileId, outputDir) {
|
|
108
129
|
const url = `${this.baseUrl}/v1/files/${encodeURIComponent(fileId)}/download`;
|
|
109
130
|
const response = await fetch(url, {
|
|
110
|
-
headers:
|
|
131
|
+
headers: this.buildHeaders(),
|
|
111
132
|
});
|
|
112
133
|
if (!response.ok)
|
|
113
134
|
await this.throwResponseError(response);
|
|
@@ -133,7 +154,7 @@ export class LoticsClient {
|
|
|
133
154
|
const url = `${this.baseUrl}/v1/files`;
|
|
134
155
|
const response = await fetch(url, {
|
|
135
156
|
method: "POST",
|
|
136
|
-
headers:
|
|
157
|
+
headers: this.buildHeaders(),
|
|
137
158
|
body: formData,
|
|
138
159
|
});
|
|
139
160
|
if (!response.ok)
|
package/dist/src/config.d.ts
CHANGED