@moxt-ai/cli 0.1.8 → 0.2.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 +74 -10
- package/dist/index.js +156 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,37 +45,101 @@ moxt members list -w <workspace-id>
|
|
|
45
45
|
moxt members remove -w <workspace-id> <email1>,<email2>
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
### space
|
|
49
|
+
|
|
50
|
+
List accessible spaces in a workspace.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# List all accessible spaces (personal, team, teammate)
|
|
54
|
+
moxt space list -w <workspace-id>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Output columns: TYPE, TEAM_SPACE_ID, TEAMMATE_ID, NAME
|
|
58
|
+
|
|
59
|
+
- `personal` — Your personal space (Momo). Both IDs are empty; this is the default.
|
|
60
|
+
- `team` — Teamspace. Only `TEAM_SPACE_ID` is populated.
|
|
61
|
+
- `teammate` — AI teammate. Only `TEAMMATE_ID` is populated.
|
|
62
|
+
|
|
48
63
|
### file
|
|
49
64
|
|
|
50
|
-
File operations
|
|
65
|
+
File operations on a space. Use `moxt space list` to find available spaces.
|
|
51
66
|
|
|
52
67
|
```bash
|
|
53
|
-
# List directory contents
|
|
54
|
-
moxt file list -w <workspace-id> [-p <path>]
|
|
68
|
+
# List directory contents (defaults to personal space)
|
|
69
|
+
moxt file list -w <workspace-id> [-p <path>]
|
|
70
|
+
|
|
71
|
+
# List directory in personal space (explicit)
|
|
72
|
+
moxt file list -w <workspace-id> --personal [-p <path>]
|
|
73
|
+
|
|
74
|
+
# List directory in a teamspace (by name)
|
|
75
|
+
moxt file list -w <workspace-id> -s <teamspace-name> [-p <path>]
|
|
76
|
+
|
|
77
|
+
# List directory in a teamspace (by ID)
|
|
78
|
+
moxt file list -w <workspace-id> --team-space-id <teamSpaceId> [-p <path>]
|
|
55
79
|
|
|
56
|
-
#
|
|
57
|
-
moxt file
|
|
80
|
+
# List directory in a teammate space (by teammate ID)
|
|
81
|
+
moxt file list -w <workspace-id> --teammate-id <teammateId> [-p <path>]
|
|
82
|
+
|
|
83
|
+
# Read file content by path
|
|
84
|
+
moxt file read -w <workspace-id> -p <path>
|
|
85
|
+
|
|
86
|
+
# Read file content from a teamspace (by name or ID)
|
|
87
|
+
moxt file read -w <workspace-id> -s <teamspace-name> -p <path>
|
|
88
|
+
moxt file read -w <workspace-id> --team-space-id <teamSpaceId> -p <path>
|
|
89
|
+
|
|
90
|
+
# Read file content from a teammate space (by teammate ID)
|
|
91
|
+
moxt file read -w <workspace-id> --teammate-id <teammateId> -p <path>
|
|
92
|
+
|
|
93
|
+
# Read file content by URL
|
|
94
|
+
moxt file read --url <moxt-url>
|
|
95
|
+
|
|
96
|
+
# Resolve the shareable browser URL for a file by path
|
|
97
|
+
moxt file get-url -w <workspace-id> -p <path>
|
|
98
|
+
|
|
99
|
+
# Resolve URL in a teamspace (by name or ID)
|
|
100
|
+
moxt file get-url -w <workspace-id> -s <teamspace-name> -p <path>
|
|
101
|
+
moxt file get-url -w <workspace-id> --team-space-id <teamSpaceId> -p <path>
|
|
102
|
+
|
|
103
|
+
# Resolve URL in a teammate space (by teammate ID)
|
|
104
|
+
moxt file get-url -w <workspace-id> --teammate-id <teammateId> -p <path>
|
|
58
105
|
|
|
59
106
|
# Upload a file
|
|
60
|
-
moxt file put -w <workspace-id> -p <path> -l <local-path> [-
|
|
107
|
+
moxt file put -w <workspace-id> -p <path> -l <local-path> [-r]
|
|
108
|
+
|
|
109
|
+
# Upload a file to a teamspace (by name or ID)
|
|
110
|
+
moxt file put -w <workspace-id> -s <teamspace-name> -p <path> -l <local-path> [-r]
|
|
111
|
+
moxt file put -w <workspace-id> --team-space-id <teamSpaceId> -p <path> -l <local-path> [-r]
|
|
112
|
+
|
|
113
|
+
# Upload a file to a teammate space (by teammate ID)
|
|
114
|
+
moxt file put -w <workspace-id> --teammate-id <teammateId> -p <path> -l <local-path> [-r]
|
|
61
115
|
|
|
62
116
|
# Create a directory
|
|
63
|
-
moxt file mkdir -w <workspace-id> -p <path> [-
|
|
117
|
+
moxt file mkdir -w <workspace-id> -p <path> [-r]
|
|
118
|
+
moxt file mkdir -w <workspace-id> -s <teamspace-name> -p <path> [-r]
|
|
64
119
|
|
|
65
120
|
# Delete a file or empty directory
|
|
66
|
-
moxt file del -w <workspace-id> -p <path>
|
|
121
|
+
moxt file del -w <workspace-id> -p <path>
|
|
122
|
+
moxt file del -w <workspace-id> -s <teamspace-name> -p <path>
|
|
67
123
|
```
|
|
68
124
|
|
|
69
125
|
#### File command options
|
|
70
126
|
|
|
71
127
|
| Option | Description |
|
|
72
128
|
|--------|-------------|
|
|
73
|
-
| `-w, --workspace <id>` | Workspace ID (required) |
|
|
129
|
+
| `-w, --workspace <id>` | Workspace ID (required for all except URL mode) |
|
|
130
|
+
| `-s, --space <name>` | Space name (`personal` or teamspace name) |
|
|
131
|
+
| `--personal` | Use personal space (default when no space specified) |
|
|
132
|
+
| `--team-space-id <id>` | Team space ID |
|
|
133
|
+
| `--teammate-id <id>` | Teammate ID (teammate space) |
|
|
74
134
|
| `-p, --path <path>` | File or directory path |
|
|
135
|
+
| `-u, --url <url>` | Moxt file URL (e.g., `https://moxt.ai/w/{workspaceId}/{fileId}`) |
|
|
75
136
|
| `-l, --local-path <path>` | Local file path (for upload) |
|
|
76
|
-
| `-s, --space <type>` | Space type: `personal` or `team` (default: `team`) |
|
|
77
137
|
| `-r, --recursive` | Create parent directories if needed |
|
|
78
138
|
|
|
139
|
+
> **Note**: `-s`, `--personal`, `--team-space-id`, and `--teammate-id` are mutually exclusive.
|
|
140
|
+
> For `file read`, use either `--url` or `--workspace` with `--path`. They are mutually exclusive.
|
|
141
|
+
> `file get-url` returns a stable Moxt URL (e.g., `https://moxt.ai/w/{workspaceId}/{fileId}`) for the given path; the URL is unchanged across writes to the same path and can be fed back into `file read --url`.
|
|
142
|
+
|
|
79
143
|
## Configuration
|
|
80
144
|
|
|
81
145
|
Set the following environment variables:
|
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ function getApiKey() {
|
|
|
32
32
|
|
|
33
33
|
// src/utils/http.ts
|
|
34
34
|
function getUserAgent() {
|
|
35
|
-
return `moxt-cli/${"0.
|
|
35
|
+
return `moxt-cli/${"0.2.0"} (${platform()}/${arch()}) node/${process.versions.node}`;
|
|
36
36
|
}
|
|
37
37
|
async function request(method, path, body) {
|
|
38
38
|
const apiKey = getApiKey();
|
|
@@ -83,6 +83,7 @@ var colors = {
|
|
|
83
83
|
blue: "\x1B[1;34m",
|
|
84
84
|
green: "\x1B[32m",
|
|
85
85
|
red: "\x1B[31m",
|
|
86
|
+
cyan: "\x1B[36m",
|
|
86
87
|
dim: "\x1B[2m",
|
|
87
88
|
reset: "\x1B[0m"
|
|
88
89
|
};
|
|
@@ -126,13 +127,86 @@ function isBinaryContent(buffer) {
|
|
|
126
127
|
}
|
|
127
128
|
return false;
|
|
128
129
|
}
|
|
130
|
+
function validateSpaceOptions(options) {
|
|
131
|
+
const mutuallyExclusive = [options.space, options.personal, options.teamSpaceId, options.teammateId].filter(
|
|
132
|
+
Boolean
|
|
133
|
+
).length;
|
|
134
|
+
if (mutuallyExclusive > 1) {
|
|
135
|
+
printError("Error: -s, --personal, --team-space-id, and --teammate-id are mutually exclusive.");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
if (options.teamSpaceId) {
|
|
139
|
+
return { teamSpaceId: options.teamSpaceId };
|
|
140
|
+
}
|
|
141
|
+
if (options.teammateId) {
|
|
142
|
+
const parsed = Number.parseInt(options.teammateId, 10);
|
|
143
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
144
|
+
printError(`Error: --teammate-id must be a positive integer, got "${options.teammateId}".`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
return { agentId: parsed };
|
|
148
|
+
}
|
|
149
|
+
if (options.personal) {
|
|
150
|
+
return { space: "personal" };
|
|
151
|
+
}
|
|
152
|
+
if (options.space) {
|
|
153
|
+
return { space: options.space };
|
|
154
|
+
}
|
|
155
|
+
return {};
|
|
156
|
+
}
|
|
157
|
+
function buildFileQueryString(path, spaceParams) {
|
|
158
|
+
const params = new URLSearchParams();
|
|
159
|
+
if (path) {
|
|
160
|
+
params.set("path", path);
|
|
161
|
+
}
|
|
162
|
+
if (spaceParams.space) {
|
|
163
|
+
params.set("space", spaceParams.space);
|
|
164
|
+
}
|
|
165
|
+
if (spaceParams.teamSpaceId) {
|
|
166
|
+
params.set("teamSpaceId", spaceParams.teamSpaceId);
|
|
167
|
+
}
|
|
168
|
+
if (spaceParams.agentId != null) {
|
|
169
|
+
params.set("agentId", String(spaceParams.agentId));
|
|
170
|
+
}
|
|
171
|
+
const qs = params.toString();
|
|
172
|
+
return qs ? `?${qs}` : "";
|
|
173
|
+
}
|
|
174
|
+
function addSpaceOptions(cmd) {
|
|
175
|
+
return cmd.requiredOption("-w, --workspace <workspaceId>", "Workspace ID").option("-s, --space <name>", "Space name (personal or teamspace name)").option("--personal", "Use personal space (default)").option("--team-space-id <teamSpaceId>", "Team space ID").option("--teammate-id <teammateId>", "Teammate ID (teammate space)");
|
|
176
|
+
}
|
|
129
177
|
function registerFileCommand(program2) {
|
|
130
178
|
const file = program2.command("file").description("File operations");
|
|
131
|
-
|
|
132
|
-
|
|
179
|
+
addSpaceOptions(
|
|
180
|
+
file.command("get-url").description("Resolve the shareable browser URL for a file").requiredOption("-p, --path <path>", "File path")
|
|
181
|
+
).action(async (options) => {
|
|
182
|
+
const spaceParams = validateSpaceOptions(options);
|
|
183
|
+
const qs = buildFileQueryString(options.path, spaceParams);
|
|
184
|
+
startSpinner("Resolving URL...");
|
|
185
|
+
const response = await httpGet(
|
|
186
|
+
`/workspaces/${options.workspace}/files/url${qs}`
|
|
187
|
+
);
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
stopSpinner(false, "Failed");
|
|
190
|
+
if (response.status === 404) {
|
|
191
|
+
print(`${colors.red}\u2620 ${options.path} does not exist${colors.reset}`);
|
|
192
|
+
} else if (response.status === 400) {
|
|
193
|
+
printError(`Error: ${JSON.stringify(response.data)}`);
|
|
194
|
+
} else {
|
|
195
|
+
printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`);
|
|
196
|
+
}
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
stopSpinner(true, response.data.path);
|
|
200
|
+
print(`${colors.cyan}${response.data.url}${colors.reset}`);
|
|
201
|
+
});
|
|
202
|
+
addSpaceOptions(
|
|
203
|
+
file.command("list").description("List directory contents").option("-p, --path <path>", "Directory path", "/")
|
|
204
|
+
).action(async (options) => {
|
|
205
|
+
const spaceParams = validateSpaceOptions(options);
|
|
206
|
+
const qs = buildFileQueryString(options.path, spaceParams);
|
|
133
207
|
startSpinner("Listing files...");
|
|
134
208
|
const response = await httpGet(
|
|
135
|
-
`/workspaces/${options.workspace}/files/list
|
|
209
|
+
`/workspaces/${options.workspace}/files/list${qs}`
|
|
136
210
|
);
|
|
137
211
|
if (!response.ok) {
|
|
138
212
|
stopSpinner(false, "Failed");
|
|
@@ -157,16 +231,43 @@ function registerFileCommand(program2) {
|
|
|
157
231
|
}
|
|
158
232
|
}
|
|
159
233
|
});
|
|
160
|
-
file.command("read").description("Read file content").
|
|
161
|
-
|
|
234
|
+
file.command("read").description("Read file content").option("-w, --workspace <workspaceId>", "Workspace ID").option("-s, --space <name>", "Space name (personal or teamspace name)").option("--personal", "Use personal space (default)").option("--team-space-id <teamSpaceId>", "Team space ID").option("--teammate-id <teammateId>", "Teammate ID (teammate space)").option("-p, --path <path>", "File path").option("-u, --url <url>", "Moxt file URL (e.g., https://moxt.ai/w/{workspaceId}/{fileId})").action(async (options) => {
|
|
235
|
+
if (options.url && options.workspace) {
|
|
236
|
+
printError("Error: --url cannot be used with --workspace");
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
if (options.url && options.path) {
|
|
240
|
+
printError("Error: --url cannot be used with --path");
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
if (!options.url && !options.workspace) {
|
|
244
|
+
printError("Error: Either --url or --workspace is required");
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
if (!options.url && !options.path) {
|
|
248
|
+
printError("Error: --path is required when not using --url");
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
162
251
|
startSpinner("Reading file...");
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
252
|
+
let response;
|
|
253
|
+
let displayPath;
|
|
254
|
+
if (options.url) {
|
|
255
|
+
displayPath = options.url;
|
|
256
|
+
response = await httpGet(
|
|
257
|
+
`/files/read-by-url?url=${encodeURIComponent(options.url)}`
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
displayPath = options.path;
|
|
261
|
+
const spaceParams = validateSpaceOptions(options);
|
|
262
|
+
const qs = buildFileQueryString(options.path, spaceParams);
|
|
263
|
+
response = await httpGet(
|
|
264
|
+
`/workspaces/${options.workspace}/files/read${qs}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
166
267
|
if (!response.ok) {
|
|
167
268
|
stopSpinner(false, "Failed");
|
|
168
269
|
if (response.status === 404) {
|
|
169
|
-
print(`${colors.red}\u2620 ${
|
|
270
|
+
print(`${colors.red}\u2620 ${displayPath} does not exist${colors.reset}`);
|
|
170
271
|
} else {
|
|
171
272
|
printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`);
|
|
172
273
|
}
|
|
@@ -175,15 +276,17 @@ function registerFileCommand(program2) {
|
|
|
175
276
|
const { type, content } = response.data;
|
|
176
277
|
if (type === "directory") {
|
|
177
278
|
stopSpinner(false, "Failed");
|
|
178
|
-
print(`${colors.red}\u2620 ${
|
|
279
|
+
print(`${colors.red}\u2620 ${displayPath} is a directory${colors.reset}`);
|
|
179
280
|
process.exit(1);
|
|
180
281
|
}
|
|
181
282
|
stopSpinner(true, "Done");
|
|
182
283
|
print(content ?? "");
|
|
183
284
|
});
|
|
184
|
-
|
|
285
|
+
addSpaceOptions(
|
|
286
|
+
file.command("put").description("Upload a file").requiredOption("-p, --path <path>", "Remote file path").requiredOption("-l, --local-path <localPath>", "Local file path").option("-r, --recursive", "Create parent directories if needed")
|
|
287
|
+
).action(
|
|
185
288
|
async (options) => {
|
|
186
|
-
const
|
|
289
|
+
const spaceParams = validateSpaceOptions(options);
|
|
187
290
|
if (!fs.existsSync(options.localPath)) {
|
|
188
291
|
print(`${colors.red}\u2620 Local file not found: ${options.localPath}${colors.reset}`);
|
|
189
292
|
process.exit(1);
|
|
@@ -210,8 +313,8 @@ function registerFileCommand(program2) {
|
|
|
210
313
|
{
|
|
211
314
|
path: options.path,
|
|
212
315
|
content,
|
|
213
|
-
|
|
214
|
-
|
|
316
|
+
recursive: options.recursive ?? false,
|
|
317
|
+
...spaceParams
|
|
215
318
|
}
|
|
216
319
|
);
|
|
217
320
|
if (!response.ok) {
|
|
@@ -229,16 +332,18 @@ function registerFileCommand(program2) {
|
|
|
229
332
|
stopSpinner(true, `${action}: ${response.data.path}`);
|
|
230
333
|
}
|
|
231
334
|
);
|
|
232
|
-
|
|
335
|
+
addSpaceOptions(
|
|
336
|
+
file.command("mkdir").description("Create a directory").requiredOption("-p, --path <path>", "Directory path").option("-r, --recursive", "Create parent directories if needed")
|
|
337
|
+
).action(
|
|
233
338
|
async (options) => {
|
|
234
|
-
const
|
|
339
|
+
const spaceParams = validateSpaceOptions(options);
|
|
235
340
|
startSpinner("Creating directory...");
|
|
236
341
|
const response = await httpPost(
|
|
237
342
|
`/workspaces/${options.workspace}/files/mkdir`,
|
|
238
343
|
{
|
|
239
344
|
path: options.path,
|
|
240
|
-
|
|
241
|
-
|
|
345
|
+
recursive: options.recursive ?? false,
|
|
346
|
+
...spaceParams
|
|
242
347
|
}
|
|
243
348
|
);
|
|
244
349
|
if (!response.ok) {
|
|
@@ -256,15 +361,17 @@ function registerFileCommand(program2) {
|
|
|
256
361
|
stopSpinner(true, `${action}: ${response.data.path}`);
|
|
257
362
|
}
|
|
258
363
|
);
|
|
259
|
-
|
|
364
|
+
addSpaceOptions(
|
|
365
|
+
file.command("del").description("Delete a file or empty directory").requiredOption("-p, --path <path>", "File or directory path")
|
|
366
|
+
).action(
|
|
260
367
|
async (options) => {
|
|
261
|
-
const
|
|
368
|
+
const spaceParams = validateSpaceOptions(options);
|
|
262
369
|
startSpinner("Deleting...");
|
|
263
370
|
const response = await httpDelete(
|
|
264
371
|
`/workspaces/${options.workspace}/files/delete`,
|
|
265
372
|
{
|
|
266
373
|
path: options.path,
|
|
267
|
-
|
|
374
|
+
...spaceParams
|
|
268
375
|
}
|
|
269
376
|
);
|
|
270
377
|
if (!response.ok) {
|
|
@@ -385,14 +492,39 @@ function registerWorkspaceCommand(program2) {
|
|
|
385
492
|
}
|
|
386
493
|
}
|
|
387
494
|
});
|
|
495
|
+
const space = program2.command("space").description("Manage spaces");
|
|
496
|
+
space.command("list").description("List accessible spaces in a workspace").requiredOption("-w, --workspace <workspaceId>", "Workspace ID").action(async (options) => {
|
|
497
|
+
const { workspace: workspaceId } = options;
|
|
498
|
+
startSpinner("Fetching spaces...");
|
|
499
|
+
const response = await httpGet(`/workspaces/${workspaceId}/spaces`);
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
stopSpinner(false, "Failed to fetch spaces");
|
|
502
|
+
printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`);
|
|
503
|
+
process.exit(1);
|
|
504
|
+
}
|
|
505
|
+
const { spaces } = response.data;
|
|
506
|
+
if (spaces.length === 0) {
|
|
507
|
+
stopSpinner(true, "No spaces found.");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
stopSpinner(true, `Found ${spaces.length} space(s)`);
|
|
511
|
+
print(
|
|
512
|
+
`${colors.bold}TYPE${colors.reset} ${colors.bold}TEAM_SPACE_ID${colors.reset} ${colors.bold}TEAMMATE_ID${colors.reset} ${colors.bold}NAME${colors.reset}`
|
|
513
|
+
);
|
|
514
|
+
for (const s of spaces) {
|
|
515
|
+
const teamSpaceId = s.teamSpaceId ?? "";
|
|
516
|
+
const teammateId = s.agentId != null ? String(s.agentId) : "";
|
|
517
|
+
print(`${s.type} ${teamSpaceId} ${teammateId} ${s.name}`);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
388
520
|
}
|
|
389
521
|
|
|
390
522
|
// src/index.ts
|
|
391
523
|
updateNotifier({
|
|
392
|
-
pkg: { name: "@moxt-ai/cli", version: "0.
|
|
524
|
+
pkg: { name: "@moxt-ai/cli", version: "0.2.0" }
|
|
393
525
|
}).notify();
|
|
394
526
|
var program = new Command();
|
|
395
|
-
program.name("moxt").description("Moxt CLI - AI Workspace").version("0.
|
|
527
|
+
program.name("moxt").description("Moxt CLI - AI Workspace").version("0.2.0").action(() => {
|
|
396
528
|
program.help();
|
|
397
529
|
});
|
|
398
530
|
registerWhoamiCommand(program);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/file.ts","../src/utils/http.ts","../src/utils/config.ts","../src/utils/output.ts","../src/utils/spinner.ts","../src/commands/whoami.ts","../src/commands/workspace.ts"],"sourcesContent":["import { Command } from 'commander'\nimport updateNotifier from 'update-notifier'\nimport { registerFileCommand } from './commands/file.js'\nimport { registerWhoamiCommand } from './commands/whoami.js'\nimport { registerWorkspaceCommand } from './commands/workspace.js'\n\ndeclare const __CLI_VERSION__: string\n\nupdateNotifier({\n pkg: { name: '@moxt-ai/cli', version: __CLI_VERSION__ },\n}).notify()\n\nconst program = new Command()\n\nprogram\n .name('moxt')\n .description('Moxt CLI - AI Workspace')\n .version(__CLI_VERSION__)\n .action(() => {\n program.help()\n })\n\nregisterWhoamiCommand(program)\nregisterWorkspaceCommand(program)\nregisterFileCommand(program)\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error('CLI Error:', err)\n process.exit(1)\n})\n","import * as fs from 'node:fs'\nimport { Command } from 'commander'\nimport { httpDelete, httpGet, httpPost, httpPut } from '../utils/http.js'\nimport { colors, print, printError } from '../utils/output.js'\nimport { startSpinner, stopSpinner } from '../utils/spinner.js'\n\ninterface FileEntry {\n name: string\n type: 'file' | 'directory'\n size: number | null\n}\n\ninterface FileReadResponse {\n path: string\n type: 'file' | 'directory'\n entries: FileEntry[] | null\n content: string | null\n}\n\ninterface FileWriteResponse {\n path: string\n created: boolean\n}\n\ninterface MkdirResponse {\n path: string\n created: boolean\n}\n\ninterface DeleteResponse {\n path: string\n}\n\n/**\n * 检测 Buffer 是否为二进制内容\n * 文本文件不应包含控制字符(除了 tab、换行、回车)\n */\nfunction isBinaryContent(buffer: Buffer): boolean {\n const sampleSize = Math.min(buffer.length, 8192)\n\n for (let i = 0; i < sampleSize; i++) {\n const byte = buffer[i]\n // 控制字符(除了 tab=9, newline=10, carriage return=13)\n if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {\n return true\n }\n }\n\n return false\n}\n\nexport function registerFileCommand(program: Command): void {\n const file = program.command('file').description('File operations')\n\n file.command('list')\n .description('List directory contents')\n .requiredOption('-w, --workspace <id>', 'Workspace ID')\n .option('-p, --path <path>', 'Directory path', '/')\n .option('-s, --space <type>', 'Space type: personal or team', 'team')\n .action(async (options: { workspace: string; path: string; space: string }) => {\n const space = options.space\n startSpinner('Listing files...')\n const response = await httpGet<FileReadResponse>(\n `/workspaces/${options.workspace}/files/list?path=${encodeURIComponent(options.path)}&space=${space}`,\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ ${options.path} does not exist${colors.reset}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const { path, entries } = response.data\n\n if (!entries || entries.length === 0) {\n stopSpinner(true, `${path}: empty directory`)\n return\n }\n\n stopSpinner(true, path)\n for (const entry of entries) {\n if (entry.type === 'directory') {\n print(`${colors.blue}${entry.name}/${colors.reset}`)\n } else {\n print(entry.name)\n }\n }\n })\n\n file.command('read')\n .description('Read file content')\n .requiredOption('-w, --workspace <id>', 'Workspace ID')\n .requiredOption('-p, --path <path>', 'File path')\n .option('-s, --space <type>', 'Space type: personal or team', 'team')\n .action(async (options: { workspace: string; path: string; space: string }) => {\n const space = options.space\n startSpinner('Reading file...')\n const response = await httpGet<FileReadResponse>(\n `/workspaces/${options.workspace}/files/read?path=${encodeURIComponent(options.path)}&space=${space}`,\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ ${options.path} does not exist${colors.reset}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const { type, content } = response.data\n\n if (type === 'directory') {\n stopSpinner(false, 'Failed')\n print(`${colors.red}☠ ${options.path} is a directory${colors.reset}`)\n process.exit(1)\n }\n\n stopSpinner(true, 'Done')\n print(content ?? '')\n })\n\n file.command('put')\n .description('Upload a file')\n .requiredOption('-w, --workspace <id>', 'Workspace ID')\n .requiredOption('-p, --path <path>', 'Remote file path')\n .requiredOption('-l, --local-path <localPath>', 'Local file path')\n .option('-s, --space <type>', 'Space type: personal or team', 'team')\n .option('-r, --recursive', 'Create parent directories if needed')\n .action(\n async (options: {\n workspace: string\n path: string\n localPath: string\n space: string\n recursive?: boolean\n }) => {\n const space = options.space\n\n // 检查本地文件是否存在\n if (!fs.existsSync(options.localPath)) {\n print(`${colors.red}☠ Local file not found: ${options.localPath}${colors.reset}`)\n process.exit(1)\n }\n\n // 检查是否为文件\n const stats = fs.statSync(options.localPath)\n if (!stats.isFile()) {\n print(`${colors.red}☠ Not a file: ${options.localPath}${colors.reset}`)\n process.exit(1)\n }\n\n // 检查文件大小 (10MB)\n const maxSize = 10 * 1024 * 1024\n if (stats.size > maxSize) {\n print(`${colors.red}☠ File too large (max 10MB): ${options.localPath}${colors.reset}`)\n process.exit(1)\n }\n\n // 读取文件内容并检查是否为二进制\n const buffer = fs.readFileSync(options.localPath)\n if (isBinaryContent(buffer)) {\n print(`${colors.red}☠ Binary files are not allowed. Only text files can be uploaded.${colors.reset}`)\n process.exit(1)\n }\n\n const content = buffer.toString('utf8')\n\n startSpinner('Uploading...')\n const response = await httpPut<FileWriteResponse>(\n `/workspaces/${options.workspace}/files/write`,\n {\n path: options.path,\n content,\n space,\n recursive: options.recursive ?? false,\n },\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ Parent directory does not exist${colors.reset}`)\n } else if (response.status === 400) {\n printError(`Error: ${JSON.stringify(response.data)}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const action = response.data.created ? 'Created' : 'Updated'\n stopSpinner(true, `${action}: ${response.data.path}`)\n },\n )\n\n file.command('mkdir')\n .description('Create a directory')\n .requiredOption('-w, --workspace <id>', 'Workspace ID')\n .requiredOption('-p, --path <path>', 'Directory path')\n .option('-s, --space <type>', 'Space type: personal or team', 'team')\n .option('-r, --recursive', 'Create parent directories if needed')\n .action(\n async (options: {\n workspace: string\n path: string\n space: string\n recursive?: boolean\n }) => {\n const space = options.space\n\n startSpinner('Creating directory...')\n const response = await httpPost<MkdirResponse>(\n `/workspaces/${options.workspace}/files/mkdir`,\n {\n path: options.path,\n space,\n recursive: options.recursive ?? false,\n },\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ Parent directory does not exist${colors.reset}`)\n } else if (response.status === 400) {\n printError(`Error: ${JSON.stringify(response.data)}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const action = response.data.created ? 'Created' : 'Already exists'\n stopSpinner(true, `${action}: ${response.data.path}`)\n },\n )\n\n file.command('del')\n .description('Delete a file or empty directory')\n .requiredOption('-w, --workspace <id>', 'Workspace ID')\n .requiredOption('-p, --path <path>', 'File or directory path')\n .option('-s, --space <type>', 'Space type: personal or team', 'team')\n .action(\n async (options: {\n workspace: string\n path: string\n space: string\n }) => {\n const space = options.space\n\n startSpinner('Deleting...')\n const response = await httpDelete<DeleteResponse>(\n `/workspaces/${options.workspace}/files/delete`,\n {\n path: options.path,\n space,\n },\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ Path not found: ${options.path}${colors.reset}`)\n } else if (response.status === 400) {\n print(`${colors.red}☠ Cannot delete non-empty directory${colors.reset}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n stopSpinner(true, `Deleted: ${response.data.path}`)\n },\n )\n}\n","import { arch, platform } from 'os'\nimport { getApiBaseUrl, getApiKey } from './config.js'\n\ndeclare const __CLI_VERSION__: string\n\nexport interface ApiResponse<T> {\n ok: boolean\n status: number\n data: T\n}\n\nfunction getUserAgent(): string {\n return `moxt-cli/${__CLI_VERSION__} (${platform()}/${arch()}) node/${process.versions.node}`\n}\n\nasync function request<T>(method: string, path: string, body?: unknown): Promise<ApiResponse<T>> {\n const apiKey = getApiKey()\n const url = `${getApiBaseUrl()}${path}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n 'User-Agent': getUserAgent(),\n },\n body: body ? JSON.stringify(body) : undefined,\n })\n\n let data: T\n const contentType = response.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n data = (await response.json()) as T\n } else {\n data = (await response.text()) as T\n }\n\n if (response.status === 401) {\n console.error('Authentication failed. Check your MOXT_API_KEY.')\n console.error('Get a new key at: https://moxt.ai')\n process.exit(1)\n }\n\n return {\n ok: response.ok,\n status: response.status,\n data,\n }\n}\n\nexport async function httpGet<T>(path: string): Promise<ApiResponse<T>> {\n return request<T>('GET', path)\n}\n\nexport async function httpPut<T>(path: string, body: unknown): Promise<ApiResponse<T>> {\n return request<T>('PUT', path, body)\n}\n\nexport async function httpPost<T>(path: string, body: unknown): Promise<ApiResponse<T>> {\n return request<T>('POST', path, body)\n}\n\nexport async function httpDelete<T>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>('DELETE', path, body)\n}\n","const DEFAULT_HOST = 'api.moxt.ai'\n\nexport function getApiBaseUrl(): string {\n const host = process.env.MOXT_HOST ?? DEFAULT_HOST\n return `https://${host}/openapi/v1`\n}\n\nexport function getApiKey(): string {\n const apiKey = process.env.MOXT_API_KEY\n\n if (!apiKey) {\n console.error('MOXT_API_KEY environment variable is not set.')\n console.error('')\n console.error('Get your API key at: https://moxt.ai')\n console.error('')\n console.error('Then set it:')\n console.error(' export MOXT_API_KEY=moxt_...')\n process.exit(1)\n }\n\n return apiKey\n}\n","// ANSI 颜色\nexport const colors = {\n bold: '\\x1b[1m',\n blue: '\\x1b[1;34m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n dim: '\\x1b[2m',\n reset: '\\x1b[0m',\n}\n\nexport function print(message: string): void {\n console.log(message)\n}\n\nexport function printError(message: string): void {\n console.error(`${colors.red}${message}${colors.reset}`)\n}\n","const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']\n\nlet intervalId: ReturnType<typeof setInterval> | null = null\nlet frameIndex = 0\n\nexport function startSpinner(message: string): void {\n frameIndex = 0\n process.stdout.write(`${frames[frameIndex]} ${message}`)\n\n intervalId = setInterval(() => {\n frameIndex = (frameIndex + 1) % frames.length\n process.stdout.write(`\\r${frames[frameIndex]} ${message}`)\n }, 80)\n}\n\nexport function stopSpinner(success: boolean, message?: string): void {\n if (intervalId) {\n clearInterval(intervalId)\n intervalId = null\n }\n\n const symbol = success ? '\\x1b[32m✓\\x1b[0m' : '\\x1b[31m✗\\x1b[0m'\n // \\x1b[2K 清除整行,\\r 回到行首\n process.stdout.write(`\\x1b[2K\\r${symbol} ${message ?? ''}\\n`)\n}\n","import { Command } from 'commander'\nimport { httpGet } from '../utils/http.js'\nimport { colors, print, printError } from '../utils/output.js'\nimport { startSpinner, stopSpinner } from '../utils/spinner.js'\n\ninterface WhoamiResponse {\n nickname: string\n email: string\n}\n\nexport function registerWhoamiCommand(program: Command): void {\n program\n .command('whoami')\n .description('Show current user information')\n .action(async () => {\n startSpinner('Fetching user info...')\n const response = await httpGet<WhoamiResponse>('/users/whoami')\n\n if (response.ok) {\n stopSpinner(true, 'Done')\n print(`${colors.bold}Nickname${colors.reset}: ${response.data.nickname}`)\n print(`${colors.bold}Email${colors.reset}: ${response.data.email}`)\n } else {\n stopSpinner(false, 'Failed')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n })\n}\n","import { Command } from 'commander'\nimport { httpDelete, httpGet } from '../utils/http.js'\nimport { colors, print, printError } from '../utils/output.js'\nimport { startSpinner, stopSpinner } from '../utils/spinner.js'\n\ninterface WorkspaceItem {\n workspaceId: string\n name: string\n}\n\ninterface WorkspaceListResponse {\n workspaces: WorkspaceItem[]\n}\n\ninterface MemberItem {\n userId: number\n email: string\n displayName: string | null\n role: string\n}\n\ninterface MembersResponse {\n members: MemberItem[]\n}\n\n// Email regex (same as web/src/utils/email.ts)\nconst emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/\n\nfunction isValidEmail(email: string): boolean {\n return emailRegex.test(email)\n}\n\nexport function registerWorkspaceCommand(program: Command): void {\n const workspace = program.command('workspace').description('Workspace management')\n\n workspace\n .command('list')\n .description('List all workspaces you belong to')\n .action(async () => {\n startSpinner('Fetching workspaces...')\n const response = await httpGet<WorkspaceListResponse>('/workspaces')\n\n if (response.ok) {\n const { workspaces } = response.data\n if (workspaces.length === 0) {\n stopSpinner(true, 'No workspaces found.')\n return\n }\n\n stopSpinner(true, `Found ${workspaces.length} workspace(s)`)\n\n for (const ws of workspaces) {\n print(`${colors.bold}${ws.workspaceId}${colors.reset}\\t${ws.name}`)\n }\n } else {\n stopSpinner(false, 'Failed')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n })\n\n const members = program.command('members').description('Manage workspace members')\n\n members\n .command('list')\n .description('List all members in a workspace')\n .requiredOption('-w, --workspace <workspaceId>', 'Workspace ID')\n .action(async (options: { workspace: string }) => {\n const { workspace: workspaceId } = options\n\n startSpinner('Fetching workspace members...')\n const response = await httpGet<MembersResponse>(`/workspaces/${workspaceId}/members`)\n\n if (!response.ok) {\n stopSpinner(false, 'Failed to fetch members')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n\n const { members } = response.data\n if (members.length === 0) {\n stopSpinner(true, 'No members found.')\n return\n }\n\n stopSpinner(true, `Found ${members.length} member(s)`)\n\n for (const member of members) {\n const name = member.displayName || '-'\n print(`${colors.bold}${member.email}${colors.reset}\\t${name}\\t${member.role}`)\n }\n })\n\n members\n .command('remove')\n .description('Remove members from a workspace')\n .requiredOption('-w, --workspace <workspaceId>', 'Workspace ID')\n .argument('<emails>', 'Emails to remove (comma-separated)')\n .action(async (emailsArg: string, options: { workspace: string }) => {\n const { workspace: workspaceId } = options\n\n const emails = emailsArg.split(',').map((e) => e.trim()).filter(Boolean)\n if (emails.length === 0) {\n printError('No valid emails provided')\n process.exit(1)\n }\n\n // Validate email format\n const invalidEmails = emails.filter((e) => !isValidEmail(e))\n if (invalidEmails.length > 0) {\n for (const email of invalidEmails) {\n printError(`Invalid email format: ${email}`)\n }\n process.exit(1)\n }\n\n // Fetch members to get userId by email\n startSpinner('Fetching workspace members...')\n const membersResponse = await httpGet<MembersResponse>(`/workspaces/${workspaceId}/members`)\n\n if (!membersResponse.ok) {\n stopSpinner(false, 'Failed to fetch members')\n printError(`Error [${membersResponse.status}]: ${JSON.stringify(membersResponse.data)}`)\n process.exit(1)\n }\n\n stopSpinner(true, 'Members fetched')\n\n const { members } = membersResponse.data\n const emailToMember = new Map(members.map((m) => [m.email.toLowerCase(), m]))\n\n // Remove each member\n for (const email of emails) {\n const member = emailToMember.get(email.toLowerCase())\n if (!member) {\n print(`${colors.red}✗${colors.reset} ${email} - not found in workspace`)\n continue\n }\n\n const deleteResponse = await httpDelete(`/workspaces/${workspaceId}/members/${member.userId}`)\n\n if (deleteResponse.ok) {\n print(`${colors.green}✓${colors.reset} ${email} - removed`)\n } else {\n print(`${colors.red}✗${colors.reset} ${email} - failed: ${JSON.stringify(deleteResponse.data)}`)\n }\n }\n })\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAO,oBAAoB;;;ACD3B,YAAY,QAAQ;;;ACApB,SAAS,MAAM,gBAAgB;;;ACA/B,IAAM,eAAe;AAEd,SAAS,gBAAwB;AACpC,QAAM,OAAO,QAAQ,IAAI,aAAa;AACtC,SAAO,WAAW,IAAI;AAC1B;AAEO,SAAS,YAAoB;AAChC,QAAM,SAAS,QAAQ,IAAI;AAE3B,MAAI,CAAC,QAAQ;AACT,YAAQ,MAAM,+CAA+C;AAC7D,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,sCAAsC;AACpD,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,cAAc;AAC5B,YAAQ,MAAM,gCAAgC;AAC9C,YAAQ,KAAK,CAAC;AAAA,EAClB;AAEA,SAAO;AACX;;;ADVA,SAAS,eAAuB;AAC5B,SAAO,YAAY,OAAe,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU,QAAQ,SAAS,IAAI;AAC9F;AAEA,eAAe,QAAW,QAAgB,MAAc,MAAyC;AAC7F,QAAM,SAAS,UAAU;AACzB,QAAM,MAAM,GAAG,cAAc,CAAC,GAAG,IAAI;AAErC,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAC9B;AAAA,IACA,SAAS;AAAA,MACL,gBAAgB;AAAA,MAChB,eAAe,UAAU,MAAM;AAAA,MAC/B,cAAc,aAAa;AAAA,IAC/B;AAAA,IACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EACxC,CAAC;AAED,MAAI;AACJ,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;AACvD,MAAI,aAAa,SAAS,kBAAkB,GAAG;AAC3C,WAAQ,MAAM,SAAS,KAAK;AAAA,EAChC,OAAO;AACH,WAAQ,MAAM,SAAS,KAAK;AAAA,EAChC;AAEA,MAAI,SAAS,WAAW,KAAK;AACzB,YAAQ,MAAM,iDAAiD;AAC/D,YAAQ,MAAM,mCAAmC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAClB;AAEA,SAAO;AAAA,IACH,IAAI,SAAS;AAAA,IACb,QAAQ,SAAS;AAAA,IACjB;AAAA,EACJ;AACJ;AAEA,eAAsB,QAAW,MAAuC;AACpE,SAAO,QAAW,OAAO,IAAI;AACjC;AAEA,eAAsB,QAAW,MAAc,MAAwC;AACnF,SAAO,QAAW,OAAO,MAAM,IAAI;AACvC;AAEA,eAAsB,SAAY,MAAc,MAAwC;AACpF,SAAO,QAAW,QAAQ,MAAM,IAAI;AACxC;AAEA,eAAsB,WAAc,MAAc,MAAyC;AACvF,SAAO,QAAW,UAAU,MAAM,IAAI;AAC1C;;;AE/DO,IAAM,SAAS;AAAA,EAClB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AACX;AAEO,SAAS,MAAM,SAAuB;AACzC,UAAQ,IAAI,OAAO;AACvB;AAEO,SAAS,WAAW,SAAuB;AAC9C,UAAQ,MAAM,GAAG,OAAO,GAAG,GAAG,OAAO,GAAG,OAAO,KAAK,EAAE;AAC1D;;;AChBA,IAAM,SAAS,CAAC,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,QAAG;AAEhE,IAAI,aAAoD;AACxD,IAAI,aAAa;AAEV,SAAS,aAAa,SAAuB;AAChD,eAAa;AACb,UAAQ,OAAO,MAAM,GAAG,OAAO,UAAU,CAAC,IAAI,OAAO,EAAE;AAEvD,eAAa,YAAY,MAAM;AAC3B,kBAAc,aAAa,KAAK,OAAO;AACvC,YAAQ,OAAO,MAAM,KAAK,OAAO,UAAU,CAAC,IAAI,OAAO,EAAE;AAAA,EAC7D,GAAG,EAAE;AACT;AAEO,SAAS,YAAY,SAAkB,SAAwB;AAClE,MAAI,YAAY;AACZ,kBAAc,UAAU;AACxB,iBAAa;AAAA,EACjB;AAEA,QAAM,SAAS,UAAU,0BAAqB;AAE9C,UAAQ,OAAO,MAAM,YAAY,MAAM,IAAI,WAAW,EAAE;AAAA,CAAI;AAChE;;;AJaA,SAAS,gBAAgB,QAAyB;AAC9C,QAAM,aAAa,KAAK,IAAI,OAAO,QAAQ,IAAI;AAE/C,WAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACjC,UAAM,OAAO,OAAO,CAAC;AAErB,QAAI,OAAO,MAAM,SAAS,KAAK,SAAS,MAAM,SAAS,IAAI;AACvD,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEO,SAAS,oBAAoBA,UAAwB;AACxD,QAAM,OAAOA,SAAQ,QAAQ,MAAM,EAAE,YAAY,iBAAiB;AAElE,OAAK,QAAQ,MAAM,EACd,YAAY,yBAAyB,EACrC,eAAe,wBAAwB,cAAc,EACrD,OAAO,qBAAqB,kBAAkB,GAAG,EACjD,OAAO,sBAAsB,gCAAgC,MAAM,EACnE,OAAO,OAAO,YAAgE;AAC3E,UAAM,QAAQ,QAAQ;AACtB,iBAAa,kBAAkB;AAC/B,UAAM,WAAW,MAAM;AAAA,MACnB,eAAe,QAAQ,SAAS,oBAAoB,mBAAmB,QAAQ,IAAI,CAAC,UAAU,KAAK;AAAA,IACvG;AAEA,QAAI,CAAC,SAAS,IAAI;AACd,kBAAY,OAAO,QAAQ;AAC3B,UAAI,SAAS,WAAW,KAAK;AACzB,cAAM,GAAG,OAAO,GAAG,UAAK,QAAQ,IAAI,kBAAkB,OAAO,KAAK,EAAE;AAAA,MACxE,OAAO;AACH,mBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,MAC7E;AACA,cAAQ,KAAK,CAAC;AAAA,IAClB;AAEA,UAAM,EAAE,MAAM,QAAQ,IAAI,SAAS;AAEnC,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AAClC,kBAAY,MAAM,GAAG,IAAI,mBAAmB;AAC5C;AAAA,IACJ;AAEA,gBAAY,MAAM,IAAI;AACtB,eAAW,SAAS,SAAS;AACzB,UAAI,MAAM,SAAS,aAAa;AAC5B,cAAM,GAAG,OAAO,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,KAAK,EAAE;AAAA,MACvD,OAAO;AACH,cAAM,MAAM,IAAI;AAAA,MACpB;AAAA,IACJ;AAAA,EACJ,CAAC;AAEL,OAAK,QAAQ,MAAM,EACd,YAAY,mBAAmB,EAC/B,eAAe,wBAAwB,cAAc,EACrD,eAAe,qBAAqB,WAAW,EAC/C,OAAO,sBAAsB,gCAAgC,MAAM,EACnE,OAAO,OAAO,YAAgE;AAC3E,UAAM,QAAQ,QAAQ;AACtB,iBAAa,iBAAiB;AAC9B,UAAM,WAAW,MAAM;AAAA,MACnB,eAAe,QAAQ,SAAS,oBAAoB,mBAAmB,QAAQ,IAAI,CAAC,UAAU,KAAK;AAAA,IACvG;AAEA,QAAI,CAAC,SAAS,IAAI;AACd,kBAAY,OAAO,QAAQ;AAC3B,UAAI,SAAS,WAAW,KAAK;AACzB,cAAM,GAAG,OAAO,GAAG,UAAK,QAAQ,IAAI,kBAAkB,OAAO,KAAK,EAAE;AAAA,MACxE,OAAO;AACH,mBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,MAC7E;AACA,cAAQ,KAAK,CAAC;AAAA,IAClB;AAEA,UAAM,EAAE,MAAM,QAAQ,IAAI,SAAS;AAEnC,QAAI,SAAS,aAAa;AACtB,kBAAY,OAAO,QAAQ;AAC3B,YAAM,GAAG,OAAO,GAAG,UAAK,QAAQ,IAAI,kBAAkB,OAAO,KAAK,EAAE;AACpE,cAAQ,KAAK,CAAC;AAAA,IAClB;AAEA,gBAAY,MAAM,MAAM;AACxB,UAAM,WAAW,EAAE;AAAA,EACvB,CAAC;AAEL,OAAK,QAAQ,KAAK,EACb,YAAY,eAAe,EAC3B,eAAe,wBAAwB,cAAc,EACrD,eAAe,qBAAqB,kBAAkB,EACtD,eAAe,gCAAgC,iBAAiB,EAChE,OAAO,sBAAsB,gCAAgC,MAAM,EACnE,OAAO,mBAAmB,qCAAqC,EAC/D;AAAA,IACG,OAAO,YAMD;AACF,YAAM,QAAQ,QAAQ;AAGtB,UAAI,CAAI,cAAW,QAAQ,SAAS,GAAG;AACnC,cAAM,GAAG,OAAO,GAAG,gCAA2B,QAAQ,SAAS,GAAG,OAAO,KAAK,EAAE;AAChF,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAGA,YAAM,QAAW,YAAS,QAAQ,SAAS;AAC3C,UAAI,CAAC,MAAM,OAAO,GAAG;AACjB,cAAM,GAAG,OAAO,GAAG,sBAAiB,QAAQ,SAAS,GAAG,OAAO,KAAK,EAAE;AACtE,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAGA,YAAM,UAAU,KAAK,OAAO;AAC5B,UAAI,MAAM,OAAO,SAAS;AACtB,cAAM,GAAG,OAAO,GAAG,qCAAgC,QAAQ,SAAS,GAAG,OAAO,KAAK,EAAE;AACrF,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAGA,YAAM,SAAY,gBAAa,QAAQ,SAAS;AAChD,UAAI,gBAAgB,MAAM,GAAG;AACzB,cAAM,GAAG,OAAO,GAAG,wEAAmE,OAAO,KAAK,EAAE;AACpG,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAEA,YAAM,UAAU,OAAO,SAAS,MAAM;AAEtC,mBAAa,cAAc;AAC3B,YAAM,WAAW,MAAM;AAAA,QACnB,eAAe,QAAQ,SAAS;AAAA,QAChC;AAAA,UACI,MAAM,QAAQ;AAAA,UACd;AAAA,UACA;AAAA,UACA,WAAW,QAAQ,aAAa;AAAA,QACpC;AAAA,MACJ;AAEA,UAAI,CAAC,SAAS,IAAI;AACd,oBAAY,OAAO,QAAQ;AAC3B,YAAI,SAAS,WAAW,KAAK;AACzB,gBAAM,GAAG,OAAO,GAAG,yCAAoC,OAAO,KAAK,EAAE;AAAA,QACzE,WAAW,SAAS,WAAW,KAAK;AAChC,qBAAW,UAAU,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,QACxD,OAAO;AACH,qBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,QAC7E;AACA,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAEA,YAAM,SAAS,SAAS,KAAK,UAAU,YAAY;AACnD,kBAAY,MAAM,GAAG,MAAM,KAAK,SAAS,KAAK,IAAI,EAAE;AAAA,IACxD;AAAA,EACJ;AAEJ,OAAK,QAAQ,OAAO,EACf,YAAY,oBAAoB,EAChC,eAAe,wBAAwB,cAAc,EACrD,eAAe,qBAAqB,gBAAgB,EACpD,OAAO,sBAAsB,gCAAgC,MAAM,EACnE,OAAO,mBAAmB,qCAAqC,EAC/D;AAAA,IACG,OAAO,YAKD;AACF,YAAM,QAAQ,QAAQ;AAEtB,mBAAa,uBAAuB;AACpC,YAAM,WAAW,MAAM;AAAA,QACnB,eAAe,QAAQ,SAAS;AAAA,QAChC;AAAA,UACI,MAAM,QAAQ;AAAA,UACd;AAAA,UACA,WAAW,QAAQ,aAAa;AAAA,QACpC;AAAA,MACJ;AAEA,UAAI,CAAC,SAAS,IAAI;AACd,oBAAY,OAAO,QAAQ;AAC3B,YAAI,SAAS,WAAW,KAAK;AACzB,gBAAM,GAAG,OAAO,GAAG,yCAAoC,OAAO,KAAK,EAAE;AAAA,QACzE,WAAW,SAAS,WAAW,KAAK;AAChC,qBAAW,UAAU,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,QACxD,OAAO;AACH,qBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,QAC7E;AACA,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAEA,YAAM,SAAS,SAAS,KAAK,UAAU,YAAY;AACnD,kBAAY,MAAM,GAAG,MAAM,KAAK,SAAS,KAAK,IAAI,EAAE;AAAA,IACxD;AAAA,EACJ;AAEJ,OAAK,QAAQ,KAAK,EACb,YAAY,kCAAkC,EAC9C,eAAe,wBAAwB,cAAc,EACrD,eAAe,qBAAqB,wBAAwB,EAC5D,OAAO,sBAAsB,gCAAgC,MAAM,EACnE;AAAA,IACG,OAAO,YAID;AACF,YAAM,QAAQ,QAAQ;AAEtB,mBAAa,aAAa;AAC1B,YAAM,WAAW,MAAM;AAAA,QACnB,eAAe,QAAQ,SAAS;AAAA,QAChC;AAAA,UACI,MAAM,QAAQ;AAAA,UACd;AAAA,QACJ;AAAA,MACJ;AAEA,UAAI,CAAC,SAAS,IAAI;AACd,oBAAY,OAAO,QAAQ;AAC3B,YAAI,SAAS,WAAW,KAAK;AACzB,gBAAM,GAAG,OAAO,GAAG,0BAAqB,QAAQ,IAAI,GAAG,OAAO,KAAK,EAAE;AAAA,QACzE,WAAW,SAAS,WAAW,KAAK;AAChC,gBAAM,GAAG,OAAO,GAAG,2CAAsC,OAAO,KAAK,EAAE;AAAA,QAC3E,OAAO;AACH,qBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AAAA,QAC7E;AACA,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAEA,kBAAY,MAAM,YAAY,SAAS,KAAK,IAAI,EAAE;AAAA,IACtD;AAAA,EACJ;AACR;;;AK9QO,SAAS,sBAAsBC,UAAwB;AAC1D,EAAAA,SACK,QAAQ,QAAQ,EAChB,YAAY,+BAA+B,EAC3C,OAAO,YAAY;AAChB,iBAAa,uBAAuB;AACpC,UAAM,WAAW,MAAM,QAAwB,eAAe;AAE9D,QAAI,SAAS,IAAI;AACb,kBAAY,MAAM,MAAM;AACxB,YAAM,GAAG,OAAO,IAAI,WAAW,OAAO,KAAK,KAAK,SAAS,KAAK,QAAQ,EAAE;AACxE,YAAM,GAAG,OAAO,IAAI,QAAQ,OAAO,KAAK,KAAK,SAAS,KAAK,KAAK,EAAE;AAAA,IACtE,OAAO;AACH,kBAAY,OAAO,QAAQ;AAC3B,iBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AACzE,cAAQ,KAAK,CAAC;AAAA,IAClB;AAAA,EACJ,CAAC;AACT;;;ACFA,IAAM,aAAa;AAEnB,SAAS,aAAa,OAAwB;AAC1C,SAAO,WAAW,KAAK,KAAK;AAChC;AAEO,SAAS,yBAAyBC,UAAwB;AAC7D,QAAM,YAAYA,SAAQ,QAAQ,WAAW,EAAE,YAAY,sBAAsB;AAEjF,YACK,QAAQ,MAAM,EACd,YAAY,mCAAmC,EAC/C,OAAO,YAAY;AAChB,iBAAa,wBAAwB;AACrC,UAAM,WAAW,MAAM,QAA+B,aAAa;AAEnE,QAAI,SAAS,IAAI;AACb,YAAM,EAAE,WAAW,IAAI,SAAS;AAChC,UAAI,WAAW,WAAW,GAAG;AACzB,oBAAY,MAAM,sBAAsB;AACxC;AAAA,MACJ;AAEA,kBAAY,MAAM,SAAS,WAAW,MAAM,eAAe;AAE3D,iBAAW,MAAM,YAAY;AACzB,cAAM,GAAG,OAAO,IAAI,GAAG,GAAG,WAAW,GAAG,OAAO,KAAK,IAAK,GAAG,IAAI,EAAE;AAAA,MACtE;AAAA,IACJ,OAAO;AACH,kBAAY,OAAO,QAAQ;AAC3B,iBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AACzE,cAAQ,KAAK,CAAC;AAAA,IAClB;AAAA,EACJ,CAAC;AAEL,QAAM,UAAUA,SAAQ,QAAQ,SAAS,EAAE,YAAY,0BAA0B;AAEjF,UACK,QAAQ,MAAM,EACd,YAAY,iCAAiC,EAC7C,eAAe,iCAAiC,cAAc,EAC9D,OAAO,OAAO,YAAmC;AAC9C,UAAM,EAAE,WAAW,YAAY,IAAI;AAEnC,iBAAa,+BAA+B;AAC5C,UAAM,WAAW,MAAM,QAAyB,eAAe,WAAW,UAAU;AAEpF,QAAI,CAAC,SAAS,IAAI;AACd,kBAAY,OAAO,yBAAyB;AAC5C,iBAAW,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,CAAC,EAAE;AACzE,cAAQ,KAAK,CAAC;AAAA,IAClB;AAEA,UAAM,EAAE,SAAAC,SAAQ,IAAI,SAAS;AAC7B,QAAIA,SAAQ,WAAW,GAAG;AACtB,kBAAY,MAAM,mBAAmB;AACrC;AAAA,IACJ;AAEA,gBAAY,MAAM,SAASA,SAAQ,MAAM,YAAY;AAErD,eAAW,UAAUA,UAAS;AAC1B,YAAM,OAAO,OAAO,eAAe;AACnC,YAAM,GAAG,OAAO,IAAI,GAAG,OAAO,KAAK,GAAG,OAAO,KAAK,IAAK,IAAI,IAAK,OAAO,IAAI,EAAE;AAAA,IACjF;AAAA,EACJ,CAAC;AAEL,UACK,QAAQ,QAAQ,EAChB,YAAY,iCAAiC,EAC7C,eAAe,iCAAiC,cAAc,EAC9D,SAAS,YAAY,oCAAoC,EACzD,OAAO,OAAO,WAAmB,YAAmC;AACjE,UAAM,EAAE,WAAW,YAAY,IAAI;AAEnC,UAAM,SAAS,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACvE,QAAI,OAAO,WAAW,GAAG;AACrB,iBAAW,0BAA0B;AACrC,cAAQ,KAAK,CAAC;AAAA,IAClB;AAGA,UAAM,gBAAgB,OAAO,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAC3D,QAAI,cAAc,SAAS,GAAG;AAC1B,iBAAW,SAAS,eAAe;AAC/B,mBAAW,yBAAyB,KAAK,EAAE;AAAA,MAC/C;AACA,cAAQ,KAAK,CAAC;AAAA,IAClB;AAGA,iBAAa,+BAA+B;AAC5C,UAAM,kBAAkB,MAAM,QAAyB,eAAe,WAAW,UAAU;AAE3F,QAAI,CAAC,gBAAgB,IAAI;AACrB,kBAAY,OAAO,yBAAyB;AAC5C,iBAAW,UAAU,gBAAgB,MAAM,MAAM,KAAK,UAAU,gBAAgB,IAAI,CAAC,EAAE;AACvF,cAAQ,KAAK,CAAC;AAAA,IAClB;AAEA,gBAAY,MAAM,iBAAiB;AAEnC,UAAM,EAAE,SAAAA,SAAQ,IAAI,gBAAgB;AACpC,UAAM,gBAAgB,IAAI,IAAIA,SAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC;AAG5E,eAAW,SAAS,QAAQ;AACxB,YAAM,SAAS,cAAc,IAAI,MAAM,YAAY,CAAC;AACpD,UAAI,CAAC,QAAQ;AACT,cAAM,GAAG,OAAO,GAAG,SAAI,OAAO,KAAK,IAAI,KAAK,2BAA2B;AACvE;AAAA,MACJ;AAEA,YAAM,iBAAiB,MAAM,WAAW,eAAe,WAAW,YAAY,OAAO,MAAM,EAAE;AAE7F,UAAI,eAAe,IAAI;AACnB,cAAM,GAAG,OAAO,KAAK,SAAI,OAAO,KAAK,IAAI,KAAK,YAAY;AAAA,MAC9D,OAAO;AACH,cAAM,GAAG,OAAO,GAAG,SAAI,OAAO,KAAK,IAAI,KAAK,cAAc,KAAK,UAAU,eAAe,IAAI,CAAC,EAAE;AAAA,MACnG;AAAA,IACJ;AAAA,EACJ,CAAC;AACT;;;AP5IA,eAAe;AAAA,EACX,KAAK,EAAE,MAAM,gBAAgB,SAAS,QAAgB;AAC1D,CAAC,EAAE,OAAO;AAEV,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACK,KAAK,MAAM,EACX,YAAY,yBAAyB,EACrC,QAAQ,OAAe,EACvB,OAAO,MAAM;AACV,UAAQ,KAAK;AACjB,CAAC;AAEL,sBAAsB,OAAO;AAC7B,yBAAyB,OAAO;AAChC,oBAAoB,OAAO;AAE3B,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAiB;AACrD,UAAQ,MAAM,cAAc,GAAG;AAC/B,UAAQ,KAAK,CAAC;AAClB,CAAC;","names":["program","program","program","members"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/file.ts","../src/utils/http.ts","../src/utils/config.ts","../src/utils/output.ts","../src/utils/spinner.ts","../src/commands/whoami.ts","../src/commands/workspace.ts"],"sourcesContent":["import { Command } from 'commander'\nimport updateNotifier from 'update-notifier'\nimport { registerFileCommand } from './commands/file.js'\nimport { registerWhoamiCommand } from './commands/whoami.js'\nimport { registerWorkspaceCommand } from './commands/workspace.js'\n\ndeclare const __CLI_VERSION__: string\n\nupdateNotifier({\n pkg: { name: '@moxt-ai/cli', version: __CLI_VERSION__ },\n}).notify()\n\nconst program = new Command()\n\nprogram\n .name('moxt')\n .description('Moxt CLI - AI Workspace')\n .version(__CLI_VERSION__)\n .action(() => {\n program.help()\n })\n\nregisterWhoamiCommand(program)\nregisterWorkspaceCommand(program)\nregisterFileCommand(program)\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error('CLI Error:', err)\n process.exit(1)\n})\n","import * as fs from 'node:fs'\nimport { Command } from 'commander'\nimport { httpDelete, httpGet, httpPost, httpPut } from '../utils/http.js'\nimport { colors, print, printError } from '../utils/output.js'\nimport { startSpinner, stopSpinner } from '../utils/spinner.js'\n\ninterface FileEntry {\n name: string\n type: 'file' | 'directory'\n size: number | null\n}\n\ninterface FileReadResponse {\n path: string\n type: 'file' | 'directory'\n entries: FileEntry[] | null\n content: string | null\n}\n\ninterface FileWriteResponse {\n path: string\n created: boolean\n}\n\ninterface MkdirResponse {\n path: string\n created: boolean\n}\n\ninterface DeleteResponse {\n path: string\n}\n\ninterface FileUrlResponse {\n path: string\n url: string\n}\n\n/**\n * Detect if a Buffer contains binary content.\n * Text files must not contain control characters (except tab, newline, carriage return).\n */\nfunction isBinaryContent(buffer: Buffer): boolean {\n const sampleSize = Math.min(buffer.length, 8192)\n\n for (let i = 0; i < sampleSize; i++) {\n const byte = buffer[i]\n // control chars except tab=9, newline=10, carriage return=13\n if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {\n return true\n }\n }\n\n return false\n}\n\ninterface SpaceOptions {\n workspace: string\n space?: string\n personal?: boolean\n teamSpaceId?: string\n teammateId?: string\n}\n\ninterface SpaceParams {\n space?: string\n teamSpaceId?: string\n agentId?: number\n}\n\nfunction validateSpaceOptions(options: SpaceOptions): SpaceParams {\n const mutuallyExclusive = [options.space, options.personal, options.teamSpaceId, options.teammateId].filter(\n Boolean,\n ).length\n if (mutuallyExclusive > 1) {\n printError('Error: -s, --personal, --team-space-id, and --teammate-id are mutually exclusive.')\n process.exit(1)\n }\n\n if (options.teamSpaceId) {\n return { teamSpaceId: options.teamSpaceId }\n }\n if (options.teammateId) {\n const parsed = Number.parseInt(options.teammateId, 10)\n if (!Number.isInteger(parsed) || parsed <= 0) {\n printError(`Error: --teammate-id must be a positive integer, got \"${options.teammateId}\".`)\n process.exit(1)\n }\n return { agentId: parsed }\n }\n if (options.personal) {\n return { space: 'personal' }\n }\n if (options.space) {\n return { space: options.space }\n }\n // Default: personal space\n return {}\n}\n\nfunction buildFileQueryString(path: string | undefined, spaceParams: SpaceParams): string {\n const params = new URLSearchParams()\n if (path) {\n params.set('path', path)\n }\n if (spaceParams.space) {\n params.set('space', spaceParams.space)\n }\n if (spaceParams.teamSpaceId) {\n params.set('teamSpaceId', spaceParams.teamSpaceId)\n }\n if (spaceParams.agentId != null) {\n params.set('agentId', String(spaceParams.agentId))\n }\n const qs = params.toString()\n return qs ? `?${qs}` : ''\n}\n\nfunction addSpaceOptions(cmd: Command): Command {\n return cmd\n .requiredOption('-w, --workspace <workspaceId>', 'Workspace ID')\n .option('-s, --space <name>', 'Space name (personal or teamspace name)')\n .option('--personal', 'Use personal space (default)')\n .option('--team-space-id <teamSpaceId>', 'Team space ID')\n .option('--teammate-id <teammateId>', 'Teammate ID (teammate space)')\n}\n\nexport function registerFileCommand(program: Command): void {\n const file = program.command('file').description('File operations')\n\n addSpaceOptions(\n file.command('get-url')\n .description('Resolve the shareable browser URL for a file')\n .requiredOption('-p, --path <path>', 'File path'),\n ).action(async (options: SpaceOptions & { path: string }) => {\n const spaceParams = validateSpaceOptions(options)\n const qs = buildFileQueryString(options.path, spaceParams)\n\n startSpinner('Resolving URL...')\n const response = await httpGet<FileUrlResponse>(\n `/workspaces/${options.workspace}/files/url${qs}`,\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ ${options.path} does not exist${colors.reset}`)\n } else if (response.status === 400) {\n printError(`Error: ${JSON.stringify(response.data)}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n stopSpinner(true, response.data.path)\n print(`${colors.cyan}${response.data.url}${colors.reset}`)\n })\n\n addSpaceOptions(\n file.command('list')\n .description('List directory contents')\n .option('-p, --path <path>', 'Directory path', '/'),\n ).action(async (options: SpaceOptions & { path: string }) => {\n const spaceParams = validateSpaceOptions(options)\n const qs = buildFileQueryString(options.path, spaceParams)\n\n startSpinner('Listing files...')\n const response = await httpGet<FileReadResponse>(\n `/workspaces/${options.workspace}/files/list${qs}`,\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ ${options.path} does not exist${colors.reset}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const { path, entries } = response.data\n\n if (!entries || entries.length === 0) {\n stopSpinner(true, `${path}: empty directory`)\n return\n }\n\n stopSpinner(true, path)\n for (const entry of entries) {\n if (entry.type === 'directory') {\n print(`${colors.blue}${entry.name}/${colors.reset}`)\n } else {\n print(entry.name)\n }\n }\n })\n\n file.command('read')\n .description('Read file content')\n .option('-w, --workspace <workspaceId>', 'Workspace ID')\n .option('-s, --space <name>', 'Space name (personal or teamspace name)')\n .option('--personal', 'Use personal space (default)')\n .option('--team-space-id <teamSpaceId>', 'Team space ID')\n .option('--teammate-id <teammateId>', 'Teammate ID (teammate space)')\n .option('-p, --path <path>', 'File path')\n .option('-u, --url <url>', 'Moxt file URL (e.g., https://moxt.ai/w/{workspaceId}/{fileId})')\n .action(async (options: Partial<SpaceOptions> & { path?: string; url?: string }) => {\n // Validate mutually exclusive options: -u vs -w\n if (options.url && options.workspace) {\n printError('Error: --url cannot be used with --workspace')\n process.exit(1)\n }\n if (options.url && options.path) {\n printError('Error: --url cannot be used with --path')\n process.exit(1)\n }\n if (!options.url && !options.workspace) {\n printError('Error: Either --url or --workspace is required')\n process.exit(1)\n }\n if (!options.url && !options.path) {\n printError('Error: --path is required when not using --url')\n process.exit(1)\n }\n\n startSpinner('Reading file...')\n\n let response: { ok: boolean; status: number; data: FileReadResponse }\n let displayPath: string\n\n if (options.url) {\n displayPath = options.url\n response = await httpGet<FileReadResponse>(\n `/files/read-by-url?url=${encodeURIComponent(options.url)}`,\n )\n } else {\n displayPath = options.path!\n const spaceParams = validateSpaceOptions(options as SpaceOptions)\n const qs = buildFileQueryString(options.path, spaceParams)\n response = await httpGet<FileReadResponse>(\n `/workspaces/${options.workspace}/files/read${qs}`,\n )\n }\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ ${displayPath} does not exist${colors.reset}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const { type, content } = response.data\n\n if (type === 'directory') {\n stopSpinner(false, 'Failed')\n print(`${colors.red}☠ ${displayPath} is a directory${colors.reset}`)\n process.exit(1)\n }\n\n stopSpinner(true, 'Done')\n print(content ?? '')\n })\n\n addSpaceOptions(\n file.command('put')\n .description('Upload a file')\n .requiredOption('-p, --path <path>', 'Remote file path')\n .requiredOption('-l, --local-path <localPath>', 'Local file path')\n .option('-r, --recursive', 'Create parent directories if needed'),\n ).action(\n async (options: SpaceOptions & {\n path: string\n localPath: string\n recursive?: boolean\n }) => {\n const spaceParams = validateSpaceOptions(options)\n\n // check local file exists\n if (!fs.existsSync(options.localPath)) {\n print(`${colors.red}☠ Local file not found: ${options.localPath}${colors.reset}`)\n process.exit(1)\n }\n\n // check it is a file, not a directory\n const stats = fs.statSync(options.localPath)\n if (!stats.isFile()) {\n print(`${colors.red}☠ Not a file: ${options.localPath}${colors.reset}`)\n process.exit(1)\n }\n\n // check file size (10MB limit)\n const maxSize = 10 * 1024 * 1024\n if (stats.size > maxSize) {\n print(`${colors.red}☠ File too large (max 10MB): ${options.localPath}${colors.reset}`)\n process.exit(1)\n }\n\n // read file and check for binary content\n const buffer = fs.readFileSync(options.localPath)\n if (isBinaryContent(buffer)) {\n print(`${colors.red}☠ Binary files are not allowed. Only text files can be uploaded.${colors.reset}`)\n process.exit(1)\n }\n\n const content = buffer.toString('utf8')\n\n startSpinner('Uploading...')\n const response = await httpPut<FileWriteResponse>(\n `/workspaces/${options.workspace}/files/write`,\n {\n path: options.path,\n content,\n recursive: options.recursive ?? false,\n ...spaceParams,\n },\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ Parent directory does not exist${colors.reset}`)\n } else if (response.status === 400) {\n printError(`Error: ${JSON.stringify(response.data)}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const action = response.data.created ? 'Created' : 'Updated'\n stopSpinner(true, `${action}: ${response.data.path}`)\n },\n )\n\n addSpaceOptions(\n file.command('mkdir')\n .description('Create a directory')\n .requiredOption('-p, --path <path>', 'Directory path')\n .option('-r, --recursive', 'Create parent directories if needed'),\n ).action(\n async (options: SpaceOptions & {\n path: string\n recursive?: boolean\n }) => {\n const spaceParams = validateSpaceOptions(options)\n\n startSpinner('Creating directory...')\n const response = await httpPost<MkdirResponse>(\n `/workspaces/${options.workspace}/files/mkdir`,\n {\n path: options.path,\n recursive: options.recursive ?? false,\n ...spaceParams,\n },\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ Parent directory does not exist${colors.reset}`)\n } else if (response.status === 400) {\n printError(`Error: ${JSON.stringify(response.data)}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n const action = response.data.created ? 'Created' : 'Already exists'\n stopSpinner(true, `${action}: ${response.data.path}`)\n },\n )\n\n addSpaceOptions(\n file.command('del')\n .description('Delete a file or empty directory')\n .requiredOption('-p, --path <path>', 'File or directory path'),\n ).action(\n async (options: SpaceOptions & {\n path: string\n }) => {\n const spaceParams = validateSpaceOptions(options)\n\n startSpinner('Deleting...')\n const response = await httpDelete<DeleteResponse>(\n `/workspaces/${options.workspace}/files/delete`,\n {\n path: options.path,\n ...spaceParams,\n },\n )\n\n if (!response.ok) {\n stopSpinner(false, 'Failed')\n if (response.status === 404) {\n print(`${colors.red}☠ Path not found: ${options.path}${colors.reset}`)\n } else if (response.status === 400) {\n print(`${colors.red}☠ Cannot delete non-empty directory${colors.reset}`)\n } else {\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n }\n process.exit(1)\n }\n\n stopSpinner(true, `Deleted: ${response.data.path}`)\n },\n )\n}\n","import { arch, platform } from 'os'\nimport { getApiBaseUrl, getApiKey } from './config.js'\n\ndeclare const __CLI_VERSION__: string\n\nexport interface ApiResponse<T> {\n ok: boolean\n status: number\n data: T\n}\n\nfunction getUserAgent(): string {\n return `moxt-cli/${__CLI_VERSION__} (${platform()}/${arch()}) node/${process.versions.node}`\n}\n\nasync function request<T>(method: string, path: string, body?: unknown): Promise<ApiResponse<T>> {\n const apiKey = getApiKey()\n const url = `${getApiBaseUrl()}${path}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n 'User-Agent': getUserAgent(),\n },\n body: body ? JSON.stringify(body) : undefined,\n })\n\n let data: T\n const contentType = response.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n data = (await response.json()) as T\n } else {\n data = (await response.text()) as T\n }\n\n if (response.status === 401) {\n console.error('Authentication failed. Check your MOXT_API_KEY.')\n console.error('Get a new key at: https://moxt.ai')\n process.exit(1)\n }\n\n return {\n ok: response.ok,\n status: response.status,\n data,\n }\n}\n\nexport async function httpGet<T>(path: string): Promise<ApiResponse<T>> {\n return request<T>('GET', path)\n}\n\nexport async function httpPut<T>(path: string, body: unknown): Promise<ApiResponse<T>> {\n return request<T>('PUT', path, body)\n}\n\nexport async function httpPost<T>(path: string, body: unknown): Promise<ApiResponse<T>> {\n return request<T>('POST', path, body)\n}\n\nexport async function httpDelete<T>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>('DELETE', path, body)\n}\n","const DEFAULT_HOST = 'api.moxt.ai'\n\nexport function getApiBaseUrl(): string {\n const host = process.env.MOXT_HOST ?? DEFAULT_HOST\n return `https://${host}/openapi/v1`\n}\n\nexport function getApiKey(): string {\n const apiKey = process.env.MOXT_API_KEY\n\n if (!apiKey) {\n console.error('MOXT_API_KEY environment variable is not set.')\n console.error('')\n console.error('Get your API key at: https://moxt.ai')\n console.error('')\n console.error('Then set it:')\n console.error(' export MOXT_API_KEY=moxt_...')\n process.exit(1)\n }\n\n return apiKey\n}\n","// ANSI colors\nexport const colors = {\n bold: '\\x1b[1m',\n blue: '\\x1b[1;34m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n reset: '\\x1b[0m',\n}\n\nexport function print(message: string): void {\n console.log(message)\n}\n\nexport function printError(message: string): void {\n console.error(`${colors.red}${message}${colors.reset}`)\n}\n","const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']\n\nlet intervalId: ReturnType<typeof setInterval> | null = null\nlet frameIndex = 0\n\nexport function startSpinner(message: string): void {\n frameIndex = 0\n process.stdout.write(`${frames[frameIndex]} ${message}`)\n\n intervalId = setInterval(() => {\n frameIndex = (frameIndex + 1) % frames.length\n process.stdout.write(`\\r${frames[frameIndex]} ${message}`)\n }, 80)\n}\n\nexport function stopSpinner(success: boolean, message?: string): void {\n if (intervalId) {\n clearInterval(intervalId)\n intervalId = null\n }\n\n const symbol = success ? '\\x1b[32m✓\\x1b[0m' : '\\x1b[31m✗\\x1b[0m'\n // \\x1b[2K 清除整行,\\r 回到行首\n process.stdout.write(`\\x1b[2K\\r${symbol} ${message ?? ''}\\n`)\n}\n","import { Command } from 'commander'\nimport { httpGet } from '../utils/http.js'\nimport { colors, print, printError } from '../utils/output.js'\nimport { startSpinner, stopSpinner } from '../utils/spinner.js'\n\ninterface WhoamiResponse {\n nickname: string\n email: string\n}\n\nexport function registerWhoamiCommand(program: Command): void {\n program\n .command('whoami')\n .description('Show current user information')\n .action(async () => {\n startSpinner('Fetching user info...')\n const response = await httpGet<WhoamiResponse>('/users/whoami')\n\n if (response.ok) {\n stopSpinner(true, 'Done')\n print(`${colors.bold}Nickname${colors.reset}: ${response.data.nickname}`)\n print(`${colors.bold}Email${colors.reset}: ${response.data.email}`)\n } else {\n stopSpinner(false, 'Failed')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n })\n}\n","import { Command } from 'commander'\nimport { httpGet, httpDelete } from '../utils/http.js'\nimport { colors, print, printError } from '../utils/output.js'\nimport { startSpinner, stopSpinner } from '../utils/spinner.js'\n\ninterface WorkspaceItem {\n workspaceId: string\n name: string\n}\n\ninterface WorkspaceListResponse {\n workspaces: WorkspaceItem[]\n}\n\ninterface MemberItem {\n userId: number\n email: string\n displayName: string | null\n role: string\n}\n\ninterface MembersResponse {\n members: MemberItem[]\n}\n\ninterface SpaceItem {\n type: 'personal' | 'team' | 'teammate'\n name: string\n teamSpaceId: string | null\n agentId: number | null\n}\n\n// Email regex (same as web/src/utils/email.ts)\nconst emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/\n\nfunction isValidEmail(email: string): boolean {\n return emailRegex.test(email)\n}\n\nexport function registerWorkspaceCommand(program: Command): void {\n const workspace = program.command('workspace').description('Workspace management')\n\n workspace\n .command('list')\n .description('List all workspaces you belong to')\n .action(async () => {\n startSpinner('Fetching workspaces...')\n const response = await httpGet<WorkspaceListResponse>('/workspaces')\n\n if (response.ok) {\n const { workspaces } = response.data\n if (workspaces.length === 0) {\n stopSpinner(true, 'No workspaces found.')\n return\n }\n\n stopSpinner(true, `Found ${workspaces.length} workspace(s)`)\n\n for (const ws of workspaces) {\n print(`${colors.bold}${ws.workspaceId}${colors.reset}\\t${ws.name}`)\n }\n } else {\n stopSpinner(false, 'Failed')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n })\n\n const members = program.command('members').description('Manage workspace members')\n\n members\n .command('list')\n .description('List all members in a workspace')\n .requiredOption('-w, --workspace <workspaceId>', 'Workspace ID')\n .action(async (options: { workspace: string }) => {\n const { workspace: workspaceId } = options\n\n startSpinner('Fetching workspace members...')\n const response = await httpGet<MembersResponse>(`/workspaces/${workspaceId}/members`)\n\n if (!response.ok) {\n stopSpinner(false, 'Failed to fetch members')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n\n const { members } = response.data\n if (members.length === 0) {\n stopSpinner(true, 'No members found.')\n return\n }\n\n stopSpinner(true, `Found ${members.length} member(s)`)\n\n for (const member of members) {\n const name = member.displayName || '-'\n print(`${colors.bold}${member.email}${colors.reset}\\t${name}\\t${member.role}`)\n }\n })\n\n members\n .command('remove')\n .description('Remove members from a workspace')\n .requiredOption('-w, --workspace <workspaceId>', 'Workspace ID')\n .argument('<emails>', 'Emails to remove (comma-separated)')\n .action(async (emailsArg: string, options: { workspace: string }) => {\n const { workspace: workspaceId } = options\n\n const emails = emailsArg.split(',').map((e) => e.trim()).filter(Boolean)\n if (emails.length === 0) {\n printError('No valid emails provided')\n process.exit(1)\n }\n\n // Validate email format\n const invalidEmails = emails.filter((e) => !isValidEmail(e))\n if (invalidEmails.length > 0) {\n for (const email of invalidEmails) {\n printError(`Invalid email format: ${email}`)\n }\n process.exit(1)\n }\n\n // Fetch members to get userId by email\n startSpinner('Fetching workspace members...')\n const membersResponse = await httpGet<MembersResponse>(`/workspaces/${workspaceId}/members`)\n\n if (!membersResponse.ok) {\n stopSpinner(false, 'Failed to fetch members')\n printError(`Error [${membersResponse.status}]: ${JSON.stringify(membersResponse.data)}`)\n process.exit(1)\n }\n\n stopSpinner(true, 'Members fetched')\n\n const { members } = membersResponse.data\n const emailToMember = new Map(members.map((m) => [m.email.toLowerCase(), m]))\n\n // Remove each member\n for (const email of emails) {\n const member = emailToMember.get(email.toLowerCase())\n if (!member) {\n print(`${colors.red}✗${colors.reset} ${email} - not found in workspace`)\n continue\n }\n\n const deleteResponse = await httpDelete(`/workspaces/${workspaceId}/members/${member.userId}`)\n\n if (deleteResponse.ok) {\n print(`${colors.green}✓${colors.reset} ${email} - removed`)\n } else {\n print(`${colors.red}✗${colors.reset} ${email} - failed: ${JSON.stringify(deleteResponse.data)}`)\n }\n }\n })\n\n const space = program.command('space').description('Manage spaces')\n\n space\n .command('list')\n .description('List accessible spaces in a workspace')\n .requiredOption('-w, --workspace <workspaceId>', 'Workspace ID')\n .action(async (options: { workspace: string }) => {\n const { workspace: workspaceId } = options\n\n startSpinner('Fetching spaces...')\n const response = await httpGet<{ spaces: SpaceItem[] }>(`/workspaces/${workspaceId}/spaces`)\n\n if (!response.ok) {\n stopSpinner(false, 'Failed to fetch spaces')\n printError(`Error [${response.status}]: ${JSON.stringify(response.data)}`)\n process.exit(1)\n }\n\n const { spaces } = response.data\n if (spaces.length === 0) {\n stopSpinner(true, 'No spaces found.')\n return\n }\n\n stopSpinner(true, `Found ${spaces.length} space(s)`)\n\n print(\n `${colors.bold}TYPE${colors.reset}\\t${colors.bold}TEAM_SPACE_ID${colors.reset}\\t${colors.bold}TEAMMATE_ID${colors.reset}\\t${colors.bold}NAME${colors.reset}`,\n )\n for (const s of spaces) {\n const teamSpaceId = s.teamSpaceId ?? ''\n const teammateId = s.agentId != null ? String(s.agentId) : ''\n print(`${s.type}\\t${teamSpaceId}\\t${teammateId}\\t${s.name}`)\n }\n })\n\n}\n"],"mappings":"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","names":["program","program","program","members"]}
|