@lotics/cli 0.29.0 → 0.30.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/args.d.ts +32 -0
- package/dist/src/args.js +94 -0
- package/dist/src/args.test.d.ts +1 -0
- package/dist/src/args.test.js +37 -0
- package/dist/src/cli.js +6 -80
- package/dist/src/generate_app_workflows_dts.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI argument parser.
|
|
3
|
+
*
|
|
4
|
+
* Splits `process.argv.slice(2)` into a command / subcommand / positional
|
|
5
|
+
* (`toolArgs`) / remaining-positionals (`restArgs`) shape plus typed `flags`.
|
|
6
|
+
* Value-taking flags (`-m`, `--api-key`, …) consume the next token; boolean
|
|
7
|
+
* flags (`--force-workflow-sync`, `--json`, …) toggle. Anything not matching a
|
|
8
|
+
* known flag is positional.
|
|
9
|
+
*
|
|
10
|
+
* Lives apart from `cli.ts` so it can be unit-tested — `cli.ts` runs `main()`
|
|
11
|
+
* on import, so importing the parser from there would execute the CLI.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseArgs(argv: string[]): {
|
|
14
|
+
command?: string;
|
|
15
|
+
subcommand?: string;
|
|
16
|
+
toolArgs?: string;
|
|
17
|
+
restArgs: string[];
|
|
18
|
+
flags: {
|
|
19
|
+
json: boolean;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
output?: string;
|
|
22
|
+
as?: string;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
timezone?: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
forceWorkflowSync: boolean;
|
|
28
|
+
local: boolean;
|
|
29
|
+
version: boolean;
|
|
30
|
+
help: boolean;
|
|
31
|
+
};
|
|
32
|
+
};
|
package/dist/src/args.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI argument parser.
|
|
3
|
+
*
|
|
4
|
+
* Splits `process.argv.slice(2)` into a command / subcommand / positional
|
|
5
|
+
* (`toolArgs`) / remaining-positionals (`restArgs`) shape plus typed `flags`.
|
|
6
|
+
* Value-taking flags (`-m`, `--api-key`, …) consume the next token; boolean
|
|
7
|
+
* flags (`--force-workflow-sync`, `--json`, …) toggle. Anything not matching a
|
|
8
|
+
* known flag is positional.
|
|
9
|
+
*
|
|
10
|
+
* Lives apart from `cli.ts` so it can be unit-tested — `cli.ts` runs `main()`
|
|
11
|
+
* on import, so importing the parser from there would execute the CLI.
|
|
12
|
+
*/
|
|
13
|
+
export function parseArgs(argv) {
|
|
14
|
+
const flags = {
|
|
15
|
+
json: false,
|
|
16
|
+
timeout: undefined,
|
|
17
|
+
output: undefined,
|
|
18
|
+
as: undefined,
|
|
19
|
+
apiKey: undefined,
|
|
20
|
+
name: undefined,
|
|
21
|
+
timezone: undefined,
|
|
22
|
+
message: undefined,
|
|
23
|
+
forceWorkflowSync: false,
|
|
24
|
+
local: false,
|
|
25
|
+
version: false,
|
|
26
|
+
help: false,
|
|
27
|
+
};
|
|
28
|
+
let command;
|
|
29
|
+
let subcommand;
|
|
30
|
+
let toolArgs;
|
|
31
|
+
const restArgs = [];
|
|
32
|
+
let i = 0;
|
|
33
|
+
while (i < argv.length) {
|
|
34
|
+
const arg = argv[i];
|
|
35
|
+
switch (arg) {
|
|
36
|
+
case "--json":
|
|
37
|
+
flags.json = true;
|
|
38
|
+
break;
|
|
39
|
+
case "--timeout":
|
|
40
|
+
flags.timeout = parseInt(argv[++i], 10);
|
|
41
|
+
break;
|
|
42
|
+
case "--output":
|
|
43
|
+
case "-o":
|
|
44
|
+
flags.output = argv[++i];
|
|
45
|
+
break;
|
|
46
|
+
case "--as":
|
|
47
|
+
flags.as = argv[++i];
|
|
48
|
+
break;
|
|
49
|
+
case "--api-key":
|
|
50
|
+
flags.apiKey = argv[++i];
|
|
51
|
+
break;
|
|
52
|
+
case "--name":
|
|
53
|
+
flags.name = argv[++i];
|
|
54
|
+
break;
|
|
55
|
+
case "--timezone":
|
|
56
|
+
flags.timezone = argv[++i];
|
|
57
|
+
break;
|
|
58
|
+
case "-m":
|
|
59
|
+
case "--message":
|
|
60
|
+
flags.message = argv[++i];
|
|
61
|
+
break;
|
|
62
|
+
case "--force-workflow-sync":
|
|
63
|
+
flags.forceWorkflowSync = true;
|
|
64
|
+
break;
|
|
65
|
+
case "--local":
|
|
66
|
+
flags.local = true;
|
|
67
|
+
break;
|
|
68
|
+
case "--version":
|
|
69
|
+
case "-v":
|
|
70
|
+
flags.version = true;
|
|
71
|
+
break;
|
|
72
|
+
case "--help":
|
|
73
|
+
case "-h":
|
|
74
|
+
flags.help = true;
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
if (!command) {
|
|
78
|
+
command = arg;
|
|
79
|
+
}
|
|
80
|
+
else if (!subcommand) {
|
|
81
|
+
subcommand = arg;
|
|
82
|
+
}
|
|
83
|
+
else if (!toolArgs) {
|
|
84
|
+
toolArgs = arg;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
restArgs.push(arg);
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
return { command, subcommand, toolArgs, restArgs, flags };
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseArgs } from "./args.js";
|
|
3
|
+
describe("parseArgs", () => {
|
|
4
|
+
it("splits command / subcommand / positional", () => {
|
|
5
|
+
const r = parseArgs(["app", "deploy", "my message"]);
|
|
6
|
+
expect(r.command).toBe("app");
|
|
7
|
+
expect(r.subcommand).toBe("deploy");
|
|
8
|
+
expect(r.toolArgs).toBe("my message");
|
|
9
|
+
expect(r.restArgs).toEqual([]);
|
|
10
|
+
});
|
|
11
|
+
// `app deploy` regression: `-m` and `--force-workflow-sync` must be parsed
|
|
12
|
+
// as flags, not silently consumed as the positional message.
|
|
13
|
+
it("parses -m as the message flag", () => {
|
|
14
|
+
const r = parseArgs(["app", "deploy", "-m", "a message"]);
|
|
15
|
+
expect(r.flags.message).toBe("a message");
|
|
16
|
+
expect(r.toolArgs).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
it("parses --message as the message flag", () => {
|
|
19
|
+
const r = parseArgs(["app", "deploy", "--message", "a message"]);
|
|
20
|
+
expect(r.flags.message).toBe("a message");
|
|
21
|
+
});
|
|
22
|
+
it("parses --force-workflow-sync as a boolean flag", () => {
|
|
23
|
+
const r = parseArgs(["app", "deploy", "msg", "--force-workflow-sync"]);
|
|
24
|
+
expect(r.flags.forceWorkflowSync).toBe(true);
|
|
25
|
+
expect(r.toolArgs).toBe("msg");
|
|
26
|
+
});
|
|
27
|
+
it("parses --force-workflow-sync before -m without eating the message", () => {
|
|
28
|
+
const r = parseArgs(["app", "deploy", "--force-workflow-sync", "-m", "msg"]);
|
|
29
|
+
expect(r.flags.forceWorkflowSync).toBe(true);
|
|
30
|
+
expect(r.flags.message).toBe("msg");
|
|
31
|
+
});
|
|
32
|
+
it("defaults forceWorkflowSync to false and message to undefined", () => {
|
|
33
|
+
const r = parseArgs(["app", "deploy"]);
|
|
34
|
+
expect(r.flags.forceWorkflowSync).toBe(false);
|
|
35
|
+
expect(r.flags.message).toBeUndefined();
|
|
36
|
+
});
|
|
37
|
+
});
|
package/dist/src/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { LoticsClient, API_BASE_URL } from "./client.js";
|
|
|
6
6
|
import { resolveAuth, loadConfig, saveConfig, deleteConfig, getConfigPath, checkForUpdate } from "./config.js";
|
|
7
7
|
import { VERSION } from "./version.js";
|
|
8
8
|
import { appCreate, appPull, appDeploy, appDev } from "./app_commands.js";
|
|
9
|
+
import { parseArgs } from "./args.js";
|
|
9
10
|
function printHelp() {
|
|
10
11
|
console.log(`Lotics CLI v${VERSION} — AI agent interface for Lotics
|
|
11
12
|
|
|
@@ -114,79 +115,6 @@ else ~/.lotics/config.json. A per-directory config pins a project or worktree to
|
|
|
114
115
|
its own account and workspace; --local creates one. Note: an exported
|
|
115
116
|
LOTICS_API_KEY env var overrides the config file's key.`);
|
|
116
117
|
}
|
|
117
|
-
function parseArgs(argv) {
|
|
118
|
-
const flags = {
|
|
119
|
-
json: false,
|
|
120
|
-
timeout: undefined,
|
|
121
|
-
output: undefined,
|
|
122
|
-
as: undefined,
|
|
123
|
-
apiKey: undefined,
|
|
124
|
-
name: undefined,
|
|
125
|
-
timezone: undefined,
|
|
126
|
-
local: false,
|
|
127
|
-
version: false,
|
|
128
|
-
help: false,
|
|
129
|
-
};
|
|
130
|
-
let command;
|
|
131
|
-
let subcommand;
|
|
132
|
-
let toolArgs;
|
|
133
|
-
const restArgs = [];
|
|
134
|
-
let i = 0;
|
|
135
|
-
while (i < argv.length) {
|
|
136
|
-
const arg = argv[i];
|
|
137
|
-
switch (arg) {
|
|
138
|
-
case "--json":
|
|
139
|
-
flags.json = true;
|
|
140
|
-
break;
|
|
141
|
-
case "--timeout":
|
|
142
|
-
flags.timeout = parseInt(argv[++i], 10);
|
|
143
|
-
break;
|
|
144
|
-
case "--output":
|
|
145
|
-
case "-o":
|
|
146
|
-
flags.output = argv[++i];
|
|
147
|
-
break;
|
|
148
|
-
case "--as":
|
|
149
|
-
flags.as = argv[++i];
|
|
150
|
-
break;
|
|
151
|
-
case "--api-key":
|
|
152
|
-
flags.apiKey = argv[++i];
|
|
153
|
-
break;
|
|
154
|
-
case "--name":
|
|
155
|
-
flags.name = argv[++i];
|
|
156
|
-
break;
|
|
157
|
-
case "--timezone":
|
|
158
|
-
flags.timezone = argv[++i];
|
|
159
|
-
break;
|
|
160
|
-
case "--local":
|
|
161
|
-
flags.local = true;
|
|
162
|
-
break;
|
|
163
|
-
case "--version":
|
|
164
|
-
case "-v":
|
|
165
|
-
flags.version = true;
|
|
166
|
-
break;
|
|
167
|
-
case "--help":
|
|
168
|
-
case "-h":
|
|
169
|
-
flags.help = true;
|
|
170
|
-
break;
|
|
171
|
-
default:
|
|
172
|
-
if (!command) {
|
|
173
|
-
command = arg;
|
|
174
|
-
}
|
|
175
|
-
else if (!subcommand) {
|
|
176
|
-
subcommand = arg;
|
|
177
|
-
}
|
|
178
|
-
else if (!toolArgs) {
|
|
179
|
-
toolArgs = arg;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
restArgs.push(arg);
|
|
183
|
-
}
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
i++;
|
|
187
|
-
}
|
|
188
|
-
return { command, subcommand, toolArgs, restArgs, flags };
|
|
189
|
-
}
|
|
190
118
|
function readStdin() {
|
|
191
119
|
return new Promise((resolve, reject) => {
|
|
192
120
|
const chunks = [];
|
|
@@ -546,13 +474,11 @@ async function main() {
|
|
|
546
474
|
return;
|
|
547
475
|
}
|
|
548
476
|
if (subcommand === "deploy") {
|
|
549
|
-
//
|
|
550
|
-
//
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const forceWorkflowSync = restArgs.includes("--force-workflow-sync");
|
|
555
|
-
await appDeploy(client, { message, forceWorkflowSync });
|
|
477
|
+
// The message is either `-m <message>` or a bare positional arg after
|
|
478
|
+
// `deploy`. `--force-workflow-sync` is a parsed boolean flag, valid in
|
|
479
|
+
// any position.
|
|
480
|
+
const message = flags.message ?? toolArgs;
|
|
481
|
+
await appDeploy(client, { message, forceWorkflowSync: flags.forceWorkflowSync });
|
|
556
482
|
return;
|
|
557
483
|
}
|
|
558
484
|
if (subcommand === "dev") {
|