@moxt-ai/cli 0.3.3-beta.0 → 0.3.3-beta.2
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 +7 -0
- package/dist/index.js +436 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -193,6 +193,11 @@ moxt workflow task assign -w <workspace-id> <workflow-file-id> <task-id> --teamm
|
|
|
193
193
|
moxt workflow task assign -w <workspace-id> <workflow-file-id> <task-id> --clear
|
|
194
194
|
moxt workflow task delete -w <workspace-id> <workflow-file-id> <task-id>
|
|
195
195
|
|
|
196
|
+
# Manage workflow task comments
|
|
197
|
+
moxt workflow comment create -w <workspace-id> <workflow-file-id> <task-id> --content-path <local-path>
|
|
198
|
+
moxt workflow comment list -w <workspace-id> <workflow-file-id> <task-id>
|
|
199
|
+
moxt workflow comment delete -w <workspace-id> <workflow-file-id> <task-id> <comment-id>
|
|
200
|
+
|
|
196
201
|
# Inspect or abort workflow agent runs
|
|
197
202
|
moxt workflow run status -w <workspace-id> <workflow-file-id> <trigger-id> [trigger-id...]
|
|
198
203
|
moxt workflow run output -w <workspace-id> <workflow-file-id> <trigger-id>
|
|
@@ -201,6 +206,8 @@ moxt workflow run abort -w <workspace-id> <workflow-file-id> <trigger-id>
|
|
|
201
206
|
|
|
202
207
|
Workflow, status, and task descriptions are read from local UTF-8 text files with `--desc-file`.
|
|
203
208
|
Inline `--desc` is not supported.
|
|
209
|
+
Workflow task comment content is read only from a local UTF-8 text file with `--content-path` and is limited to
|
|
210
|
+
5,000 characters. Inline `--content` is not supported.
|
|
204
211
|
Workflow status type values are `1=todo`, `2=in-progress`, and `3=done`.
|
|
205
212
|
|
|
206
213
|
#### File command options
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,157 @@ import updateNotifier from "update-notifier";
|
|
|
6
6
|
// src/program.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
|
|
9
|
+
// src/utils/output.ts
|
|
10
|
+
var colors = {
|
|
11
|
+
bold: "\x1B[1m",
|
|
12
|
+
blue: "\x1B[1;34m",
|
|
13
|
+
green: "\x1B[32m",
|
|
14
|
+
red: "\x1B[31m",
|
|
15
|
+
cyan: "\x1B[36m",
|
|
16
|
+
dim: "\x1B[2m",
|
|
17
|
+
reset: "\x1B[0m"
|
|
18
|
+
};
|
|
19
|
+
function print(message) {
|
|
20
|
+
console.log(message);
|
|
21
|
+
}
|
|
22
|
+
function printError(message) {
|
|
23
|
+
console.error(`${colors.red}${message}${colors.reset}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/utils/runtime-context.ts
|
|
27
|
+
var DEFAULT_USER_HOST = "api.moxt.ai";
|
|
28
|
+
var SANDBOX_ENV_KEYS = ["BASE_API_HOST", "SANDBOX_TOOL_API_TOKEN", "MOXT_REPO_PAYLOAD", "MOXT_WORKSPACE_ID"];
|
|
29
|
+
var RuntimeContextError = class extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "RuntimeContextError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
function resolveRuntimeContext(env = process.env) {
|
|
36
|
+
if (hasAnySandboxEnv(env)) {
|
|
37
|
+
return resolveSandboxRuntimeContext(env);
|
|
38
|
+
}
|
|
39
|
+
const apiKey = readNonEmptyEnv(env, "MOXT_API_KEY");
|
|
40
|
+
if (apiKey) {
|
|
41
|
+
return {
|
|
42
|
+
mode: "user",
|
|
43
|
+
host: readNonEmptyEnv(env, "MOXT_HOST") ?? DEFAULT_USER_HOST,
|
|
44
|
+
apiKey
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
throw new RuntimeContextError("Missing auth. Set sandbox environment variables or MOXT_API_KEY.");
|
|
48
|
+
}
|
|
49
|
+
function resolveSandboxRuntimeContext(env = process.env) {
|
|
50
|
+
const missingKeys = SANDBOX_ENV_KEYS.filter((key) => !readNonEmptyEnv(env, key));
|
|
51
|
+
if (missingKeys.length > 0) {
|
|
52
|
+
throw new RuntimeContextError(`Incomplete sandbox environment. Missing: ${missingKeys.join(", ")}.`);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
mode: "sandbox",
|
|
56
|
+
baseApiHost: normalizeBaseApiHost(readRequiredEnv(env, "BASE_API_HOST")),
|
|
57
|
+
sandboxToolApiToken: readRequiredEnv(env, "SANDBOX_TOOL_API_TOKEN"),
|
|
58
|
+
workspaceId: readRequiredEnv(env, "MOXT_WORKSPACE_ID"),
|
|
59
|
+
repoPayload: parseRepoPayload(readRequiredEnv(env, "MOXT_REPO_PAYLOAD"))
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function parseRepoPayload(raw) {
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = JSON.parse(raw);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new RuntimeContextError(`MOXT_REPO_PAYLOAD must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
68
|
+
}
|
|
69
|
+
if (!isRecord(parsed)) {
|
|
70
|
+
throw new RuntimeContextError("MOXT_REPO_PAYLOAD must be a JSON object.");
|
|
71
|
+
}
|
|
72
|
+
const personal = parseRepoEntry(parsed.personal, "personal");
|
|
73
|
+
const teamsValue = parsed.teams;
|
|
74
|
+
if (!Array.isArray(teamsValue)) {
|
|
75
|
+
throw new RuntimeContextError("MOXT_REPO_PAYLOAD.teams must be an array.");
|
|
76
|
+
}
|
|
77
|
+
const teams = teamsValue.map((entry, index) => parseRepoEntry(entry, `teams[${index}]`));
|
|
78
|
+
const workspace = parsed.workspace == null ? null : parseRepoEntry(parsed.workspace, "workspace");
|
|
79
|
+
return { personal, teams, workspace };
|
|
80
|
+
}
|
|
81
|
+
function normalizeBaseApiHost(value) {
|
|
82
|
+
const trimmed = value.trim();
|
|
83
|
+
if (!trimmed) {
|
|
84
|
+
throw new RuntimeContextError("BASE_API_HOST must not be empty.");
|
|
85
|
+
}
|
|
86
|
+
return trimmed.replace(/\/+$/, "");
|
|
87
|
+
}
|
|
88
|
+
function hasAnySandboxEnv(env) {
|
|
89
|
+
return SANDBOX_ENV_KEYS.some((key) => readNonEmptyEnv(env, key) !== void 0);
|
|
90
|
+
}
|
|
91
|
+
function readRequiredEnv(env, key) {
|
|
92
|
+
const value = readNonEmptyEnv(env, key);
|
|
93
|
+
if (!value) {
|
|
94
|
+
throw new RuntimeContextError(`${key} is required.`);
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function readNonEmptyEnv(env, key) {
|
|
99
|
+
const value = env[key];
|
|
100
|
+
if (typeof value !== "string") return void 0;
|
|
101
|
+
const trimmed = value.trim();
|
|
102
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
103
|
+
}
|
|
104
|
+
function parseRepoEntry(value, path2) {
|
|
105
|
+
if (!isRecord(value)) {
|
|
106
|
+
throw new RuntimeContextError(`MOXT_REPO_PAYLOAD.${path2} must be an object.`);
|
|
107
|
+
}
|
|
108
|
+
const repoId = readStringField(value, "repoId", path2);
|
|
109
|
+
const name = readStringField(value, "name", path2);
|
|
110
|
+
return { repoId, name };
|
|
111
|
+
}
|
|
112
|
+
function readStringField(record2, field, path2) {
|
|
113
|
+
const value = record2[field];
|
|
114
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
115
|
+
throw new RuntimeContextError(`MOXT_REPO_PAYLOAD.${path2}.${field} must be a non-empty string.`);
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
function isRecord(value) {
|
|
120
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/utils/sandbox-path.ts
|
|
124
|
+
function listRepoEntries(repoPayload) {
|
|
125
|
+
return [
|
|
126
|
+
repoPayload.personal,
|
|
127
|
+
...repoPayload.teams,
|
|
128
|
+
...repoPayload.workspace ? [repoPayload.workspace] : []
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/commands/auth.ts
|
|
133
|
+
function registerAuthCommand(program2) {
|
|
134
|
+
const auth = program2.command("auth").description("Authentication utilities");
|
|
135
|
+
auth.command("status").description("Show CLI authentication mode").action(() => {
|
|
136
|
+
try {
|
|
137
|
+
const context = resolveRuntimeContext();
|
|
138
|
+
if (context.mode === "sandbox") {
|
|
139
|
+
print(`${colors.bold}Mode${colors.reset}: sandbox`);
|
|
140
|
+
print(`${colors.bold}Workspace${colors.reset}: ${context.workspaceId}`);
|
|
141
|
+
print(`${colors.bold}Base API${colors.reset}: ${context.baseApiHost}`);
|
|
142
|
+
print(`${colors.bold}Repos${colors.reset}:`);
|
|
143
|
+
for (const repo of listRepoEntries(context.repoPayload)) {
|
|
144
|
+
print(` ${repo.name} ${repo.repoId}`);
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
print(`${colors.bold}Mode${colors.reset}: user`);
|
|
149
|
+
print(`${colors.bold}Host${colors.reset}: ${context.host}`);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
if (error instanceof RuntimeContextError) {
|
|
152
|
+
printError(error.message);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
9
160
|
// src/commands/file.ts
|
|
10
161
|
import * as fs from "fs";
|
|
11
162
|
|
|
@@ -149,7 +300,7 @@ function getApiKeyOrUndefined() {
|
|
|
149
300
|
|
|
150
301
|
// src/utils/http.ts
|
|
151
302
|
function getUserAgent() {
|
|
152
|
-
return `moxt-cli/${"0.3.3-beta.
|
|
303
|
+
return `moxt-cli/${"0.3.3-beta.2"} (${platform()}/${arch()}) node/${process.versions.node}`;
|
|
153
304
|
}
|
|
154
305
|
async function fetchApi(method, path2, body) {
|
|
155
306
|
const apiKey = getApiKey();
|
|
@@ -205,23 +356,6 @@ async function httpRaw(method, path2, body) {
|
|
|
205
356
|
};
|
|
206
357
|
}
|
|
207
358
|
|
|
208
|
-
// src/utils/output.ts
|
|
209
|
-
var colors = {
|
|
210
|
-
bold: "\x1B[1m",
|
|
211
|
-
blue: "\x1B[1;34m",
|
|
212
|
-
green: "\x1B[32m",
|
|
213
|
-
red: "\x1B[31m",
|
|
214
|
-
cyan: "\x1B[36m",
|
|
215
|
-
dim: "\x1B[2m",
|
|
216
|
-
reset: "\x1B[0m"
|
|
217
|
-
};
|
|
218
|
-
function print(message) {
|
|
219
|
-
console.log(message);
|
|
220
|
-
}
|
|
221
|
-
function printError(message) {
|
|
222
|
-
console.error(`${colors.red}${message}${colors.reset}`);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
359
|
// src/utils/search-options.ts
|
|
226
360
|
var SearchOptionsError = class extends Error {
|
|
227
361
|
constructor(message) {
|
|
@@ -946,7 +1080,10 @@ function registerWorkspaceCommand(program2) {
|
|
|
946
1080
|
|
|
947
1081
|
// src/commands/workflow.ts
|
|
948
1082
|
import * as fs3 from "fs";
|
|
1083
|
+
import { TextDecoder } from "util";
|
|
949
1084
|
var MAX_DESC_LENGTH = 5e3;
|
|
1085
|
+
var MAX_COMMENT_CONTENT_LENGTH = 5e3;
|
|
1086
|
+
var MAX_COMMENT_CONTENT_BYTES = MAX_COMMENT_CONTENT_LENGTH * 4;
|
|
950
1087
|
var WorkflowOptionsError = class extends Error {
|
|
951
1088
|
constructor(message) {
|
|
952
1089
|
super(message);
|
|
@@ -1052,6 +1189,42 @@ function readDescFile(path2) {
|
|
|
1052
1189
|
}
|
|
1053
1190
|
return content;
|
|
1054
1191
|
}
|
|
1192
|
+
function readCommentContentFile(path2) {
|
|
1193
|
+
if (!fs3.existsSync(path2)) {
|
|
1194
|
+
throw new WorkflowOptionsError(`Error: comment content file not found: ${path2}`);
|
|
1195
|
+
}
|
|
1196
|
+
const stat = fs3.statSync(path2);
|
|
1197
|
+
if (!stat.isFile()) {
|
|
1198
|
+
throw new WorkflowOptionsError(`Error: comment content path is not a file: ${path2}`);
|
|
1199
|
+
}
|
|
1200
|
+
if (stat.size > MAX_COMMENT_CONTENT_BYTES) {
|
|
1201
|
+
throw new WorkflowOptionsError(
|
|
1202
|
+
`Error: comment content file must be at most ${MAX_COMMENT_CONTENT_LENGTH} characters.`
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
const buffer2 = fs3.readFileSync(path2);
|
|
1206
|
+
if (buffer2.includes(0)) {
|
|
1207
|
+
throw new WorkflowOptionsError("Error: comment content file must not contain NUL bytes.");
|
|
1208
|
+
}
|
|
1209
|
+
if (isBinaryContent(buffer2)) {
|
|
1210
|
+
throw new WorkflowOptionsError("Error: comment content file must be UTF-8 text, not binary content.");
|
|
1211
|
+
}
|
|
1212
|
+
let content;
|
|
1213
|
+
try {
|
|
1214
|
+
content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(buffer2);
|
|
1215
|
+
} catch {
|
|
1216
|
+
throw new WorkflowOptionsError("Error: comment content file must contain valid UTF-8 text.");
|
|
1217
|
+
}
|
|
1218
|
+
if (content.length === 0) {
|
|
1219
|
+
throw new WorkflowOptionsError("Error: comment content file must not be empty.");
|
|
1220
|
+
}
|
|
1221
|
+
if (content.length > MAX_COMMENT_CONTENT_LENGTH) {
|
|
1222
|
+
throw new WorkflowOptionsError(
|
|
1223
|
+
`Error: comment content file must be at most ${MAX_COMMENT_CONTENT_LENGTH} characters.`
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
return content;
|
|
1227
|
+
}
|
|
1055
1228
|
function parseAssigneeBody(options, allowClear) {
|
|
1056
1229
|
const hasClear = options.clear === true;
|
|
1057
1230
|
const humanId = parseOptionalPositiveInteger(options.humanId, "--human-id");
|
|
@@ -1111,10 +1284,34 @@ function failResponse(response) {
|
|
|
1111
1284
|
process.exit(1);
|
|
1112
1285
|
}
|
|
1113
1286
|
function priorityLabel(value) {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1287
|
+
switch (value) {
|
|
1288
|
+
case 0:
|
|
1289
|
+
return "urgent";
|
|
1290
|
+
case 1:
|
|
1291
|
+
return "high";
|
|
1292
|
+
case 2:
|
|
1293
|
+
return "medium";
|
|
1294
|
+
case 3:
|
|
1295
|
+
return "low";
|
|
1296
|
+
default: {
|
|
1297
|
+
const unsupportedValue = value;
|
|
1298
|
+
throw new Error(`Unsupported workflow task priority: ${String(unsupportedValue)}`);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function statusTypeLabel(value) {
|
|
1303
|
+
switch (value) {
|
|
1304
|
+
case 1:
|
|
1305
|
+
return "todo";
|
|
1306
|
+
case 2:
|
|
1307
|
+
return "in-progress";
|
|
1308
|
+
case 3:
|
|
1309
|
+
return "done";
|
|
1310
|
+
default: {
|
|
1311
|
+
const unsupportedValue = value;
|
|
1312
|
+
throw new Error(`Unsupported workflow status type: ${String(unsupportedValue)}`);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1118
1315
|
}
|
|
1119
1316
|
function formatAssignee(assignee) {
|
|
1120
1317
|
if (!assignee) return "-";
|
|
@@ -1126,17 +1323,149 @@ function formatAssignee(assignee) {
|
|
|
1126
1323
|
function printWorkflow(item) {
|
|
1127
1324
|
print(`${colors.bold}${item.fileId}${colors.reset} owner=${item.ownerHumanId ?? "-"} desc=${item.desc ?? "-"}`);
|
|
1128
1325
|
}
|
|
1129
|
-
function
|
|
1130
|
-
|
|
1131
|
-
|
|
1326
|
+
function indentation(size) {
|
|
1327
|
+
return " ".repeat(size);
|
|
1328
|
+
}
|
|
1329
|
+
function escapeTerminalControlCharacters(value) {
|
|
1330
|
+
return value.replace(/\r\n/g, "\n").replace(
|
|
1331
|
+
/[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g,
|
|
1332
|
+
(character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`
|
|
1132
1333
|
);
|
|
1133
1334
|
}
|
|
1134
|
-
function
|
|
1335
|
+
function formatDetailScalar(value) {
|
|
1336
|
+
if (value === null) return "null";
|
|
1337
|
+
if (typeof value === "string") {
|
|
1338
|
+
const escaped = escapeTerminalControlCharacters(value);
|
|
1339
|
+
if (escaped === "") return '""';
|
|
1340
|
+
if (/\n/.test(escaped)) return JSON.stringify(escaped);
|
|
1341
|
+
return escaped;
|
|
1342
|
+
}
|
|
1343
|
+
return String(value);
|
|
1344
|
+
}
|
|
1345
|
+
function printDetailField(name, value, indent = 0) {
|
|
1346
|
+
print(`${indentation(indent)}${name}: ${formatDetailScalar(value)}`);
|
|
1347
|
+
}
|
|
1348
|
+
function printDetailTextField(name, value, indent = 0) {
|
|
1349
|
+
if (value === null || value === "") {
|
|
1350
|
+
printDetailField(name, value, indent);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
const escaped = escapeTerminalControlCharacters(value);
|
|
1354
|
+
print(`${indentation(indent)}${name}:`);
|
|
1355
|
+
for (const line of escaped.split("\n")) {
|
|
1356
|
+
print(`${indentation(indent + 2)}${line}`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
function printAssigneeDetail(name, assignee, indent = 0) {
|
|
1360
|
+
if (assignee === null) {
|
|
1361
|
+
printDetailField(name, null, indent);
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
print(`${indentation(indent)}${name}:`);
|
|
1365
|
+
printDetailField("type", assignee.type, indent + 2);
|
|
1366
|
+
switch (assignee.type) {
|
|
1367
|
+
case "human":
|
|
1368
|
+
printDetailField("humanId", assignee.humanId, indent + 2);
|
|
1369
|
+
printDetailField("displayName", assignee.displayName, indent + 2);
|
|
1370
|
+
printDetailField("avatarUrl", assignee.avatarUrl, indent + 2);
|
|
1371
|
+
return;
|
|
1372
|
+
case "assistant":
|
|
1373
|
+
printDetailField("aiAssistantId", assignee.aiAssistantId, indent + 2);
|
|
1374
|
+
printDetailField("displayName", assignee.displayName, indent + 2);
|
|
1375
|
+
printDetailField("avatarUrl", assignee.avatarUrl, indent + 2);
|
|
1376
|
+
return;
|
|
1377
|
+
case "teammate":
|
|
1378
|
+
printDetailField("aiTeammateId", assignee.aiTeammateId, indent + 2);
|
|
1379
|
+
printDetailField("displayName", assignee.displayName, indent + 2);
|
|
1380
|
+
printDetailField("avatarUrl", assignee.avatarUrl, indent + 2);
|
|
1381
|
+
return;
|
|
1382
|
+
case "unknown":
|
|
1383
|
+
printDetailField("humanId", assignee.humanId, indent + 2);
|
|
1384
|
+
printDetailField("aiAssistantId", assignee.aiAssistantId, indent + 2);
|
|
1385
|
+
printDetailField("aiTeammateId", assignee.aiTeammateId, indent + 2);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
function printStatusRemainingFields(item, indent) {
|
|
1389
|
+
printDetailField("name", item.name, indent);
|
|
1390
|
+
printDetailTextField("desc", item.desc, indent);
|
|
1391
|
+
printDetailField("type", `${item.type} (${statusTypeLabel(item.type)})`, indent);
|
|
1392
|
+
printAssigneeDetail("preferredAssignee", item.preferredAssignee, indent);
|
|
1393
|
+
printDetailField("order", item.order, indent);
|
|
1394
|
+
}
|
|
1395
|
+
function printStatusDetail(item) {
|
|
1396
|
+
print(`${colors.bold}Status${colors.reset}`);
|
|
1397
|
+
printDetailField("scopedId", item.scopedId);
|
|
1398
|
+
printStatusRemainingFields(item, 0);
|
|
1399
|
+
}
|
|
1400
|
+
function printWorkflowDetail(item) {
|
|
1401
|
+
print(`${colors.bold}Workflow${colors.reset}`);
|
|
1402
|
+
printDetailField("fileId", item.fileId);
|
|
1403
|
+
printDetailField("ownerHumanId", item.ownerHumanId);
|
|
1404
|
+
printDetailTextField("desc", item.desc);
|
|
1405
|
+
if (item.statusList.length === 0) {
|
|
1406
|
+
print("statusList: []");
|
|
1407
|
+
} else {
|
|
1408
|
+
print("statusList:");
|
|
1409
|
+
for (const status of item.statusList) {
|
|
1410
|
+
print(` - scopedId: ${status.scopedId}`);
|
|
1411
|
+
printStatusRemainingFields(status, 4);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (item.transitions.length === 0) {
|
|
1415
|
+
print("transitions: []");
|
|
1416
|
+
} else {
|
|
1417
|
+
print("transitions:");
|
|
1418
|
+
for (const transition of item.transitions) {
|
|
1419
|
+
print(` - statusFromScopedId: ${transition.statusFromScopedId}`);
|
|
1420
|
+
printDetailField("statusToScopedId", transition.statusToScopedId, 4);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
function printTaskSummary(item) {
|
|
1135
1425
|
const runIds = item.agentRuns.map((run) => run.pipelineTriggerId).join(",") || "-";
|
|
1136
1426
|
print(
|
|
1137
1427
|
`${colors.bold}#${item.scopedId}${colors.reset} ${item.title} status=${item.statusScopedId} priority=${priorityLabel(item.priority)} assignee=${formatAssignee(item.assignee)} runs=${runIds}`
|
|
1138
1428
|
);
|
|
1139
1429
|
}
|
|
1430
|
+
function printTaskDetail(item) {
|
|
1431
|
+
print(`${colors.bold}Task${colors.reset}`);
|
|
1432
|
+
printDetailField("scopedId", item.scopedId);
|
|
1433
|
+
printDetailField("workflowFileId", item.workflowFileId);
|
|
1434
|
+
printDetailField("ownerHumanId", item.ownerHumanId);
|
|
1435
|
+
printDetailField("title", item.title);
|
|
1436
|
+
printDetailTextField("desc", item.desc);
|
|
1437
|
+
printDetailField("statusScopedId", item.statusScopedId);
|
|
1438
|
+
printDetailField("priority", `${item.priority} (${priorityLabel(item.priority)})`);
|
|
1439
|
+
printAssigneeDetail("assignee", item.assignee);
|
|
1440
|
+
if (item.linkedFileIds.length === 0) {
|
|
1441
|
+
print("linkedFileIds: []");
|
|
1442
|
+
} else {
|
|
1443
|
+
print("linkedFileIds:");
|
|
1444
|
+
for (const fileId of item.linkedFileIds) print(` - ${fileId}`);
|
|
1445
|
+
}
|
|
1446
|
+
printDetailField("subscribed", item.subscribed);
|
|
1447
|
+
if (item.agentRuns.length === 0) {
|
|
1448
|
+
print("agentRuns: []");
|
|
1449
|
+
} else {
|
|
1450
|
+
print("agentRuns:");
|
|
1451
|
+
for (const run of item.agentRuns) {
|
|
1452
|
+
print(` - pipelineTriggerId: ${run.pipelineTriggerId}`);
|
|
1453
|
+
printAssigneeDetail("executor", run.executor, 4);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
printDetailField("createdAt", item.createdAt);
|
|
1457
|
+
printDetailField("latestActAt", item.latestActAt);
|
|
1458
|
+
}
|
|
1459
|
+
function printCommentDetail(item) {
|
|
1460
|
+
print(`${colors.bold}Comment${colors.reset}`);
|
|
1461
|
+
printDetailField("id", item.id);
|
|
1462
|
+
printDetailField("workflowFileId", item.workflowFileId);
|
|
1463
|
+
printDetailField("workflowTaskScopedId", item.workflowTaskScopedId);
|
|
1464
|
+
printDetailTextField("content", item.content);
|
|
1465
|
+
printAssigneeDetail("createdBy", item.createdBy);
|
|
1466
|
+
printDetailField("createdAt", item.createdAt);
|
|
1467
|
+
printDetailField("updatedAt", item.updatedAt);
|
|
1468
|
+
}
|
|
1140
1469
|
function registerWorkflowCommand(program2) {
|
|
1141
1470
|
const workflow = program2.command("workflow").description("Workflow operations");
|
|
1142
1471
|
addWorkspaceOption(
|
|
@@ -1192,13 +1521,7 @@ function registerWorkflowCommand(program2) {
|
|
|
1192
1521
|
failResponse(response);
|
|
1193
1522
|
}
|
|
1194
1523
|
stopSpinner(true, "Workflow fetched");
|
|
1195
|
-
|
|
1196
|
-
print(`${colors.bold}Statuses${colors.reset}`);
|
|
1197
|
-
for (const status of response.data.statusList) printStatus(status);
|
|
1198
|
-
print(`${colors.bold}Transitions${colors.reset}`);
|
|
1199
|
-
for (const transition of response.data.transitions) {
|
|
1200
|
-
print(`${transition.statusFromScopedId} -> ${transition.statusToScopedId}`);
|
|
1201
|
-
}
|
|
1524
|
+
printWorkflowDetail(response.data);
|
|
1202
1525
|
}
|
|
1203
1526
|
);
|
|
1204
1527
|
addWorkspaceOption(
|
|
@@ -1254,6 +1577,7 @@ function registerWorkflowCommand(program2) {
|
|
|
1254
1577
|
registerStatusCommands(workflow);
|
|
1255
1578
|
registerTransitionCommands(workflow);
|
|
1256
1579
|
registerTaskCommands(workflow);
|
|
1580
|
+
registerCommentCommands(workflow);
|
|
1257
1581
|
registerRunCommands(workflow);
|
|
1258
1582
|
}
|
|
1259
1583
|
function registerStatusCommands(workflow) {
|
|
@@ -1284,7 +1608,7 @@ function registerStatusCommands(workflow) {
|
|
|
1284
1608
|
failResponse(response);
|
|
1285
1609
|
}
|
|
1286
1610
|
stopSpinner(true, "Status created");
|
|
1287
|
-
|
|
1611
|
+
printStatusDetail(response.data);
|
|
1288
1612
|
}
|
|
1289
1613
|
);
|
|
1290
1614
|
addPreferredAssigneeOptions(
|
|
@@ -1319,7 +1643,7 @@ function registerStatusCommands(workflow) {
|
|
|
1319
1643
|
failResponse(response);
|
|
1320
1644
|
}
|
|
1321
1645
|
stopSpinner(true, "Status updated");
|
|
1322
|
-
|
|
1646
|
+
printStatusDetail(response.data);
|
|
1323
1647
|
}
|
|
1324
1648
|
);
|
|
1325
1649
|
addWorkspaceOption(
|
|
@@ -1415,7 +1739,7 @@ function registerTaskCommands(workflow) {
|
|
|
1415
1739
|
return;
|
|
1416
1740
|
}
|
|
1417
1741
|
stopSpinner(true, `Found ${response.data.tasks.length} task(s)`);
|
|
1418
|
-
for (const item of response.data.tasks)
|
|
1742
|
+
for (const item of response.data.tasks) printTaskSummary(item);
|
|
1419
1743
|
if (response.data.nextCursor) print(`${colors.dim}nextCursor=${response.data.nextCursor}${colors.reset}`);
|
|
1420
1744
|
}
|
|
1421
1745
|
);
|
|
@@ -1432,7 +1756,7 @@ function registerTaskCommands(workflow) {
|
|
|
1432
1756
|
failResponse(response);
|
|
1433
1757
|
}
|
|
1434
1758
|
stopSpinner(true, "Task fetched");
|
|
1435
|
-
|
|
1759
|
+
printTaskDetail(response.data);
|
|
1436
1760
|
});
|
|
1437
1761
|
addAssigneeOptions(
|
|
1438
1762
|
addWorkspaceOption(
|
|
@@ -1463,7 +1787,7 @@ function registerTaskCommands(workflow) {
|
|
|
1463
1787
|
failResponse(response);
|
|
1464
1788
|
}
|
|
1465
1789
|
stopSpinner(true, "Task created");
|
|
1466
|
-
|
|
1790
|
+
printTaskDetail(response.data);
|
|
1467
1791
|
}
|
|
1468
1792
|
);
|
|
1469
1793
|
addWorkspaceOption(
|
|
@@ -1497,7 +1821,7 @@ function registerTaskCommands(workflow) {
|
|
|
1497
1821
|
failResponse(response);
|
|
1498
1822
|
}
|
|
1499
1823
|
stopSpinner(true, "Task updated");
|
|
1500
|
-
|
|
1824
|
+
printTaskDetail(response.data);
|
|
1501
1825
|
}
|
|
1502
1826
|
);
|
|
1503
1827
|
for (const action of ["add-linked-files", "remove-linked-files"]) {
|
|
@@ -1517,7 +1841,7 @@ function registerTaskCommands(workflow) {
|
|
|
1517
1841
|
failResponse(response);
|
|
1518
1842
|
}
|
|
1519
1843
|
stopSpinner(true, action === "add-linked-files" ? "Linked files added" : "Linked files removed");
|
|
1520
|
-
|
|
1844
|
+
printTaskDetail(response.data);
|
|
1521
1845
|
}
|
|
1522
1846
|
);
|
|
1523
1847
|
}
|
|
@@ -1539,7 +1863,7 @@ function registerTaskCommands(workflow) {
|
|
|
1539
1863
|
failResponse(response);
|
|
1540
1864
|
}
|
|
1541
1865
|
stopSpinner(true, "Task moved");
|
|
1542
|
-
|
|
1866
|
+
printTaskDetail(response.data);
|
|
1543
1867
|
}
|
|
1544
1868
|
);
|
|
1545
1869
|
addAssigneeOptions(
|
|
@@ -1564,7 +1888,7 @@ function registerTaskCommands(workflow) {
|
|
|
1564
1888
|
failResponse(response);
|
|
1565
1889
|
}
|
|
1566
1890
|
stopSpinner(true, "Task assignee updated");
|
|
1567
|
-
|
|
1891
|
+
printTaskDetail(response.data);
|
|
1568
1892
|
}
|
|
1569
1893
|
);
|
|
1570
1894
|
addWorkspaceOption(
|
|
@@ -1582,6 +1906,71 @@ function registerTaskCommands(workflow) {
|
|
|
1582
1906
|
stopSpinner(true, `Deleted: ${response.data.scopedId}`);
|
|
1583
1907
|
});
|
|
1584
1908
|
}
|
|
1909
|
+
function registerCommentCommands(workflow) {
|
|
1910
|
+
const comment = workflow.command("comment").description("Workflow task comment operations");
|
|
1911
|
+
addWorkspaceOption(
|
|
1912
|
+
comment.command("create").description("Create a workflow task comment").argument("<workflowFileId>").argument("<taskScopedId>").requiredOption("--content-path <localPath>", "Local UTF-8 text file for comment content")
|
|
1913
|
+
).action(
|
|
1914
|
+
async (workflowFileId, taskScopedId, options) => {
|
|
1915
|
+
const { id, content } = runOrExit3(() => ({
|
|
1916
|
+
id: parsePositiveInteger(taskScopedId, "taskScopedId"),
|
|
1917
|
+
content: readCommentContentFile(options.contentPath)
|
|
1918
|
+
}));
|
|
1919
|
+
startSpinner("Creating comment...");
|
|
1920
|
+
const response = await httpPost(
|
|
1921
|
+
`/workspaces/${options.workspace}/workflows/${workflowFileId}/tasks/${id}/comments`,
|
|
1922
|
+
{ content }
|
|
1923
|
+
);
|
|
1924
|
+
if (!response.ok) {
|
|
1925
|
+
stopSpinner(false, "Failed");
|
|
1926
|
+
failResponse(response);
|
|
1927
|
+
}
|
|
1928
|
+
stopSpinner(true, "Comment created");
|
|
1929
|
+
printCommentDetail(response.data);
|
|
1930
|
+
}
|
|
1931
|
+
);
|
|
1932
|
+
addWorkspaceOption(
|
|
1933
|
+
comment.command("list").description("List workflow task comments").argument("<workflowFileId>").argument("<taskScopedId>")
|
|
1934
|
+
).action(async (workflowFileId, taskScopedId, options) => {
|
|
1935
|
+
const id = runOrExit3(() => parsePositiveInteger(taskScopedId, "taskScopedId"));
|
|
1936
|
+
startSpinner("Fetching comments...");
|
|
1937
|
+
const response = await httpGet(
|
|
1938
|
+
`/workspaces/${options.workspace}/workflows/${workflowFileId}/tasks/${id}/comments`
|
|
1939
|
+
);
|
|
1940
|
+
if (!response.ok) {
|
|
1941
|
+
stopSpinner(false, "Failed");
|
|
1942
|
+
failResponse(response);
|
|
1943
|
+
}
|
|
1944
|
+
if (response.data.items.length === 0) {
|
|
1945
|
+
stopSpinner(true, "No comments found.");
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
stopSpinner(true, `Found ${response.data.items.length} comment(s)`);
|
|
1949
|
+
response.data.items.forEach((item, index) => {
|
|
1950
|
+
if (index > 0) print("");
|
|
1951
|
+
printCommentDetail(item);
|
|
1952
|
+
});
|
|
1953
|
+
});
|
|
1954
|
+
addWorkspaceOption(
|
|
1955
|
+
comment.command("delete").description("Delete a workflow task comment").argument("<workflowFileId>").argument("<taskScopedId>").argument("<commentId>")
|
|
1956
|
+
).action(
|
|
1957
|
+
async (workflowFileId, taskScopedId, commentId, options) => {
|
|
1958
|
+
const ids = runOrExit3(() => ({
|
|
1959
|
+
task: parsePositiveInteger(taskScopedId, "taskScopedId"),
|
|
1960
|
+
comment: parsePositiveInteger(commentId, "commentId")
|
|
1961
|
+
}));
|
|
1962
|
+
startSpinner("Deleting comment...");
|
|
1963
|
+
const response = await httpDelete(
|
|
1964
|
+
`/workspaces/${options.workspace}/workflows/${workflowFileId}/tasks/${ids.task}/comments/${ids.comment}`
|
|
1965
|
+
);
|
|
1966
|
+
if (!response.ok) {
|
|
1967
|
+
stopSpinner(false, "Failed");
|
|
1968
|
+
failResponse(response);
|
|
1969
|
+
}
|
|
1970
|
+
stopSpinner(true, `Deleted: ${response.data.id}`);
|
|
1971
|
+
}
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1585
1974
|
function registerRunCommands(workflow) {
|
|
1586
1975
|
const run = workflow.command("run").description("Workflow agent run operations");
|
|
1587
1976
|
addWorkspaceOption(
|
|
@@ -1642,6 +2031,7 @@ function createProgram(version) {
|
|
|
1642
2031
|
program2.name("moxt").description("Moxt CLI - AI Workspace").version(version).action(() => {
|
|
1643
2032
|
program2.help();
|
|
1644
2033
|
});
|
|
2034
|
+
registerAuthCommand(program2);
|
|
1645
2035
|
registerWhoamiCommand(program2);
|
|
1646
2036
|
registerWorkspaceCommand(program2);
|
|
1647
2037
|
registerFileCommand(program2);
|
|
@@ -1659,7 +2049,7 @@ var FLUSH_TIMEOUT_MS = 3e3;
|
|
|
1659
2049
|
var MAX_BATCH_SIZE = 100;
|
|
1660
2050
|
function commonLabels() {
|
|
1661
2051
|
return {
|
|
1662
|
-
cli_version: "0.3.3-beta.
|
|
2052
|
+
cli_version: "0.3.3-beta.2",
|
|
1663
2053
|
node_version: process.versions.node,
|
|
1664
2054
|
os: platform2(),
|
|
1665
2055
|
arch: arch2(),
|
|
@@ -1705,7 +2095,7 @@ async function flush() {
|
|
|
1705
2095
|
}
|
|
1706
2096
|
const batch = buffer.splice(0, MAX_BATCH_SIZE);
|
|
1707
2097
|
const payload = {
|
|
1708
|
-
release_id: "0.3.3-beta.
|
|
2098
|
+
release_id: "0.3.3-beta.2",
|
|
1709
2099
|
client_type: "cli",
|
|
1710
2100
|
metric_data_list: batch.map((m) => ({
|
|
1711
2101
|
profile: "cli",
|
|
@@ -1846,9 +2236,9 @@ function installExitHandler() {
|
|
|
1846
2236
|
|
|
1847
2237
|
// src/index.ts
|
|
1848
2238
|
updateNotifier({
|
|
1849
|
-
pkg: { name: "@moxt-ai/cli", version: "0.3.3-beta.
|
|
2239
|
+
pkg: { name: "@moxt-ai/cli", version: "0.3.3-beta.2" }
|
|
1850
2240
|
}).notify();
|
|
1851
|
-
var program = createProgram("0.3.3-beta.
|
|
2241
|
+
var program = createProgram("0.3.3-beta.2");
|
|
1852
2242
|
instrumentProgram(program);
|
|
1853
2243
|
installExitHandler();
|
|
1854
2244
|
program.parseAsync(process.argv).then(async () => {
|