@arcships/morula-runtime 0.1.0-alpha.7 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2279 -633
- package/dist/morula.js +1607 -110
- package/dist/plugin/plugin.json +1 -1
- package/dist/plugin/skills/morula-cli/SKILL.md +0 -1
- package/dist/plugin/skills/morula-cli/references/commands.md +1533 -254
- package/dist/plugin/skills/morula-working-on-issues/SKILL.md +21 -9
- package/dist/plugin/skills/morula-working-on-issues/references/working-on-issues-source-map.md +2 -1
- package/package.json +3 -2
package/dist/morula.js
CHANGED
|
@@ -41,6 +41,11 @@ function parseArgs(argv) {
|
|
|
41
41
|
const positionals = [];
|
|
42
42
|
const flags = {};
|
|
43
43
|
const bareFlags = [];
|
|
44
|
+
const valueOccurrences = {};
|
|
45
|
+
const recordValue = (key, value) => {
|
|
46
|
+
flags[key] = value;
|
|
47
|
+
(valueOccurrences[key] ??= []).push(value);
|
|
48
|
+
};
|
|
44
49
|
let positionalOnly = false;
|
|
45
50
|
for (let index = 0; index < argv.length; index += 1) {
|
|
46
51
|
const value = argv[index];
|
|
@@ -62,7 +67,7 @@ function parseArgs(argv) {
|
|
|
62
67
|
}
|
|
63
68
|
const equal = value.indexOf("=");
|
|
64
69
|
if (equal > 2) {
|
|
65
|
-
|
|
70
|
+
recordValue(value.slice(2, equal), value.slice(equal + 1));
|
|
66
71
|
continue;
|
|
67
72
|
}
|
|
68
73
|
const key = value.slice(2);
|
|
@@ -70,21 +75,34 @@ function parseArgs(argv) {
|
|
|
70
75
|
throw usage("invalid empty flag");
|
|
71
76
|
const next = argv[index + 1];
|
|
72
77
|
if (next !== void 0 && !next.startsWith("--")) {
|
|
73
|
-
|
|
78
|
+
recordValue(key, next);
|
|
74
79
|
index += 1;
|
|
75
80
|
} else {
|
|
76
81
|
flags[key] = true;
|
|
77
82
|
bareFlags.push(key);
|
|
78
83
|
}
|
|
79
84
|
}
|
|
85
|
+
const repeatedFlags = {};
|
|
86
|
+
for (const [key, values] of Object.entries(valueOccurrences)) {
|
|
87
|
+
if (values.length > 1)
|
|
88
|
+
repeatedFlags[key] = values;
|
|
89
|
+
}
|
|
80
90
|
return {
|
|
81
91
|
command: positionals.shift(),
|
|
82
92
|
action: positionals.shift(),
|
|
83
93
|
positionals,
|
|
84
94
|
flags,
|
|
85
|
-
bareFlags
|
|
95
|
+
bareFlags,
|
|
96
|
+
repeatedFlags
|
|
86
97
|
};
|
|
87
98
|
}
|
|
99
|
+
function flagStrings(parsed, name) {
|
|
100
|
+
const repeated = parsed.repeatedFlags[name];
|
|
101
|
+
if (repeated?.length)
|
|
102
|
+
return repeated;
|
|
103
|
+
const single = flagString(parsed.flags, name);
|
|
104
|
+
return single === void 0 ? [] : [single];
|
|
105
|
+
}
|
|
88
106
|
function flagString(flags, ...names) {
|
|
89
107
|
for (const name of names) {
|
|
90
108
|
const value = flags[name];
|
|
@@ -686,8 +704,8 @@ function page(ok, message) {
|
|
|
686
704
|
}
|
|
687
705
|
|
|
688
706
|
// ../morula-cli-core/dist/build-env.js
|
|
689
|
-
var BUILD_VERSION = "0.1.0-alpha.
|
|
690
|
-
var BUILD_ID = "
|
|
707
|
+
var BUILD_VERSION = "0.1.0-alpha.9";
|
|
708
|
+
var BUILD_ID = "20260921135326.48e72dd8";
|
|
691
709
|
var BUILD_CONTEXTS = {
|
|
692
710
|
"local": "http://localhost:8080/api",
|
|
693
711
|
"test": "https://morula.basecastle.com/api",
|
|
@@ -695,6 +713,42 @@ var BUILD_CONTEXTS = {
|
|
|
695
713
|
};
|
|
696
714
|
var BUILD_DEFAULT_CONTEXT = "prod";
|
|
697
715
|
|
|
716
|
+
// ../morula-api-client/dist/agent-task.js
|
|
717
|
+
var AgentTaskApi = class {
|
|
718
|
+
client;
|
|
719
|
+
constructor(client) {
|
|
720
|
+
this.client = client;
|
|
721
|
+
}
|
|
722
|
+
/** 列出 AgentTask(objectId 必填;status/page/pageSize 可选) */
|
|
723
|
+
list(query = {}) {
|
|
724
|
+
return this.client.get("/agent-tasks", query);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* 取消执行。
|
|
728
|
+
*
|
|
729
|
+
* 服务端语义(`agent-task.controller.ts` cancel,2026-09-20 起):
|
|
730
|
+
* - 成功 → 200 `{ ok: true, message: 'Task cancelled' }`;
|
|
731
|
+
* - 任务不存在 → **404** `NOT_FOUND`;
|
|
732
|
+
* - 状态不允许(`completed` / `cancelled`)→ **409** `CONFLICT`(带可读原因)。
|
|
733
|
+
*
|
|
734
|
+
* 即:**失败会 reject**(不再 200 假成功,见 issues/agent-task-cancel-returns-200-on-failure.md)——
|
|
735
|
+
* 调用方按异常/退出码判断,不要假定「调用返回了就是已取消」。
|
|
736
|
+
*/
|
|
737
|
+
cancel(id) {
|
|
738
|
+
return this.client.post(`/agent-tasks/${encodeURIComponent(requiredId(id))}/cancel`);
|
|
739
|
+
}
|
|
740
|
+
/** 重跑失败/已取消的执行(服务端复制原任务重新入队,返回**新** taskId) */
|
|
741
|
+
rerun(id) {
|
|
742
|
+
return this.client.post(`/agent-tasks/${encodeURIComponent(requiredId(id))}/rerun`);
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
function requiredId(id) {
|
|
746
|
+
const normalized = id.trim();
|
|
747
|
+
if (!normalized)
|
|
748
|
+
throw new TypeError("agent task id is required");
|
|
749
|
+
return normalized;
|
|
750
|
+
}
|
|
751
|
+
|
|
698
752
|
// ../morula-api-client/dist/attachment.js
|
|
699
753
|
var AttachmentApi = class {
|
|
700
754
|
client;
|
|
@@ -740,6 +794,13 @@ var AttachmentApi = class {
|
|
|
740
794
|
getDownloadUrl(id) {
|
|
741
795
|
return this.client.get(`/attachments/${encodeURIComponent(id)}/download-url`);
|
|
742
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* 读取附件的解析产物全文(MinerU markdown)。
|
|
799
|
+
* 未解析/解析中/解析失败由服务端在返回体里说明,不当作 HTTP 错误。
|
|
800
|
+
*/
|
|
801
|
+
content(id, maxChars) {
|
|
802
|
+
return this.client.get(`/attachments/${encodeURIComponent(id)}/content`, { maxChars });
|
|
803
|
+
}
|
|
743
804
|
/** 更新附件关联的资源 ID */
|
|
744
805
|
updateResourceId(id, resourceId) {
|
|
745
806
|
return this.client.patch(`/attachments/${encodeURIComponent(id)}`, { resourceId });
|
|
@@ -770,6 +831,81 @@ var AuthApi = class {
|
|
|
770
831
|
}
|
|
771
832
|
};
|
|
772
833
|
|
|
834
|
+
// ../morula-api-client/dist/autopilot.js
|
|
835
|
+
var AutopilotApi = class {
|
|
836
|
+
client;
|
|
837
|
+
constructor(client) {
|
|
838
|
+
this.client = client;
|
|
839
|
+
}
|
|
840
|
+
list(query = {}) {
|
|
841
|
+
return this.client.get("/agent-autopilots", query);
|
|
842
|
+
}
|
|
843
|
+
create(input) {
|
|
844
|
+
return this.client.post("/agent-autopilots", input);
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// ../morula-api-client/dist/ci.js
|
|
849
|
+
var CiApi = class {
|
|
850
|
+
client;
|
|
851
|
+
constructor(client) {
|
|
852
|
+
this.client = client;
|
|
853
|
+
}
|
|
854
|
+
/** 任务面 CI 视图(本任务关联的流水线运行 + 部署记录 + 归属依据) */
|
|
855
|
+
taskView(taskId, query = {}) {
|
|
856
|
+
return this.client.get(`/agent-tasks/${encodeURIComponent(requiredId2(taskId, "task id"))}/vcs/ci`, query);
|
|
857
|
+
}
|
|
858
|
+
/** 任务面部署记录(D3:仅展示,不驱动流转) */
|
|
859
|
+
taskDeployments(taskId) {
|
|
860
|
+
return this.client.get(`/agent-tasks/${encodeURIComponent(requiredId2(taskId, "task id"))}/vcs/ci/deployments`);
|
|
861
|
+
}
|
|
862
|
+
/** 空间面 CI 巡检视图(管家主入口;`ref` / `failed` / `limit` 为服务端过滤) */
|
|
863
|
+
spaceView(spaceId, query = {}) {
|
|
864
|
+
return this.client.get(`/vcs/spaces/${encodeURIComponent(requiredId2(spaceId, "space id"))}/ci`, query);
|
|
865
|
+
}
|
|
866
|
+
/** 空间面部署记录(D3:仅展示) */
|
|
867
|
+
spaceDeployments(spaceId) {
|
|
868
|
+
return this.client.get(`/vcs/spaces/${encodeURIComponent(requiredId2(spaceId, "space id"))}/ci/deployments`);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* job 日志(按 job id;服务端反查所属运行)。
|
|
872
|
+
*
|
|
873
|
+
* 两个面共用本端点:任务凭证按 tenantId 收敛,会话凭证按 `connection.spaceId === scopeId` 收敛。
|
|
874
|
+
* `truncated` 显式返回——日志被尾部截断时调用方必须知道(不能把截断当全文)。
|
|
875
|
+
*/
|
|
876
|
+
jobLog(jobId, query = {}) {
|
|
877
|
+
return this.client.get(`/vcs/jobs/${encodeURIComponent(requiredId2(jobId, "job id"))}/log`, query);
|
|
878
|
+
}
|
|
879
|
+
/** 失败用例清单(平台可选能力:GitLab test_report 有、GitHub 无 → `CAPABILITY_UNSUPPORTED`) */
|
|
880
|
+
testReport(runId) {
|
|
881
|
+
return this.client.get(`/vcs/runs/${encodeURIComponent(requiredId2(runId, "run id"))}/test-report`);
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* 重跑整条流水线的失败 job(Phase 3;开放面 = 人 + 管家)。
|
|
885
|
+
*
|
|
886
|
+
* 只允许 failure / cancelled 的运行,其余状态服务端回 `CI_RETRY_NOT_ALLOWED`(不是 4xx)。
|
|
887
|
+
* **非幂等**:每次调用都会在平台上产生一次新的重跑,重复调用不会去重。
|
|
888
|
+
*/
|
|
889
|
+
retryRun(runId) {
|
|
890
|
+
return this.client.post(`/vcs/runs/${encodeURIComponent(requiredId2(runId, "run id"))}/retry`);
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* 重跑单个 job(Phase 3;开放面 = 人 + 管家)。
|
|
894
|
+
*
|
|
895
|
+
* 服务端按 jobId 反查所属运行(`CI_JOB_NOT_FOUND` 覆盖「job 不存在」与「反查不到运行」两种情况);
|
|
896
|
+
* 状态门禁与 `retryRun` 相同。非幂等。
|
|
897
|
+
*/
|
|
898
|
+
retryJob(jobId) {
|
|
899
|
+
return this.client.post(`/vcs/jobs/${encodeURIComponent(requiredId2(jobId, "job id"))}/retry`);
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
function requiredId2(id, label) {
|
|
903
|
+
const normalized = id.trim();
|
|
904
|
+
if (!normalized)
|
|
905
|
+
throw new TypeError(`${label} is required`);
|
|
906
|
+
return normalized;
|
|
907
|
+
}
|
|
908
|
+
|
|
773
909
|
// ../morula-api-client/dist/client.js
|
|
774
910
|
var EXIT_CODE = {
|
|
775
911
|
OK: 0,
|
|
@@ -787,14 +923,16 @@ var MorulaApiError = class extends Error {
|
|
|
787
923
|
status;
|
|
788
924
|
retryable;
|
|
789
925
|
details;
|
|
926
|
+
errorCode;
|
|
790
927
|
name = "MorulaApiError";
|
|
791
|
-
constructor(code, message, exitCode, status2, retryable = false, details) {
|
|
928
|
+
constructor(code, message, exitCode, status2, retryable = false, details, errorCode) {
|
|
792
929
|
super(message);
|
|
793
930
|
this.code = code;
|
|
794
931
|
this.exitCode = exitCode;
|
|
795
932
|
this.status = status2;
|
|
796
933
|
this.retryable = retryable;
|
|
797
934
|
this.details = details;
|
|
935
|
+
this.errorCode = errorCode;
|
|
798
936
|
}
|
|
799
937
|
};
|
|
800
938
|
var CLI_BUILD_HEADER = "X-Morula-Cli-Build";
|
|
@@ -802,6 +940,28 @@ var CLI_CAPS_HEADER = "X-Morula-Cli-Caps";
|
|
|
802
940
|
var CLI_BUILD_MAX_LENGTH = 64;
|
|
803
941
|
var CLI_CAPS_MAX_LENGTH = 255;
|
|
804
942
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
943
|
+
var CAUSE_DETAIL_MAX_LENGTH = 200;
|
|
944
|
+
function describeFetchFailure(error) {
|
|
945
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
946
|
+
const detail = formatFetchCause(error?.cause);
|
|
947
|
+
return detail ? `${message} (cause: ${detail})` : message;
|
|
948
|
+
}
|
|
949
|
+
function formatFetchCause(cause) {
|
|
950
|
+
if (cause === void 0 || cause === null)
|
|
951
|
+
return "";
|
|
952
|
+
if (cause instanceof Error) {
|
|
953
|
+
const detail = cause;
|
|
954
|
+
const parts = [
|
|
955
|
+
typeof detail.code === "string" ? detail.code : "",
|
|
956
|
+
typeof detail.errno === "number" || typeof detail.errno === "string" ? `errno=${detail.errno}` : "",
|
|
957
|
+
typeof detail.syscall === "string" ? `syscall=${detail.syscall}` : "",
|
|
958
|
+
// cause 的 message 常与外层重复('fetch failed'),只有不同才带
|
|
959
|
+
detail.message && detail.message !== "fetch failed" ? detail.message : ""
|
|
960
|
+
].filter(Boolean);
|
|
961
|
+
return parts.join(" ").slice(0, CAUSE_DETAIL_MAX_LENGTH);
|
|
962
|
+
}
|
|
963
|
+
return String(cause).slice(0, CAUSE_DETAIL_MAX_LENGTH);
|
|
964
|
+
}
|
|
805
965
|
var MorulaApiClient = class {
|
|
806
966
|
baseUrl;
|
|
807
967
|
token;
|
|
@@ -899,7 +1059,7 @@ var MorulaApiClient = class {
|
|
|
899
1059
|
if (controller.signal.aborted) {
|
|
900
1060
|
throw new MorulaApiError("TIMEOUT", `request timed out after ${timeoutMs}ms`, EXIT_CODE.UPSTREAM, void 0, true);
|
|
901
1061
|
}
|
|
902
|
-
throw new MorulaApiError("NETWORK", `request failed: ${
|
|
1062
|
+
throw new MorulaApiError("NETWORK", `request failed: ${describeFetchFailure(error)}`, EXIT_CODE.UPSTREAM, void 0, true);
|
|
903
1063
|
} finally {
|
|
904
1064
|
clearTimeout(timer);
|
|
905
1065
|
}
|
|
@@ -960,28 +1120,35 @@ function isEnvelope(value) {
|
|
|
960
1120
|
function errorFromResponse(status2, parsed, raw) {
|
|
961
1121
|
const message = extractMessage(parsed, raw, status2);
|
|
962
1122
|
const details = isEnvelope(parsed) ? parsed.data : parsed;
|
|
1123
|
+
const errorCode = extractErrorCode(parsed);
|
|
963
1124
|
if (status2 === 400 || status2 === 422) {
|
|
964
|
-
return new MorulaApiError("BAD_REQUEST", message, EXIT_CODE.USAGE, status2, false, details);
|
|
1125
|
+
return new MorulaApiError("BAD_REQUEST", message, EXIT_CODE.USAGE, status2, false, details, errorCode);
|
|
965
1126
|
}
|
|
966
1127
|
if (status2 === 401) {
|
|
967
|
-
return new MorulaApiError("UNAUTHENTICATED", message, EXIT_CODE.UNAUTHENTICATED, status2, false, details);
|
|
1128
|
+
return new MorulaApiError("UNAUTHENTICATED", message, EXIT_CODE.UNAUTHENTICATED, status2, false, details, errorCode);
|
|
968
1129
|
}
|
|
969
1130
|
if (status2 === 403) {
|
|
970
|
-
return new MorulaApiError("FORBIDDEN", message, EXIT_CODE.FORBIDDEN, status2, false, details);
|
|
1131
|
+
return new MorulaApiError("FORBIDDEN", message, EXIT_CODE.FORBIDDEN, status2, false, details, errorCode);
|
|
971
1132
|
}
|
|
972
1133
|
if (status2 === 404) {
|
|
973
|
-
return new MorulaApiError("NOT_FOUND", message, EXIT_CODE.NOT_FOUND, status2, false, details);
|
|
1134
|
+
return new MorulaApiError("NOT_FOUND", message, EXIT_CODE.NOT_FOUND, status2, false, details, errorCode);
|
|
974
1135
|
}
|
|
975
1136
|
if (status2 === 409) {
|
|
976
|
-
return new MorulaApiError("CONFLICT", message, EXIT_CODE.CONFLICT, status2, false, details);
|
|
1137
|
+
return new MorulaApiError("CONFLICT", message, EXIT_CODE.CONFLICT, status2, false, details, errorCode);
|
|
977
1138
|
}
|
|
978
1139
|
if (status2 === 429) {
|
|
979
|
-
return new MorulaApiError("RATE_LIMITED", message, EXIT_CODE.UPSTREAM, status2, true, details);
|
|
1140
|
+
return new MorulaApiError("RATE_LIMITED", message, EXIT_CODE.UPSTREAM, status2, true, details, errorCode);
|
|
980
1141
|
}
|
|
981
1142
|
if (status2 >= 500) {
|
|
982
|
-
return new MorulaApiError("UPSTREAM", message, EXIT_CODE.UPSTREAM, status2, true, details);
|
|
1143
|
+
return new MorulaApiError("UPSTREAM", message, EXIT_CODE.UPSTREAM, status2, true, details, errorCode);
|
|
983
1144
|
}
|
|
984
|
-
return new MorulaApiError("HTTP_ERROR", message, EXIT_CODE.OTHER, status2, false, details);
|
|
1145
|
+
return new MorulaApiError("HTTP_ERROR", message, EXIT_CODE.OTHER, status2, false, details, errorCode);
|
|
1146
|
+
}
|
|
1147
|
+
function extractErrorCode(parsed) {
|
|
1148
|
+
if (!parsed || typeof parsed !== "object")
|
|
1149
|
+
return void 0;
|
|
1150
|
+
const value = parsed.errorCode;
|
|
1151
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
985
1152
|
}
|
|
986
1153
|
function extractMessage(parsed, raw, status2) {
|
|
987
1154
|
if (parsed && typeof parsed === "object" && "message" in parsed) {
|
|
@@ -1011,25 +1178,120 @@ var CommentApi = class {
|
|
|
1011
1178
|
return this.client.get("/comments", params);
|
|
1012
1179
|
}
|
|
1013
1180
|
get(id) {
|
|
1014
|
-
return this.client.get(`/comments/${encodeURIComponent(
|
|
1181
|
+
return this.client.get(`/comments/${encodeURIComponent(requiredId3(id))}`);
|
|
1015
1182
|
}
|
|
1016
1183
|
create(input) {
|
|
1017
1184
|
return this.client.post("/comments", input);
|
|
1018
1185
|
}
|
|
1019
1186
|
update(id, input) {
|
|
1020
|
-
return this.client.patch(`/comments/${encodeURIComponent(
|
|
1187
|
+
return this.client.patch(`/comments/${encodeURIComponent(requiredId3(id))}`, input);
|
|
1021
1188
|
}
|
|
1022
1189
|
delete(id) {
|
|
1023
|
-
return this.client.delete(`/comments/${encodeURIComponent(
|
|
1190
|
+
return this.client.delete(`/comments/${encodeURIComponent(requiredId3(id))}`);
|
|
1024
1191
|
}
|
|
1025
1192
|
};
|
|
1026
|
-
function
|
|
1193
|
+
function requiredId3(id) {
|
|
1027
1194
|
const normalized = id.trim();
|
|
1028
1195
|
if (!normalized)
|
|
1029
1196
|
throw new TypeError("comment id is required");
|
|
1030
1197
|
return normalized;
|
|
1031
1198
|
}
|
|
1032
1199
|
|
|
1200
|
+
// ../morula-api-client/dist/dashboard.js
|
|
1201
|
+
var DashboardApi = class {
|
|
1202
|
+
client;
|
|
1203
|
+
constructor(client) {
|
|
1204
|
+
this.client = client;
|
|
1205
|
+
}
|
|
1206
|
+
get(spaceId) {
|
|
1207
|
+
return this.client.get(`/spaces/${encodeURIComponent(requiredId4(spaceId))}/dashboard`);
|
|
1208
|
+
}
|
|
1209
|
+
statistics(spaceId) {
|
|
1210
|
+
return this.client.get(`/spaces/${encodeURIComponent(requiredId4(spaceId))}/statistics`);
|
|
1211
|
+
}
|
|
1212
|
+
workload(spaceId) {
|
|
1213
|
+
return this.client.get(`/spaces/${encodeURIComponent(requiredId4(spaceId))}/workload`);
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
function requiredId4(id) {
|
|
1217
|
+
const normalized = id.trim();
|
|
1218
|
+
if (!normalized)
|
|
1219
|
+
throw new TypeError("space id is required");
|
|
1220
|
+
return normalized;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// ../morula-api-client/dist/document.js
|
|
1224
|
+
var DocumentApi = class {
|
|
1225
|
+
client;
|
|
1226
|
+
constructor(client) {
|
|
1227
|
+
this.client = client;
|
|
1228
|
+
}
|
|
1229
|
+
list(query = {}) {
|
|
1230
|
+
return this.client.get("/documents", query);
|
|
1231
|
+
}
|
|
1232
|
+
get(id) {
|
|
1233
|
+
return this.client.get(`/documents/${encodeURIComponent(requiredId5(id))}`);
|
|
1234
|
+
}
|
|
1235
|
+
/** 已解析归档的文档内容(Markdown 正文等) */
|
|
1236
|
+
artifacts(id) {
|
|
1237
|
+
return this.client.get(`/documents/${encodeURIComponent(requiredId5(id))}/artifacts`);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
function requiredId5(id) {
|
|
1241
|
+
const normalized = String(id).trim();
|
|
1242
|
+
if (!normalized)
|
|
1243
|
+
throw new TypeError("document id is required");
|
|
1244
|
+
return normalized;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// ../morula-api-client/dist/object-meta.js
|
|
1248
|
+
var ObjectMetaApi = class {
|
|
1249
|
+
client;
|
|
1250
|
+
constructor(client) {
|
|
1251
|
+
this.client = client;
|
|
1252
|
+
}
|
|
1253
|
+
list(objectId) {
|
|
1254
|
+
return this.client.get(`/objects/${encodeURIComponent(requiredId6(objectId))}/agent-meta`);
|
|
1255
|
+
}
|
|
1256
|
+
set(objectId, key, value) {
|
|
1257
|
+
return this.client.request(`/objects/${encodeURIComponent(requiredId6(objectId))}/agent-meta/${encodeURIComponent(requiredKey(key))}`, { method: "PUT", body: { value } });
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
function requiredId6(id) {
|
|
1261
|
+
const normalized = id.trim();
|
|
1262
|
+
if (!normalized)
|
|
1263
|
+
throw new TypeError("object id is required");
|
|
1264
|
+
return normalized;
|
|
1265
|
+
}
|
|
1266
|
+
function requiredKey(key) {
|
|
1267
|
+
const normalized = key.trim();
|
|
1268
|
+
if (!normalized)
|
|
1269
|
+
throw new TypeError("meta key is required");
|
|
1270
|
+
return normalized;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// ../morula-api-client/dist/repo.js
|
|
1274
|
+
var RepoApi = class {
|
|
1275
|
+
client;
|
|
1276
|
+
constructor(client) {
|
|
1277
|
+
this.client = client;
|
|
1278
|
+
}
|
|
1279
|
+
/** 目录清单(path 缺省 = 仓库根) */
|
|
1280
|
+
files(spaceId, query = {}) {
|
|
1281
|
+
return this.client.get(`/spaces/${encodeURIComponent(requiredId7(spaceId))}/repo/files`, query);
|
|
1282
|
+
}
|
|
1283
|
+
/** 单文件内容(path 必填) */
|
|
1284
|
+
read(spaceId, query) {
|
|
1285
|
+
return this.client.get(`/spaces/${encodeURIComponent(requiredId7(spaceId))}/repo/file`, query);
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
function requiredId7(id) {
|
|
1289
|
+
const normalized = id.trim();
|
|
1290
|
+
if (!normalized)
|
|
1291
|
+
throw new TypeError("space id is required");
|
|
1292
|
+
return normalized;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1033
1295
|
// ../morula-api-client/dist/report.js
|
|
1034
1296
|
var ReportApi = class {
|
|
1035
1297
|
client;
|
|
@@ -1040,13 +1302,13 @@ var ReportApi = class {
|
|
|
1040
1302
|
return this.client.post("/agent-reports", input);
|
|
1041
1303
|
}
|
|
1042
1304
|
get(id) {
|
|
1043
|
-
return this.client.get(`/agent-reports/${encodeURIComponent(
|
|
1305
|
+
return this.client.get(`/agent-reports/${encodeURIComponent(requiredId8(id))}`);
|
|
1044
1306
|
}
|
|
1045
1307
|
listByAutopilot(autopilotId, query = {}) {
|
|
1046
|
-
return this.client.get(`/agent-autopilots/${encodeURIComponent(
|
|
1308
|
+
return this.client.get(`/agent-autopilots/${encodeURIComponent(requiredId8(autopilotId, "autopilot"))}/reports`, { ...query });
|
|
1047
1309
|
}
|
|
1048
1310
|
};
|
|
1049
|
-
function
|
|
1311
|
+
function requiredId8(id, label = "report") {
|
|
1050
1312
|
const normalized = id.trim();
|
|
1051
1313
|
if (!normalized)
|
|
1052
1314
|
throw new TypeError(`${label} id is required`);
|
|
@@ -1063,19 +1325,19 @@ var SpaceMemberApi = class {
|
|
|
1063
1325
|
return this.client.get("/space-members", { spaceId, ...query });
|
|
1064
1326
|
}
|
|
1065
1327
|
get(id) {
|
|
1066
|
-
return this.client.get(`/space-members/${encodeURIComponent(
|
|
1328
|
+
return this.client.get(`/space-members/${encodeURIComponent(requiredId9(id))}`);
|
|
1067
1329
|
}
|
|
1068
1330
|
create(input) {
|
|
1069
1331
|
return this.client.post("/space-members", input);
|
|
1070
1332
|
}
|
|
1071
1333
|
update(id, input) {
|
|
1072
|
-
return this.client.patch(`/space-members/${encodeURIComponent(
|
|
1334
|
+
return this.client.patch(`/space-members/${encodeURIComponent(requiredId9(id))}`, input);
|
|
1073
1335
|
}
|
|
1074
1336
|
delete(id) {
|
|
1075
|
-
return this.client.delete(`/space-members/${encodeURIComponent(
|
|
1337
|
+
return this.client.delete(`/space-members/${encodeURIComponent(requiredId9(id))}`);
|
|
1076
1338
|
}
|
|
1077
1339
|
};
|
|
1078
|
-
function
|
|
1340
|
+
function requiredId9(id) {
|
|
1079
1341
|
const normalized = id.trim();
|
|
1080
1342
|
if (!normalized)
|
|
1081
1343
|
throw new TypeError("space member id is required");
|
|
@@ -1092,25 +1354,47 @@ var SpaceApi = class {
|
|
|
1092
1354
|
return this.client.get("/spaces", query);
|
|
1093
1355
|
}
|
|
1094
1356
|
get(id) {
|
|
1095
|
-
return this.client.get(`/spaces/${encodeURIComponent(
|
|
1357
|
+
return this.client.get(`/spaces/${encodeURIComponent(requiredId10(id))}`);
|
|
1096
1358
|
}
|
|
1097
1359
|
create(input) {
|
|
1098
1360
|
return this.client.post("/spaces", input);
|
|
1099
1361
|
}
|
|
1100
1362
|
update(id, input) {
|
|
1101
|
-
return this.client.patch(`/spaces/${encodeURIComponent(
|
|
1363
|
+
return this.client.patch(`/spaces/${encodeURIComponent(requiredId10(id))}`, input);
|
|
1102
1364
|
}
|
|
1103
1365
|
delete(id) {
|
|
1104
|
-
return this.client.delete(`/spaces/${encodeURIComponent(
|
|
1366
|
+
return this.client.delete(`/spaces/${encodeURIComponent(requiredId10(id))}`);
|
|
1105
1367
|
}
|
|
1106
1368
|
};
|
|
1107
|
-
function
|
|
1369
|
+
function requiredId10(id) {
|
|
1108
1370
|
const normalized = id.trim();
|
|
1109
1371
|
if (!normalized)
|
|
1110
1372
|
throw new TypeError("space id is required");
|
|
1111
1373
|
return normalized;
|
|
1112
1374
|
}
|
|
1113
1375
|
|
|
1376
|
+
// ../morula-api-client/dist/steward.js
|
|
1377
|
+
var StewardApi = class {
|
|
1378
|
+
client;
|
|
1379
|
+
constructor(client) {
|
|
1380
|
+
this.client = client;
|
|
1381
|
+
}
|
|
1382
|
+
/** 管家信息(未接管的空间返回 null) */
|
|
1383
|
+
get(projectId) {
|
|
1384
|
+
return this.client.get(`/stewards/projects/${encodeURIComponent(requiredId11(projectId))}`);
|
|
1385
|
+
}
|
|
1386
|
+
/** 派单:建任务并派发(目标状态由 state 决定,backlog 不派发执行) */
|
|
1387
|
+
dispatch(projectId, input) {
|
|
1388
|
+
return this.client.post(`/stewards/projects/${encodeURIComponent(requiredId11(projectId))}/dispatch`, input);
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
function requiredId11(id) {
|
|
1392
|
+
const normalized = id.trim();
|
|
1393
|
+
if (!normalized)
|
|
1394
|
+
throw new TypeError("project id is required");
|
|
1395
|
+
return normalized;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1114
1398
|
// ../morula-api-client/dist/task.js
|
|
1115
1399
|
var TaskApi = class {
|
|
1116
1400
|
client;
|
|
@@ -1121,16 +1405,16 @@ var TaskApi = class {
|
|
|
1121
1405
|
return this.client.get("/objects", query);
|
|
1122
1406
|
}
|
|
1123
1407
|
get(id) {
|
|
1124
|
-
return this.client.get(`/objects/${encodeURIComponent(
|
|
1408
|
+
return this.client.get(`/objects/${encodeURIComponent(requiredId12(id))}`);
|
|
1125
1409
|
}
|
|
1126
1410
|
create(input) {
|
|
1127
1411
|
return this.client.post("/objects", input);
|
|
1128
1412
|
}
|
|
1129
1413
|
update(id, input) {
|
|
1130
|
-
return this.client.patch(`/objects/${encodeURIComponent(
|
|
1414
|
+
return this.client.patch(`/objects/${encodeURIComponent(requiredId12(id))}`, input);
|
|
1131
1415
|
}
|
|
1132
1416
|
delete(id) {
|
|
1133
|
-
return this.client.delete(`/objects/${encodeURIComponent(
|
|
1417
|
+
return this.client.delete(`/objects/${encodeURIComponent(requiredId12(id))}`);
|
|
1134
1418
|
}
|
|
1135
1419
|
/** 查询空间字段模板(字段 key → id 映射、options) */
|
|
1136
1420
|
getSchemaFields(spaceId) {
|
|
@@ -1159,7 +1443,7 @@ var TaskApi = class {
|
|
|
1159
1443
|
return this.client.delete(`/object-field-values`, { objectId, schemaFieldId });
|
|
1160
1444
|
}
|
|
1161
1445
|
};
|
|
1162
|
-
function
|
|
1446
|
+
function requiredId12(id) {
|
|
1163
1447
|
const normalized = id.trim();
|
|
1164
1448
|
if (!normalized)
|
|
1165
1449
|
throw new TypeError("task id is required");
|
|
@@ -1183,23 +1467,39 @@ var WebhookApi = class {
|
|
|
1183
1467
|
// ../morula-api-client/dist/index.js
|
|
1184
1468
|
var MorulaClient = class {
|
|
1185
1469
|
http;
|
|
1470
|
+
agentTasks;
|
|
1186
1471
|
attachments;
|
|
1187
1472
|
auth;
|
|
1473
|
+
autopilots;
|
|
1474
|
+
ci;
|
|
1188
1475
|
comments;
|
|
1476
|
+
dashboard;
|
|
1477
|
+
documents;
|
|
1478
|
+
objectMeta;
|
|
1189
1479
|
reports;
|
|
1480
|
+
repos;
|
|
1190
1481
|
tasks;
|
|
1191
1482
|
spaces;
|
|
1192
1483
|
spaceMembers;
|
|
1484
|
+
stewards;
|
|
1193
1485
|
webhooks;
|
|
1194
1486
|
constructor(options) {
|
|
1195
1487
|
this.http = new MorulaApiClient(options);
|
|
1488
|
+
this.agentTasks = new AgentTaskApi(this.http);
|
|
1196
1489
|
this.attachments = new AttachmentApi(this.http);
|
|
1197
1490
|
this.auth = new AuthApi(this.http);
|
|
1491
|
+
this.autopilots = new AutopilotApi(this.http);
|
|
1492
|
+
this.ci = new CiApi(this.http);
|
|
1198
1493
|
this.comments = new CommentApi(this.http);
|
|
1494
|
+
this.dashboard = new DashboardApi(this.http);
|
|
1495
|
+
this.documents = new DocumentApi(this.http);
|
|
1496
|
+
this.objectMeta = new ObjectMetaApi(this.http);
|
|
1199
1497
|
this.reports = new ReportApi(this.http);
|
|
1498
|
+
this.repos = new RepoApi(this.http);
|
|
1200
1499
|
this.tasks = new TaskApi(this.http);
|
|
1201
1500
|
this.spaces = new SpaceApi(this.http);
|
|
1202
1501
|
this.spaceMembers = new SpaceMemberApi(this.http);
|
|
1502
|
+
this.stewards = new StewardApi(this.http);
|
|
1203
1503
|
this.webhooks = new WebhookApi(this.http);
|
|
1204
1504
|
}
|
|
1205
1505
|
};
|
|
@@ -1255,7 +1555,8 @@ function renderActionHelp(base, resource, action) {
|
|
|
1255
1555
|
...f.valueHint ? { valueHint: f.valueHint } : {},
|
|
1256
1556
|
required: f.required ?? false,
|
|
1257
1557
|
...f.enum ? { enum: f.enum } : {},
|
|
1258
|
-
...f.default !== void 0 ? { default: f.default } : {}
|
|
1558
|
+
...f.default !== void 0 ? { default: f.default } : {},
|
|
1559
|
+
...f.repeatable ? { repeatable: true } : {}
|
|
1259
1560
|
})),
|
|
1260
1561
|
examples: action.examples ?? [],
|
|
1261
1562
|
writes: action.writes ?? "none",
|
|
@@ -1396,6 +1697,16 @@ function validateArgs(resource, action, parsed, options = {}) {
|
|
|
1396
1697
|
hint: `${usageHint} (e.g. --${flag} ${valuePart})`
|
|
1397
1698
|
});
|
|
1398
1699
|
}
|
|
1700
|
+
for (const [name, values] of Object.entries(parsed.repeatedFlags)) {
|
|
1701
|
+
if (values.length < 2)
|
|
1702
|
+
continue;
|
|
1703
|
+
const spec = declaredFlags.find((f) => f.name === name);
|
|
1704
|
+
if (spec?.repeatable)
|
|
1705
|
+
continue;
|
|
1706
|
+
throw usage(`duplicate flag --${name} (given ${values.length} times; only the last value would be used)`, {
|
|
1707
|
+
hint: usageHint
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1399
1710
|
for (const spec of declaredFlags) {
|
|
1400
1711
|
if (!spec.required)
|
|
1401
1712
|
continue;
|
|
@@ -1515,6 +1826,8 @@ function resolveToken(flags, env, context, baseUrl, saved, options) {
|
|
|
1515
1826
|
return { token: explicitToken, tokenSource: "flag" };
|
|
1516
1827
|
if (!options.excludeTaskToken && env.MORULA_TASK_TOKEN)
|
|
1517
1828
|
return { token: env.MORULA_TASK_TOKEN, tokenSource: "env:MORULA_TASK_TOKEN" };
|
|
1829
|
+
if (!options.excludeTaskToken && env.MORULA_SESSION_TOKEN)
|
|
1830
|
+
return { token: env.MORULA_SESSION_TOKEN, tokenSource: "env:MORULA_SESSION_TOKEN" };
|
|
1518
1831
|
if (env.MORULA_TOKEN)
|
|
1519
1832
|
return { token: env.MORULA_TOKEN, tokenSource: "env:MORULA_TOKEN" };
|
|
1520
1833
|
const savedToken = saved?.url && normalizeBaseUrl(saved.url) === baseUrl ? saved.token : void 0;
|
|
@@ -1627,6 +1940,10 @@ async function run(argv, deps = {}) {
|
|
|
1627
1940
|
code: error.code,
|
|
1628
1941
|
message: error.message,
|
|
1629
1942
|
...error.retryable ? { retryable: true } : {},
|
|
1943
|
+
// 服务端业务错误码(如 SESSION_SCOPE_DENIED):`code` 是按 HTTP 状态派生的粗档位,
|
|
1944
|
+
// 行为契约要判断的是业务码——不打印出来,契约就永远不可执行
|
|
1945
|
+
// (issues/api-client-drops-business-error-code.md)。退出码仍由 `error.exitCode` 决定。
|
|
1946
|
+
...error instanceof MorulaApiError && error.errorCode ? { errorCode: error.errorCode } : {},
|
|
1630
1947
|
// 新增字段(可选):可执行的下一步 + typo 建议
|
|
1631
1948
|
...error instanceof CliError && error.hint ? { hint: error.hint } : {},
|
|
1632
1949
|
...error instanceof CliError && error.didYouMean ? { didYouMean: error.didYouMean } : {}
|
|
@@ -1838,6 +2155,13 @@ async function attachmentCommand(context) {
|
|
|
1838
2155
|
rejectUnknownFlags(flags, []);
|
|
1839
2156
|
return context.client.attachments.get(requireId(positionals, "attachment get"));
|
|
1840
2157
|
}
|
|
2158
|
+
if (action === "content") {
|
|
2159
|
+
rejectUnknownFlags(flags, ["max-chars"]);
|
|
2160
|
+
return context.client.attachments.content(
|
|
2161
|
+
requireId(positionals, "attachment content"),
|
|
2162
|
+
flagNumber(flags, "max-chars")
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
1841
2165
|
if (action === "download-url") {
|
|
1842
2166
|
rejectUnknownFlags(flags, []);
|
|
1843
2167
|
return context.client.attachments.getDownloadUrl(requireId(positionals, "attachment download-url"));
|
|
@@ -1855,6 +2179,198 @@ function requireId(positionals, command) {
|
|
|
1855
2179
|
return id;
|
|
1856
2180
|
}
|
|
1857
2181
|
|
|
2182
|
+
// ../morula-cli/src/commands/autopilot.ts
|
|
2183
|
+
async function autopilotCommand(context) {
|
|
2184
|
+
if (!context.runtime.token)
|
|
2185
|
+
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
2186
|
+
const { action, flags } = context.parsed;
|
|
2187
|
+
if (action === "list") {
|
|
2188
|
+
rejectUnknownFlags(flags, ["space", "page", "page-size"]);
|
|
2189
|
+
return context.client.autopilots.list({
|
|
2190
|
+
spaceId: flagString(flags, "space"),
|
|
2191
|
+
page: flagNumber(flags, "page"),
|
|
2192
|
+
pageSize: flagNumber(flags, "page-size")
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
if (action === "create") {
|
|
2196
|
+
rejectUnknownFlags(flags, ["space", "name", "runbook", "executor", "description", "cron", "vcs-connection"]);
|
|
2197
|
+
const spaceId = requireFlag(flags, "space");
|
|
2198
|
+
const name = requireFlag(flags, "name");
|
|
2199
|
+
const runbook = requireFlag(flags, "runbook");
|
|
2200
|
+
const executorKey = requireFlag(flags, "executor");
|
|
2201
|
+
const description = flagString(flags, "description");
|
|
2202
|
+
const cronExpr = flagString(flags, "cron");
|
|
2203
|
+
const vcsConnectionId = flagString(flags, "vcs-connection");
|
|
2204
|
+
return context.client.autopilots.create({
|
|
2205
|
+
spaceId,
|
|
2206
|
+
name,
|
|
2207
|
+
runbook,
|
|
2208
|
+
executorKey,
|
|
2209
|
+
...description !== void 0 ? { description } : {},
|
|
2210
|
+
...cronExpr !== void 0 ? { cronExpr } : {},
|
|
2211
|
+
...vcsConnectionId !== void 0 ? { vcsConnectionId } : {}
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
throw usage("usage: morula autopilot <list|create>");
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
// ../morula-cli/src/commands/ci.ts
|
|
2218
|
+
async function ciCommand(context) {
|
|
2219
|
+
if (!context.runtime.token)
|
|
2220
|
+
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
2221
|
+
const { action } = context.parsed;
|
|
2222
|
+
if (action === "status")
|
|
2223
|
+
return ciStatus(context);
|
|
2224
|
+
if (action === "log")
|
|
2225
|
+
return ciLog(context);
|
|
2226
|
+
if (action === "deployments")
|
|
2227
|
+
return ciDeployments(context);
|
|
2228
|
+
if (action === "test-report")
|
|
2229
|
+
return ciTestReport(context);
|
|
2230
|
+
if (action === "retry")
|
|
2231
|
+
return ciRetry(context);
|
|
2232
|
+
throw usage("usage: morula ci <status|log|deployments|test-report|retry>");
|
|
2233
|
+
}
|
|
2234
|
+
async function ciStatus(context) {
|
|
2235
|
+
const { positionals, flags } = context.parsed;
|
|
2236
|
+
rejectUnknownFlags(flags, ["space", "repo", "ref", "failed", "limit"]);
|
|
2237
|
+
const repo = flagString(flags, "repo");
|
|
2238
|
+
const ref = flagString(flags, "ref");
|
|
2239
|
+
const failed = flagBoolean(flags, "failed");
|
|
2240
|
+
const limit = flagNumber(flags, "limit");
|
|
2241
|
+
if (limit != null && (!Number.isInteger(limit) || limit <= 0))
|
|
2242
|
+
throw usage("--limit \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
2243
|
+
const face = resolveFace(context, "usage: morula ci status <taskId> [--repo <owner/name>] [--limit <n>] | morula ci status --space <spaceId> [--ref <branch>] [--failed] [--limit <n>]");
|
|
2244
|
+
if (face.kind === "space") {
|
|
2245
|
+
if (repo)
|
|
2246
|
+
throw usage("--repo \u53EA\u5728\u4EFB\u52A1\u9762\u53EF\u7528\uFF1A\u7A7A\u95F4\u9762\u8BF7\u7528 --ref \u8FC7\u6EE4\u5206\u652F\uFF08\u670D\u52A1\u7AEF\u4E0D\u652F\u6301\u6309\u4ED3\u5E93\u8FC7\u6EE4\uFF09");
|
|
2247
|
+
const result2 = await context.client.ci.spaceView(face.spaceId, {
|
|
2248
|
+
...ref ? { ref } : {},
|
|
2249
|
+
...failed ? { failed: true } : {},
|
|
2250
|
+
...limit ? { limit } : {}
|
|
2251
|
+
});
|
|
2252
|
+
if (result2.ok === false)
|
|
2253
|
+
throw ciFailure(result2);
|
|
2254
|
+
return result2;
|
|
2255
|
+
}
|
|
2256
|
+
const result = await context.client.ci.taskView(face.taskId, { ...limit ? { limit } : {} });
|
|
2257
|
+
if (result.ok === false)
|
|
2258
|
+
throw ciFailure(result);
|
|
2259
|
+
if (!repo)
|
|
2260
|
+
return result;
|
|
2261
|
+
const runs = result.runs.filter((run2) => run2.repoFullName === repo);
|
|
2262
|
+
return { ...result, runs, repoFilter: { repo, total: result.runs.length, matched: runs.length } };
|
|
2263
|
+
}
|
|
2264
|
+
async function ciLog(context) {
|
|
2265
|
+
const { flags } = context.parsed;
|
|
2266
|
+
rejectUnknownFlags(flags, ["space", "job", "tail"]);
|
|
2267
|
+
const jobId = flagString(flags, "job");
|
|
2268
|
+
if (!jobId)
|
|
2269
|
+
throw usage("usage: morula ci log <taskId> --job <jobId> [--tail <n>] | morula ci log --space <spaceId> --job <jobId> [--tail <n>]");
|
|
2270
|
+
const tail = flagNumber(flags, "tail");
|
|
2271
|
+
if (tail != null && (!Number.isInteger(tail) || tail <= 0))
|
|
2272
|
+
throw usage("--tail \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08\u4FDD\u7559\u7684\u65E5\u5FD7\u5C3E\u90E8\u5B57\u8282\u6570\uFF09");
|
|
2273
|
+
resolveFace(context, "usage: morula ci log <taskId> --job <jobId> [--tail <n>] | morula ci log --space <spaceId> --job <jobId> [--tail <n>]");
|
|
2274
|
+
const result = await context.client.ci.jobLog(jobId, { ...tail ? { tail } : {} });
|
|
2275
|
+
if (result.ok === false)
|
|
2276
|
+
throw ciFailure(result);
|
|
2277
|
+
return result;
|
|
2278
|
+
}
|
|
2279
|
+
async function ciDeployments(context) {
|
|
2280
|
+
const { flags } = context.parsed;
|
|
2281
|
+
rejectUnknownFlags(flags, ["space"]);
|
|
2282
|
+
const face = resolveFace(context, "usage: morula ci deployments <taskId> | morula ci deployments --space <spaceId>");
|
|
2283
|
+
const result = face.kind === "space" ? await context.client.ci.spaceDeployments(face.spaceId) : await context.client.ci.taskDeployments(face.taskId);
|
|
2284
|
+
if (result.ok === false)
|
|
2285
|
+
throw ciFailure(result);
|
|
2286
|
+
return result;
|
|
2287
|
+
}
|
|
2288
|
+
async function ciTestReport(context) {
|
|
2289
|
+
const { flags } = context.parsed;
|
|
2290
|
+
rejectUnknownFlags(flags, ["space", "run"]);
|
|
2291
|
+
const runFlag = flagString(flags, "run");
|
|
2292
|
+
const face = resolveFace(context, "usage: morula ci test-report <taskId> [--run <runId>] | morula ci test-report --space <spaceId> [--run <runId>]");
|
|
2293
|
+
let runId = runFlag;
|
|
2294
|
+
if (!runId) {
|
|
2295
|
+
const view = face.kind === "space" ? await context.client.ci.spaceView(face.spaceId) : await context.client.ci.taskView(face.taskId);
|
|
2296
|
+
if (view.ok === false)
|
|
2297
|
+
throw ciFailure(view);
|
|
2298
|
+
runId = view.runs[0]?.id;
|
|
2299
|
+
if (!runId) {
|
|
2300
|
+
throw new CliError(
|
|
2301
|
+
"CI_RUN_NOT_FOUND",
|
|
2302
|
+
EXIT.NOT_FOUND,
|
|
2303
|
+
face.kind === "space" ? "\u8BE5\u7A7A\u95F4\u6CA1\u6709\u6D41\u6C34\u7EBF\u8FD0\u884C\u8BB0\u5F55\uFF0C\u53D6\u4E0D\u5230\u5931\u8D25\u7528\u4F8B\u6E05\u5355\uFF08\u53EF\u5148\u7528 morula ci status --space <id> \u786E\u8BA4\uFF09" : "\u672C\u4EFB\u52A1\u6CA1\u6709\u6D41\u6C34\u7EBF\u8FD0\u884C\u8BB0\u5F55\uFF0C\u53D6\u4E0D\u5230\u5931\u8D25\u7528\u4F8B\u6E05\u5355\uFF08\u53EF\u5148\u7528 morula ci status <taskId> \u786E\u8BA4\uFF09"
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
const result = await context.client.ci.testReport(runId);
|
|
2308
|
+
if (result.ok === false)
|
|
2309
|
+
throw ciFailure(result);
|
|
2310
|
+
return result;
|
|
2311
|
+
}
|
|
2312
|
+
async function ciRetry(context) {
|
|
2313
|
+
const { positionals, flags } = context.parsed;
|
|
2314
|
+
rejectUnknownFlags(flags, ["space", "run", "job"]);
|
|
2315
|
+
const usageHint = "usage: morula ci retry --space <spaceId> [--run <runId>] [--job <jobId>]";
|
|
2316
|
+
if (positionals.length)
|
|
2317
|
+
throw usage(`\u91CD\u8DD1\u53EA\u5728\u7A7A\u95F4\u9762\u5F00\u653E\uFF1A\u8BF7\u7528 --space <spaceId>\uFF08\u4EFB\u52A1\u9762\u4E0D\u63D0\u4F9B retry\uFF09\u3002${usageHint}`);
|
|
2318
|
+
const runId = flagString(flags, "run");
|
|
2319
|
+
const jobId = flagString(flags, "job");
|
|
2320
|
+
if (runId && jobId)
|
|
2321
|
+
throw usage(`--run \u4E0E --job \u4E92\u65A5\uFF0C\u53EA\u80FD\u7ED9\u4E00\u4E2A\uFF08${usageHint}\uFF09`);
|
|
2322
|
+
const spaceId = flagString(flags, "space");
|
|
2323
|
+
if (!spaceId)
|
|
2324
|
+
throw usage(usageHint);
|
|
2325
|
+
if (jobId) {
|
|
2326
|
+
const result2 = await context.client.ci.retryJob(jobId);
|
|
2327
|
+
if (result2.ok === false)
|
|
2328
|
+
throw ciFailure(result2, "\u64CD\u4F5C");
|
|
2329
|
+
return result2;
|
|
2330
|
+
}
|
|
2331
|
+
let targetRunId = runId;
|
|
2332
|
+
if (!targetRunId) {
|
|
2333
|
+
const view = await context.client.ci.spaceView(spaceId, { failed: true });
|
|
2334
|
+
if (view.ok === false)
|
|
2335
|
+
throw ciFailure(view);
|
|
2336
|
+
targetRunId = view.runs[0]?.id;
|
|
2337
|
+
if (!targetRunId) {
|
|
2338
|
+
throw new CliError(
|
|
2339
|
+
"CI_RUN_NOT_FOUND",
|
|
2340
|
+
EXIT.NOT_FOUND,
|
|
2341
|
+
`\u7A7A\u95F4 ${spaceId} \u6CA1\u6709\u5931\u8D25\u7684\u6D41\u6C34\u7EBF\u8FD0\u884C\u53EF\u91CD\u8DD1\uFF08\u53EF\u5148\u7528 morula ci status --space ${spaceId} --failed \u786E\u8BA4\uFF09`
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
const result = await context.client.ci.retryRun(targetRunId);
|
|
2346
|
+
if (result.ok === false)
|
|
2347
|
+
throw ciFailure(result, "\u64CD\u4F5C");
|
|
2348
|
+
return result;
|
|
2349
|
+
}
|
|
2350
|
+
function resolveFace(context, usageHint) {
|
|
2351
|
+
const taskId = context.parsed.positionals[0]?.trim();
|
|
2352
|
+
const spaceId = flagString(context.parsed.flags, "space");
|
|
2353
|
+
if (taskId && spaceId)
|
|
2354
|
+
throw usage(`<taskId> \u4E0E --space \u4E92\u65A5\uFF0C\u53EA\u80FD\u7ED9\u4E00\u4E2A\uFF08${usageHint}\uFF09`);
|
|
2355
|
+
if (!taskId && !spaceId)
|
|
2356
|
+
throw usage(usageHint);
|
|
2357
|
+
return taskId ? { kind: "task", taskId } : { kind: "space", spaceId };
|
|
2358
|
+
}
|
|
2359
|
+
function ciFailure(failure, verb = "\u67E5\u8BE2") {
|
|
2360
|
+
return new CliError(
|
|
2361
|
+
failure.code,
|
|
2362
|
+
exitForCiCode(failure.code),
|
|
2363
|
+
`CI ${verb}\u5931\u8D25: ${failure.message || "\u672A\u77E5\u539F\u56E0"}\uFF08${failure.code}\uFF09`
|
|
2364
|
+
);
|
|
2365
|
+
}
|
|
2366
|
+
function exitForCiCode(code) {
|
|
2367
|
+
if (code === "CI_RETRY_NOT_ALLOWED" || code === "CI_JOB_NOT_MANUAL")
|
|
2368
|
+
return EXIT.CONFLICT;
|
|
2369
|
+
if (code === "CI_JOB_NOT_FOUND" || code === "CI_RUN_NOT_FOUND" || code === "TASK_NOT_FOUND" || code === "OBJECT_NOT_FOUND")
|
|
2370
|
+
return EXIT.NOT_FOUND;
|
|
2371
|
+
return EXIT.OTHER;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
1858
2374
|
// ../morula-cli/src/commands/comment.ts
|
|
1859
2375
|
function detectCommentFormat(text) {
|
|
1860
2376
|
if (!text)
|
|
@@ -1888,16 +2404,19 @@ async function commentCommand(context) {
|
|
|
1888
2404
|
return context.client.comments.get(id);
|
|
1889
2405
|
}
|
|
1890
2406
|
if (action === "add") {
|
|
1891
|
-
rejectUnknownFlags(flags, ["object", "content", "parent", "format"]);
|
|
2407
|
+
rejectUnknownFlags(flags, ["object", "content", "parent", "format", "mention"]);
|
|
1892
2408
|
const objectId = requireFlag(flags, "object");
|
|
1893
2409
|
const content = requireFlag(flags, "content");
|
|
1894
2410
|
const formatFlag = flagString(flags, "format");
|
|
1895
2411
|
const format = formatFlag === "markdown" || formatFlag === "plain" || formatFlag === "richtext" ? formatFlag : detectCommentFormat(content);
|
|
2412
|
+
const mentions = parseMentions(flagStrings(context.parsed, "mention"));
|
|
1896
2413
|
const input = compact({
|
|
1897
2414
|
objectId,
|
|
1898
2415
|
content,
|
|
1899
2416
|
parentCommentId: flagString(flags, "parent"),
|
|
1900
|
-
contentFormat: format
|
|
2417
|
+
contentFormat: format,
|
|
2418
|
+
// 无 --mention 时**不带** mentions 字段(服务端按缺省处理,不发任何通知)
|
|
2419
|
+
...mentions.length ? { mentions } : {}
|
|
1901
2420
|
});
|
|
1902
2421
|
return context.client.comments.create(input);
|
|
1903
2422
|
}
|
|
@@ -1923,6 +2442,89 @@ async function commentCommand(context) {
|
|
|
1923
2442
|
function compact(value) {
|
|
1924
2443
|
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
|
|
1925
2444
|
}
|
|
2445
|
+
function parseMentions(rawValues) {
|
|
2446
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2447
|
+
const mentions = [];
|
|
2448
|
+
for (const raw of rawValues) {
|
|
2449
|
+
for (const part of raw.split(",")) {
|
|
2450
|
+
const principalId = part.trim();
|
|
2451
|
+
if (!principalId || seen.has(principalId))
|
|
2452
|
+
continue;
|
|
2453
|
+
seen.add(principalId);
|
|
2454
|
+
mentions.push({ type: "member", principalId });
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
return mentions;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
// ../morula-cli/src/commands/dashboard.ts
|
|
2461
|
+
async function dashboardCommand(context) {
|
|
2462
|
+
if (!context.runtime.token)
|
|
2463
|
+
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
2464
|
+
const { action, flags } = context.parsed;
|
|
2465
|
+
if (action === "get") {
|
|
2466
|
+
rejectUnknownFlags(flags, ["space"]);
|
|
2467
|
+
return context.client.dashboard.get(requireFlag(flags, "space"));
|
|
2468
|
+
}
|
|
2469
|
+
if (action === "statistics") {
|
|
2470
|
+
rejectUnknownFlags(flags, ["space"]);
|
|
2471
|
+
return context.client.dashboard.statistics(requireFlag(flags, "space"));
|
|
2472
|
+
}
|
|
2473
|
+
if (action === "workload") {
|
|
2474
|
+
rejectUnknownFlags(flags, ["space"]);
|
|
2475
|
+
return context.client.dashboard.workload(requireFlag(flags, "space"));
|
|
2476
|
+
}
|
|
2477
|
+
throw usage("usage: morula dashboard <get|statistics|workload>");
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
// ../morula-cli/src/commands/document.ts
|
|
2481
|
+
async function documentCommand(context) {
|
|
2482
|
+
if (!context.runtime.token)
|
|
2483
|
+
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
2484
|
+
const { action, positionals, flags } = context.parsed;
|
|
2485
|
+
if (action === "list") {
|
|
2486
|
+
rejectUnknownFlags(flags, ["page", "page-size"]);
|
|
2487
|
+
return context.client.documents.list({
|
|
2488
|
+
page: flagNumber(flags, "page"),
|
|
2489
|
+
pageSize: flagNumber(flags, "page-size")
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
if (action === "get") {
|
|
2493
|
+
rejectUnknownFlags(flags, []);
|
|
2494
|
+
return context.client.documents.get(requireId2(positionals, "document get"));
|
|
2495
|
+
}
|
|
2496
|
+
if (action === "artifacts") {
|
|
2497
|
+
rejectUnknownFlags(flags, []);
|
|
2498
|
+
return context.client.documents.artifacts(requireId2(positionals, "document artifacts"));
|
|
2499
|
+
}
|
|
2500
|
+
throw usage("usage: morula document <list|get|artifacts>");
|
|
2501
|
+
}
|
|
2502
|
+
function requireId2(positionals, command) {
|
|
2503
|
+
const id = positionals[0]?.trim();
|
|
2504
|
+
if (!id)
|
|
2505
|
+
throw usage(`usage: morula ${command} <id>`);
|
|
2506
|
+
return id;
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
// ../morula-cli/src/commands/object-meta.ts
|
|
2510
|
+
async function objectMetaCommand(context) {
|
|
2511
|
+
if (!context.runtime.token)
|
|
2512
|
+
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
2513
|
+
const { action, flags } = context.parsed;
|
|
2514
|
+
if (action === "get") {
|
|
2515
|
+
rejectUnknownFlags(flags, ["object"]);
|
|
2516
|
+
return context.client.objectMeta.list(requireFlag(flags, "object"));
|
|
2517
|
+
}
|
|
2518
|
+
if (action === "set") {
|
|
2519
|
+
rejectUnknownFlags(flags, ["object", "key", "value"]);
|
|
2520
|
+
return context.client.objectMeta.set(
|
|
2521
|
+
requireFlag(flags, "object"),
|
|
2522
|
+
requireFlag(flags, "key"),
|
|
2523
|
+
requireFlag(flags, "value")
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
throw usage("usage: morula object-meta <get|set>");
|
|
2527
|
+
}
|
|
1926
2528
|
|
|
1927
2529
|
// ../morula-cli/src/commands/repo.ts
|
|
1928
2530
|
import { execFileSync } from "node:child_process";
|
|
@@ -2000,7 +2602,30 @@ async function repoCommand(context) {
|
|
|
2000
2602
|
if (action === "mr" && positionals[0] === "diff") {
|
|
2001
2603
|
return mrDiff(context, flags);
|
|
2002
2604
|
}
|
|
2003
|
-
|
|
2605
|
+
if (action === "files") {
|
|
2606
|
+
rejectUnknownFlags(flags, ["space", "repo", "path", "ref"]);
|
|
2607
|
+
const spaceId = flagString(flags, "space");
|
|
2608
|
+
if (!spaceId)
|
|
2609
|
+
throw usage("usage: morula repo files --space <id> [--repo <owner/name>] [--path <p>] [--ref <r>]");
|
|
2610
|
+
return context.client.repos.files(spaceId, {
|
|
2611
|
+
repo: flagString(flags, "repo"),
|
|
2612
|
+
path: flagString(flags, "path"),
|
|
2613
|
+
ref: flagString(flags, "ref")
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
if (action === "read") {
|
|
2617
|
+
rejectUnknownFlags(flags, ["space", "repo", "path", "ref"]);
|
|
2618
|
+
const spaceId = flagString(flags, "space");
|
|
2619
|
+
const path = flagString(flags, "path");
|
|
2620
|
+
if (!spaceId || !path)
|
|
2621
|
+
throw usage("usage: morula repo read --space <id> --path <p> [--repo <owner/name>] [--ref <r>]");
|
|
2622
|
+
return context.client.repos.read(spaceId, {
|
|
2623
|
+
path,
|
|
2624
|
+
repo: flagString(flags, "repo"),
|
|
2625
|
+
ref: flagString(flags, "ref")
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
throw usage("usage: morula repo <list|checkout|prepare|files|read|mr create|mr list|mr comment|mr get|mr diff>");
|
|
2004
2629
|
}
|
|
2005
2630
|
function resolveMrBase(flagBase, env = process.env) {
|
|
2006
2631
|
return env.MORULA_BASE_BRANCH?.trim() || flagBase?.trim() || "main";
|
|
@@ -2114,7 +2739,7 @@ async function mrCreateViaPushOptions(input) {
|
|
|
2114
2739
|
const list = listRes.ok ? await listRes.json().catch(() => null) : null;
|
|
2115
2740
|
const existing = Array.isArray(list) ? list[0] : null;
|
|
2116
2741
|
if (existing?.iid) {
|
|
2117
|
-
|
|
2742
|
+
process.stderr.write("[morula-cli] mr create \u5206\u652F\u5DF2\u6709 opened MR\uFF0C\u590D\u7528\u73B0\u6709 MR\n");
|
|
2118
2743
|
return { ok: true, repoFullName, branch, base, taskCode, title, mrUrl: existing.web_url ?? `${gitlabBase}/${repoFullName}/-/merge_requests/${existing.iid}` };
|
|
2119
2744
|
}
|
|
2120
2745
|
if (listRes.ok) {
|
|
@@ -2499,7 +3124,901 @@ async function spaceCommand(context) {
|
|
|
2499
3124
|
throw usage("usage: morula space get <id>");
|
|
2500
3125
|
return context.client.spaces.get(id);
|
|
2501
3126
|
}
|
|
2502
|
-
|
|
3127
|
+
if (action === "create") {
|
|
3128
|
+
rejectUnknownFlags(flags, ["name", "description", "code", "visibility"]);
|
|
3129
|
+
return context.client.spaces.create({
|
|
3130
|
+
name: requireFlag(flags, "name"),
|
|
3131
|
+
description: flagString(flags, "description"),
|
|
3132
|
+
code: flagString(flags, "code"),
|
|
3133
|
+
visibility: flagString(flags, "visibility")
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
if (action === "update") {
|
|
3137
|
+
rejectUnknownFlags(flags, ["name", "description", "visibility"]);
|
|
3138
|
+
const id = requireId3(positionals, "space update");
|
|
3139
|
+
const input = {
|
|
3140
|
+
name: flagString(flags, "name"),
|
|
3141
|
+
description: flagString(flags, "description"),
|
|
3142
|
+
visibility: flagString(flags, "visibility")
|
|
3143
|
+
};
|
|
3144
|
+
if (Object.values(input).every((value) => value === void 0))
|
|
3145
|
+
throw usage("space update \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u5B57\u6BB5\u65D7\u6807\uFF08--name / --description / --visibility\uFF09");
|
|
3146
|
+
return context.client.spaces.update(id, input);
|
|
3147
|
+
}
|
|
3148
|
+
if (action === "delete") {
|
|
3149
|
+
rejectUnknownFlags(flags, []);
|
|
3150
|
+
return context.client.spaces.delete(requireId3(positionals, "space delete"));
|
|
3151
|
+
}
|
|
3152
|
+
throw usage("usage: morula space <list|get|create|update|delete>");
|
|
3153
|
+
}
|
|
3154
|
+
function requireId3(positionals, command) {
|
|
3155
|
+
const id = positionals[0]?.trim();
|
|
3156
|
+
if (!id)
|
|
3157
|
+
throw usage(`usage: morula ${command} <id>`);
|
|
3158
|
+
return id;
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
// ../morula-cli/src/commands/spec.ts
|
|
3162
|
+
var SPEC = {
|
|
3163
|
+
name: "morula",
|
|
3164
|
+
summary: "Agent-first CLI for Morula task management (plugin surface, injected into task workspaces). Success output is JSON on stdout; errors are JSON on stderr.",
|
|
3165
|
+
resources: [
|
|
3166
|
+
{
|
|
3167
|
+
resource: "auth",
|
|
3168
|
+
summary: "SkyDoor authentication (shared config with morula-runtime)",
|
|
3169
|
+
actions: [
|
|
3170
|
+
{
|
|
3171
|
+
action: "login",
|
|
3172
|
+
summary: "browser SkyDoor SSO using PKCE and loopback callback (writes ~/.morula-cli/config.yaml)",
|
|
3173
|
+
flags: [{ name: "url", type: "string", valueHint: "<baseUrl>", description: "server base URL to log in to" }],
|
|
3174
|
+
examples: ["morula auth login"],
|
|
3175
|
+
writes: "local",
|
|
3176
|
+
interactive: true,
|
|
3177
|
+
emits: ["loggedIn", "context", "baseUrl", "expiresIn"]
|
|
3178
|
+
},
|
|
3179
|
+
{
|
|
3180
|
+
action: "logout",
|
|
3181
|
+
summary: "remove the current context token",
|
|
3182
|
+
flags: [],
|
|
3183
|
+
examples: ["morula auth logout"],
|
|
3184
|
+
writes: "local",
|
|
3185
|
+
idempotent: true,
|
|
3186
|
+
emits: ["loggedIn", "context"]
|
|
3187
|
+
},
|
|
3188
|
+
{
|
|
3189
|
+
action: "status",
|
|
3190
|
+
summary: "show context, endpoint, and authentication state",
|
|
3191
|
+
flags: [],
|
|
3192
|
+
examples: ["morula auth status"],
|
|
3193
|
+
writes: "none",
|
|
3194
|
+
idempotent: true,
|
|
3195
|
+
emits: ["loggedIn", "context", "baseUrl", "user", "expired"]
|
|
3196
|
+
}
|
|
3197
|
+
]
|
|
3198
|
+
},
|
|
3199
|
+
{
|
|
3200
|
+
resource: "task",
|
|
3201
|
+
summary: "task (business Object) read/write + AgentTask machine protocol (branch/state/analysis-verdict/inbox)",
|
|
3202
|
+
actions: [
|
|
3203
|
+
{
|
|
3204
|
+
action: "list",
|
|
3205
|
+
summary: "list tasks (--mine: \u4EC5\u6211\u8D1F\u8D23\u7684\uFF1B--field key=value \u81EA\u5B9A\u4E49\u5B57\u6BB5\u7B5B\u9009\uFF0C\u9700 --space)",
|
|
3206
|
+
flags: [
|
|
3207
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "space id" },
|
|
3208
|
+
{ name: "mine", type: "boolean", description: "\u4EC5\u6211\u8D1F\u8D23\u7684\uFF08\u4E0E --assignee \u4E92\u65A5\uFF09" },
|
|
3209
|
+
{ name: "assignee", type: "string", valueHint: "<principalId>", description: "\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4" },
|
|
3210
|
+
{ name: "principal", type: "string", valueHint: "<id>", description: "\u6309\u521B\u5EFA\u4EBA\u8FC7\u6EE4" },
|
|
3211
|
+
{ name: "page", type: "number", valueHint: "<n>", description: "\u9875\u7801" },
|
|
3212
|
+
{ name: "page-size", type: "number", valueHint: "<n>", description: "\u6BCF\u9875\u6761\u6570" },
|
|
3213
|
+
{ name: "search", type: "string", valueHint: "<text>", description: "\u5173\u952E\u8BCD\u641C\u7D22" },
|
|
3214
|
+
{ name: "priority", type: "string", valueHint: "<low|medium|high|urgent>", description: "\u4F18\u5148\u7EA7\u8FC7\u6EE4" },
|
|
3215
|
+
{ name: "state", type: "string", valueHint: "<id>", description: "\u72B6\u6001\u8FC7\u6EE4" },
|
|
3216
|
+
{ name: "sort", type: "string", valueHint: "<field>", description: "\u6392\u5E8F\u5B57\u6BB5" },
|
|
3217
|
+
{ name: "order", type: "string", valueHint: "<asc|desc>", description: "\u6392\u5E8F\u65B9\u5411" },
|
|
3218
|
+
{ name: "field", type: "string", valueHint: "key=value[,key=value]", description: "\u81EA\u5B9A\u4E49\u5B57\u6BB5\u7B49\u503C\u7B5B\u9009\uFF08\u591A\u6761\u4EF6 AND\uFF1B\u9700 --space \u89E3\u6790\u5B57\u6BB5\u6A21\u677F\uFF09" }
|
|
3219
|
+
],
|
|
3220
|
+
examples: ["morula task list --space <id>", "morula task list --mine", "morula task list --space <id> --field priority=p1"],
|
|
3221
|
+
writes: "none",
|
|
3222
|
+
idempotent: true
|
|
3223
|
+
},
|
|
3224
|
+
{
|
|
3225
|
+
action: "create",
|
|
3226
|
+
summary: "create task (\u5FC5\u586B --space/--title\uFF1B\u843D backlog \u7531\u670D\u52A1\u7AEF\u51B3\u5B9A\u521D\u59CB\u72B6\u6001)",
|
|
3227
|
+
flags: [
|
|
3228
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3229
|
+
{ name: "title", type: "string", valueHint: "<text>", description: "\u6807\u9898\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3230
|
+
{ name: "description", type: "string", valueHint: "<text>", description: "\u63CF\u8FF0\uFF08\u683C\u5F0F\u81EA\u52A8\u68C0\u6D4B\uFF09" },
|
|
3231
|
+
{ name: "priority", type: "string", valueHint: "<low|medium|high|urgent>", description: "\u4F18\u5148\u7EA7" },
|
|
3232
|
+
{ name: "state", type: "string", valueHint: "<id>", description: "\u521D\u59CB\u72B6\u6001\uFF08\u4E1A\u52A1 Object state id\uFF09" },
|
|
3233
|
+
{ name: "owner", type: "string", valueHint: "<principalId>", description: "\u8D1F\u8D23\u4EBA\uFF08\u4EC5\u521B\u5EFA\u65F6\uFF09" },
|
|
3234
|
+
{ name: "assignee", type: "string", valueHint: "<principalId>", description: "\u7ECF\u529E\u4EBA" },
|
|
3235
|
+
{ name: "visibility", type: "string", valueHint: "<value>", description: "\u53EF\u89C1\u6027" },
|
|
3236
|
+
{ name: "estimate-hours", type: "number", valueHint: "<n>", description: "\u9884\u4F30\u5DE5\u65F6" },
|
|
3237
|
+
{ name: "schema", type: "string", valueHint: "<id>", description: "\u5B57\u6BB5\u6A21\u677F" },
|
|
3238
|
+
{ name: "sort-order", type: "number", valueHint: "<n>", description: "\u6392\u5E8F\u53F7" },
|
|
3239
|
+
{ name: "field", type: "string", valueHint: "key=value[,key=value]", description: "\u81EA\u5B9A\u4E49\u5B57\u6BB5 upsert\uFF08\u521B\u5EFA\u540E\u5199\u5165\uFF09" }
|
|
3240
|
+
],
|
|
3241
|
+
examples: ["morula task create --space <id> --title <text>"],
|
|
3242
|
+
writes: "remote"
|
|
3243
|
+
},
|
|
3244
|
+
{
|
|
3245
|
+
action: "get",
|
|
3246
|
+
summary: "get task details. No argument = the current AgentTask (id resolved from MORULA_TASK_ID / .morula/checkout.json). With an id = the business Object (title/description)",
|
|
3247
|
+
args: [{ name: "objectId", required: false, description: "\u4E1A\u52A1\u4EFB\u52A1\uFF08Object\uFF09ID\uFF1B\u7701\u7565 = \u5F53\u524D AgentTask \u8BE6\u60C5" }],
|
|
3248
|
+
flags: [],
|
|
3249
|
+
examples: ["morula task get", "morula task get <objectId>"],
|
|
3250
|
+
writes: "none",
|
|
3251
|
+
idempotent: true
|
|
3252
|
+
},
|
|
3253
|
+
{
|
|
3254
|
+
action: "update",
|
|
3255
|
+
summary: "update task (--state here is the business Object state id, NOT the AgentTask state code \u2014 see `task state`)",
|
|
3256
|
+
args: [{ name: "id", required: true, description: "\u4EFB\u52A1\uFF08Object\uFF09ID" }],
|
|
3257
|
+
flags: [
|
|
3258
|
+
{ name: "title", type: "string", valueHint: "<text>", description: "\u6807\u9898" },
|
|
3259
|
+
{ name: "description", type: "string", valueHint: "<text>", description: "\u63CF\u8FF0" },
|
|
3260
|
+
{ name: "priority", type: "string", valueHint: "<low|medium|high|urgent>", description: "\u4F18\u5148\u7EA7" },
|
|
3261
|
+
{ name: "state", type: "string", valueHint: "<id>", description: "\u72B6\u6001\uFF08\u4E1A\u52A1 Object state id\uFF09" },
|
|
3262
|
+
{ name: "assignee", type: "string", valueHint: "<principalId>", description: "\u7ECF\u529E\u4EBA" },
|
|
3263
|
+
{ name: "visibility", type: "string", valueHint: "<value>", description: "\u53EF\u89C1\u6027" },
|
|
3264
|
+
{ name: "estimate-hours", type: "number", valueHint: "<n>", description: "\u9884\u4F30\u5DE5\u65F6" },
|
|
3265
|
+
{ name: "schema", type: "string", valueHint: "<id>", description: "\u5B57\u6BB5\u6A21\u677F" },
|
|
3266
|
+
{ name: "sort-order", type: "number", valueHint: "<n>", description: "\u6392\u5E8F\u53F7" },
|
|
3267
|
+
{ name: "field", type: "string", valueHint: "key=value[,key=value]", description: "\u81EA\u5B9A\u4E49\u5B57\u6BB5 upsert\uFF08\u7A7A value = \u6E05\u7A7A\uFF09" }
|
|
3268
|
+
],
|
|
3269
|
+
examples: ["morula task update <id> --title <text>"],
|
|
3270
|
+
writes: "remote"
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
action: "delete",
|
|
3274
|
+
summary: "hard delete task",
|
|
3275
|
+
args: [{ name: "id", required: true, description: "\u4EFB\u52A1\uFF08Object\uFF09ID" }],
|
|
3276
|
+
flags: [],
|
|
3277
|
+
examples: ["morula task delete <id>"],
|
|
3278
|
+
writes: "remote"
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
action: "branch",
|
|
3282
|
+
summary: "report the final branch name for an agent task (machine protocol; agent naming, runtimeId from env; --repo: secondary repo in a multi-repo task)",
|
|
3283
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID\uFF08morula task get \u65E0\u53C2\u8FD4\u56DE\u7684 id\uFF09" }],
|
|
3284
|
+
flags: [
|
|
3285
|
+
{ name: "name", type: "string", valueHint: "<branch>", description: "\u5206\u652F\u540D\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3286
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u6B21\u4ED3\u5E93\uFF08\u591A\u4ED3\u5E93\u4EFB\u52A1\u7684\u5206\u652F\u9010\u4ED3\u4E0A\u62A5\uFF09" }
|
|
3287
|
+
],
|
|
3288
|
+
examples: ["morula task branch <taskId> --name <type>/<code>-<slug>"],
|
|
3289
|
+
writes: "remote"
|
|
3290
|
+
},
|
|
3291
|
+
{
|
|
3292
|
+
action: "state",
|
|
3293
|
+
summary: "self-transition task state (machine protocol; agent drives todo\u2192in_progress\u2192review, runtimeId from env)",
|
|
3294
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
|
|
3295
|
+
flags: [{ name: "state", type: "string", valueHint: "<in_progress|review>", description: "\u76EE\u6807\u72B6\u6001 code\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3296
|
+
examples: ["morula task state <taskId> --state in_progress"],
|
|
3297
|
+
writes: "remote"
|
|
3298
|
+
},
|
|
3299
|
+
{
|
|
3300
|
+
action: "analysis-verdict",
|
|
3301
|
+
summary: "report the task-analysis verdict (machine protocol; ready \u2192 platform auto-starts development, blocked \u2192 task stays in analysis)",
|
|
3302
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
|
|
3303
|
+
flags: [
|
|
3304
|
+
{ name: "verdict", type: "string", valueHint: "<ready|blocked>", enum: ["ready", "blocked"], description: "\u5206\u6790\u7ED3\u8BBA\uFF08\u5FC5\u586B\uFF1B\u4EC5\u4EFB\u52A1\u5206\u6790\u4EFB\u52A1\u53EF\u4E0A\u62A5\uFF09", required: true },
|
|
3305
|
+
{ name: "reason", type: "string", valueHint: "<text>", description: "blocked \u65F6\u7684\u963B\u585E/\u5F85\u6F84\u6E05\u539F\u56E0" }
|
|
3306
|
+
],
|
|
3307
|
+
examples: ["morula task analysis-verdict <taskId> --verdict ready"],
|
|
3308
|
+
writes: "remote"
|
|
3309
|
+
},
|
|
3310
|
+
{
|
|
3311
|
+
action: "inbox",
|
|
3312
|
+
summary: "checkpoint: pull this thread's unconsumed messages into the current turn (machine protocol; --peek reads without marking them consumed)",
|
|
3313
|
+
flags: [
|
|
3314
|
+
{ name: "peek", type: "boolean", description: "\u53EA\u8BFB\uFF0C\u4E0D\u6807\u8BB0\u6D88\u8D39" },
|
|
3315
|
+
{ name: "after-seq", type: "number", valueHint: "<n>", description: "\u4ECE\u8BE5 seq \u4E4B\u540E\u62C9\u53D6" }
|
|
3316
|
+
],
|
|
3317
|
+
examples: ["morula task inbox", "morula task inbox --peek"],
|
|
3318
|
+
writes: "remote"
|
|
3319
|
+
},
|
|
3320
|
+
{
|
|
3321
|
+
action: "agent-tasks",
|
|
3322
|
+
summary: "list the AgentTask executions of a business task (Object). AgentTask UUID \u2260 \u4E1A\u52A1\u4EFB\u52A1 id \u2014 cancel/rerun \u8981\u7684\u662F\u8FD9\u91CC\u7684 id",
|
|
3323
|
+
flags: [
|
|
3324
|
+
{ name: "object", type: "string", valueHint: "<objectId>", description: "\u4E1A\u52A1\u4EFB\u52A1\uFF08Object\uFF09ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3325
|
+
{ name: "status", type: "string", valueHint: "<queued|dispatched|running|awaiting_approval|completed|failed|cancelled>", description: "\u6309\u5355\u4E2A\u72B6\u6001\u8FC7\u6EE4\uFF08\u7F3A\u7701 = \u5168\u90E8\uFF1B\u670D\u52A1\u7AEF\u4E0D\u505A\u679A\u4E3E\u6821\u9A8C\uFF0C\u975E\u6CD5\u503C\u53EA\u8FD4\u56DE\u7A7A\uFF09" },
|
|
3326
|
+
{ name: "page", type: "number", valueHint: "<n>", description: "\u9875\u7801" },
|
|
3327
|
+
{ name: "page-size", type: "number", valueHint: "<n>", description: "\u6BCF\u9875\u6761\u6570\uFF08\u670D\u52A1\u7AEF\u7F3A\u7701 20\uFF09" }
|
|
3328
|
+
],
|
|
3329
|
+
examples: ["morula task agent-tasks --object <objectId>", "morula task agent-tasks --object <objectId> --status failed"],
|
|
3330
|
+
writes: "none",
|
|
3331
|
+
idempotent: true
|
|
3332
|
+
},
|
|
3333
|
+
{
|
|
3334
|
+
action: "cancel",
|
|
3335
|
+
summary: "cancel a running execution (POST /agent-tasks/:id/cancel). queued/dispatched/running/awaiting_approval/failed \u53EF\u53D6\u6D88\uFF1Bcompleted/cancelled \u4E0D\u53EF\uFF08409 CONFLICT\uFF09\uFF1B\u4EFB\u52A1\u4E0D\u5B58\u5728 404 NOT_FOUND\u3002\u6210\u529F\u8FD4\u56DE { ok: true }\u2014\u2014**\u975E\u96F6\u9000\u51FA\u5373\u672A\u53D6\u6D88**\uFF0C\u4E0D\u8981\u5411\u7528\u6237\u6C47\u62A5\u300C\u5DF2\u505C\u6389\u300D",
|
|
3336
|
+
args: [{ name: "taskId", required: true, description: "AgentTask UUID\uFF08\u6765\u81EA morula task agent-tasks\uFF09" }],
|
|
3337
|
+
flags: [],
|
|
3338
|
+
examples: ["morula task cancel <taskId>"],
|
|
3339
|
+
writes: "remote"
|
|
3340
|
+
},
|
|
3341
|
+
{
|
|
3342
|
+
action: "rerun",
|
|
3343
|
+
summary: "rerun a failed/cancelled execution (POST /agent-tasks/:id/rerun; copies the task and re-queues it \u2192 returns a NEW taskId). \u4EC5 failed/cancelled \u53EF\u91CD\u8DD1\uFF0C\u5176\u4ED6\u72B6\u6001 ok:false + code CONFLICT\uFF1B10s \u5185\u5DF2\u6709\u6D3B\u8DC3\u91CD\u8DD1\u4E3A ALREADY_QUEUED",
|
|
3344
|
+
args: [{ name: "taskId", required: true, description: "AgentTask UUID\uFF08\u6765\u81EA morula task agent-tasks\uFF09" }],
|
|
3345
|
+
flags: [],
|
|
3346
|
+
examples: ["morula task rerun <taskId>"],
|
|
3347
|
+
writes: "remote"
|
|
3348
|
+
}
|
|
3349
|
+
]
|
|
3350
|
+
},
|
|
3351
|
+
{
|
|
3352
|
+
resource: "space",
|
|
3353
|
+
summary: "space (project) read/write",
|
|
3354
|
+
actions: [
|
|
3355
|
+
{
|
|
3356
|
+
action: "list",
|
|
3357
|
+
summary: "list visible spaces",
|
|
3358
|
+
flags: [
|
|
3359
|
+
{ name: "page", type: "number", valueHint: "<n>", description: "\u9875\u7801" },
|
|
3360
|
+
{ name: "page-size", type: "number", valueHint: "<n>", description: "\u6BCF\u9875\u6761\u6570" },
|
|
3361
|
+
{ name: "keyword", type: "string", valueHint: "<text>", description: "\u5173\u952E\u8BCD" }
|
|
3362
|
+
],
|
|
3363
|
+
examples: ["morula space list"],
|
|
3364
|
+
writes: "none",
|
|
3365
|
+
idempotent: true
|
|
3366
|
+
},
|
|
3367
|
+
{
|
|
3368
|
+
action: "get",
|
|
3369
|
+
summary: "get space details",
|
|
3370
|
+
args: [{ name: "id", required: true, description: "\u7A7A\u95F4 ID" }],
|
|
3371
|
+
flags: [],
|
|
3372
|
+
examples: ["morula space get <id>"],
|
|
3373
|
+
writes: "none",
|
|
3374
|
+
idempotent: true
|
|
3375
|
+
},
|
|
3376
|
+
{
|
|
3377
|
+
action: "create",
|
|
3378
|
+
summary: "create space (project)",
|
|
3379
|
+
flags: [
|
|
3380
|
+
{ name: "name", type: "string", valueHint: "<text>", description: "\u7A7A\u95F4\u540D\u79F0\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3381
|
+
{ name: "description", type: "string", valueHint: "<text>", description: "\u63CF\u8FF0" },
|
|
3382
|
+
{ name: "code", type: "string", valueHint: "<code>", description: "\u7A7A\u95F4\u7F16\u53F7\uFF08\u7F3A\u7701\u7531\u670D\u52A1\u7AEF\u751F\u6210\uFF09" },
|
|
3383
|
+
{ name: "visibility", type: "string", valueHint: "<private|internal|public>", enum: ["private", "internal", "public"], description: "\u53EF\u89C1\u6027" }
|
|
3384
|
+
],
|
|
3385
|
+
examples: ["morula space create --name <text>"],
|
|
3386
|
+
writes: "remote"
|
|
3387
|
+
},
|
|
3388
|
+
{
|
|
3389
|
+
action: "update",
|
|
3390
|
+
summary: "update space (\u81F3\u5C11\u4E00\u4E2A\u5B57\u6BB5\u65D7\u6807)",
|
|
3391
|
+
args: [{ name: "id", required: true, description: "\u7A7A\u95F4 ID" }],
|
|
3392
|
+
flags: [
|
|
3393
|
+
{ name: "name", type: "string", valueHint: "<text>", description: "\u7A7A\u95F4\u540D\u79F0" },
|
|
3394
|
+
{ name: "description", type: "string", valueHint: "<text>", description: "\u63CF\u8FF0" },
|
|
3395
|
+
{ name: "visibility", type: "string", valueHint: "<private|internal|public>", enum: ["private", "internal", "public"], description: "\u53EF\u89C1\u6027" }
|
|
3396
|
+
],
|
|
3397
|
+
examples: ["morula space update <id> --name <text>"],
|
|
3398
|
+
writes: "remote"
|
|
3399
|
+
},
|
|
3400
|
+
{
|
|
3401
|
+
action: "delete",
|
|
3402
|
+
summary: "delete space",
|
|
3403
|
+
args: [{ name: "id", required: true, description: "\u7A7A\u95F4 ID" }],
|
|
3404
|
+
flags: [],
|
|
3405
|
+
examples: ["morula space delete <id>"],
|
|
3406
|
+
writes: "remote"
|
|
3407
|
+
}
|
|
3408
|
+
]
|
|
3409
|
+
},
|
|
3410
|
+
{
|
|
3411
|
+
resource: "space-member",
|
|
3412
|
+
summary: "space member management (admin operations)",
|
|
3413
|
+
actions: [
|
|
3414
|
+
{
|
|
3415
|
+
action: "list",
|
|
3416
|
+
summary: "list space members",
|
|
3417
|
+
flags: [{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3418
|
+
examples: ["morula space-member list --space <id>"],
|
|
3419
|
+
writes: "none",
|
|
3420
|
+
idempotent: true
|
|
3421
|
+
},
|
|
3422
|
+
{
|
|
3423
|
+
action: "add",
|
|
3424
|
+
summary: "add member to space",
|
|
3425
|
+
flags: [
|
|
3426
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3427
|
+
{ name: "principal", type: "string", valueHint: "<id>", description: "\u6210\u5458 principalId\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3428
|
+
{ name: "role", type: "string", valueHint: "<admin|member>", enum: ["admin", "member"], description: "\u89D2\u8272\uFF08\u7F3A\u7701 member\uFF09" }
|
|
3429
|
+
],
|
|
3430
|
+
examples: ["morula space-member add --space <id> --principal <id>"],
|
|
3431
|
+
writes: "remote"
|
|
3432
|
+
},
|
|
3433
|
+
{
|
|
3434
|
+
action: "update",
|
|
3435
|
+
summary: "update member role",
|
|
3436
|
+
args: [{ name: "memberId", required: true, description: "\u6210\u5458\u884C ID" }],
|
|
3437
|
+
flags: [{ name: "role", type: "string", valueHint: "<admin|member>", enum: ["admin", "member"], description: "\u65B0\u89D2\u8272\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3438
|
+
examples: ["morula space-member update <memberId> --role admin"],
|
|
3439
|
+
writes: "remote"
|
|
3440
|
+
},
|
|
3441
|
+
{
|
|
3442
|
+
action: "remove",
|
|
3443
|
+
summary: "remove member from space",
|
|
3444
|
+
args: [{ name: "memberId", required: true, description: "\u6210\u5458\u884C ID" }],
|
|
3445
|
+
flags: [],
|
|
3446
|
+
examples: ["morula space-member remove <memberId>"],
|
|
3447
|
+
writes: "remote"
|
|
3448
|
+
}
|
|
3449
|
+
]
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
resource: "steward",
|
|
3453
|
+
summary: "space steward (project-level agent): read takeover state / dispatch a task with an explicit target state",
|
|
3454
|
+
actions: [
|
|
3455
|
+
{
|
|
3456
|
+
action: "get",
|
|
3457
|
+
summary: "get the steward record of a space (null = \u672A\u63A5\u7BA1)",
|
|
3458
|
+
flags: [{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4\uFF08\u9879\u76EE\uFF09ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3459
|
+
examples: ["morula steward get --space <id>"],
|
|
3460
|
+
writes: "none",
|
|
3461
|
+
idempotent: true
|
|
3462
|
+
},
|
|
3463
|
+
{
|
|
3464
|
+
action: "dispatch",
|
|
3465
|
+
summary: "dispatch a task: create the task and hand it to the pipeline. --state backlog = \u843D\u5F85\u89C4\u5212\uFF08\u4E0D\u6D3E\u53D1\u6267\u884C\uFF0C\u670D\u52A1\u7AEF\u4E0D\u5EFA AgentTask\uFF09\uFF1B\u7F3A\u7701 todo\uFF08\u5F85\u5206\u6790\uFF09",
|
|
3466
|
+
flags: [
|
|
3467
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4\uFF08\u9879\u76EE\uFF09ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3468
|
+
{ name: "title", type: "string", valueHint: "<text>", description: "\u4EFB\u52A1\u6807\u9898\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3469
|
+
{ name: "description", type: "string", valueHint: "<text>", description: "\u4EFB\u52A1\u63CF\u8FF0" },
|
|
3470
|
+
{ name: "state", type: "string", valueHint: "<backlog|todo|in_progress>", enum: ["backlog", "todo", "in_progress"], description: "\u76EE\u6807\u521D\u59CB\u72B6\u6001\uFF08\u7F3A\u7701 todo\uFF09" },
|
|
3471
|
+
{ name: "assignee", type: "string", valueHint: "<principalId>", description: "\u6307\u5B9A\u6267\u884C\u8005" },
|
|
3472
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u591A\u4ED3\u5E93\u7A7A\u95F4\uFF1A\u6307\u5B9A\u76EE\u6807\u4ED3\u5E93\uFF08\u7F3A\u7701\u7531\u670D\u52A1\u7AEF\u89E3\u6790\uFF09" },
|
|
3473
|
+
{ name: "client-message-id", type: "string", valueHint: "<id>", description: "\u5E42\u7B49\u952E\uFF1A\u540C\u4E00\u952E\u91CD\u590D\u6D3E\u5355\u8FD4\u56DE\u539F\u7ED3\u679C" }
|
|
3474
|
+
],
|
|
3475
|
+
examples: ["morula steward dispatch --space <id> --title <text>", "morula steward dispatch --space <id> --title <text> --state backlog"],
|
|
3476
|
+
writes: "remote"
|
|
3477
|
+
}
|
|
3478
|
+
]
|
|
3479
|
+
},
|
|
3480
|
+
{
|
|
3481
|
+
resource: "autopilot",
|
|
3482
|
+
summary: "agent autopilots (scheduled/webhook-triggered automated runs; list only shows your own)",
|
|
3483
|
+
actions: [
|
|
3484
|
+
{
|
|
3485
|
+
action: "list",
|
|
3486
|
+
summary: "list autopilots created by the current user",
|
|
3487
|
+
flags: [
|
|
3488
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u6309\u7A7A\u95F4\u8FC7\u6EE4" },
|
|
3489
|
+
{ name: "page", type: "number", valueHint: "<n>", description: "\u9875\u7801" },
|
|
3490
|
+
{ name: "page-size", type: "number", valueHint: "<n>", description: "\u6BCF\u9875\u6761\u6570" }
|
|
3491
|
+
],
|
|
3492
|
+
examples: ["morula autopilot list", "morula autopilot list --space <id>"],
|
|
3493
|
+
writes: "none",
|
|
3494
|
+
idempotent: true
|
|
3495
|
+
},
|
|
3496
|
+
{
|
|
3497
|
+
action: "create",
|
|
3498
|
+
summary: "create an autopilot (--executor is a runtime key; the server currently only accepts `dim`)",
|
|
3499
|
+
flags: [
|
|
3500
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u5F52\u5C5E\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3501
|
+
{ name: "name", type: "string", valueHint: "<text>", description: "\u540D\u79F0\uFF08\u540C\u79DF\u6237\u552F\u4E00\uFF0C\u5FC5\u586B\uFF09", required: true },
|
|
3502
|
+
{ name: "runbook", type: "string", valueHint: "<text>", description: "Runbook\uFF1A\u6BCF\u6B21\u89E6\u53D1\u7ED9\u8FD0\u884C\u65F6\u7684\u76EE\u6807/\u80CC\u666F/\u6B65\u9AA4\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3503
|
+
{ name: "executor", type: "string", valueHint: "<key>", description: "\u6267\u884C\u8005\u8FD0\u884C\u65F6 key\uFF08\u5FC5\u586B\uFF1B\u5F53\u524D\u4EC5 dim\uFF09", required: true },
|
|
3504
|
+
{ name: "description", type: "string", valueHint: "<text>", description: "\u63CF\u8FF0" },
|
|
3505
|
+
{ name: "cron", type: "string", valueHint: "<expr>", description: "cron \u8868\u8FBE\u5F0F\uFF085 \u5B57\u6BB5\uFF09\uFF1B\u7F3A\u7701 = \u4EC5 webhook \u89E6\u53D1" },
|
|
3506
|
+
{ name: "vcs-connection", type: "string", valueHint: "<id>", description: "\u9650\u5B9A\u4ED3\u5E93\u8FDE\u63A5\uFF08\u7F3A\u7701\u4E0D\u9650\uFF09" }
|
|
3507
|
+
],
|
|
3508
|
+
examples: ["morula autopilot create --space <id> --name <text> --runbook <text> --executor dim"],
|
|
3509
|
+
writes: "remote"
|
|
3510
|
+
}
|
|
3511
|
+
]
|
|
3512
|
+
},
|
|
3513
|
+
{
|
|
3514
|
+
resource: "dashboard",
|
|
3515
|
+
summary: "space dashboard / statistics / member workload (read-only aggregates)",
|
|
3516
|
+
actions: [
|
|
3517
|
+
{
|
|
3518
|
+
action: "get",
|
|
3519
|
+
summary: "get the space dashboard aggregate",
|
|
3520
|
+
flags: [{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3521
|
+
examples: ["morula dashboard get --space <id>"],
|
|
3522
|
+
writes: "none",
|
|
3523
|
+
idempotent: true
|
|
3524
|
+
},
|
|
3525
|
+
{
|
|
3526
|
+
action: "statistics",
|
|
3527
|
+
summary: "get space task statistics",
|
|
3528
|
+
flags: [{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3529
|
+
examples: ["morula dashboard statistics --space <id>"],
|
|
3530
|
+
writes: "none",
|
|
3531
|
+
idempotent: true
|
|
3532
|
+
},
|
|
3533
|
+
{
|
|
3534
|
+
action: "workload",
|
|
3535
|
+
summary: "get per-member workload in a space",
|
|
3536
|
+
flags: [{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3537
|
+
examples: ["morula dashboard workload --space <id>"],
|
|
3538
|
+
writes: "none",
|
|
3539
|
+
idempotent: true
|
|
3540
|
+
}
|
|
3541
|
+
]
|
|
3542
|
+
},
|
|
3543
|
+
{
|
|
3544
|
+
resource: "object-meta",
|
|
3545
|
+
summary: "task-level agent metadata (D-06 high-signal keys only)",
|
|
3546
|
+
actions: [
|
|
3547
|
+
{
|
|
3548
|
+
action: "get",
|
|
3549
|
+
summary: "read all agent metadata of a task",
|
|
3550
|
+
flags: [{ name: "object", type: "string", valueHint: "<id>", description: "\u4EFB\u52A1\uFF08Object\uFF09ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3551
|
+
examples: ["morula object-meta get --object <id>"],
|
|
3552
|
+
writes: "none",
|
|
3553
|
+
idempotent: true
|
|
3554
|
+
},
|
|
3555
|
+
{
|
|
3556
|
+
action: "set",
|
|
3557
|
+
summary: "write one agent metadata key. Key must be in the high-signal whitelist: pr_url / pr_number / pipeline_status / deploy_url / waiting_on / blocked_reason / decision",
|
|
3558
|
+
flags: [
|
|
3559
|
+
{ name: "object", type: "string", valueHint: "<id>", description: "\u4EFB\u52A1\uFF08Object\uFF09ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3560
|
+
{ name: "key", type: "string", valueHint: "<key>", description: "\u5143\u6570\u636E\u952E\uFF08\u5FC5\u586B\uFF0C\u767D\u540D\u5355\uFF09", required: true },
|
|
3561
|
+
{ name: "value", type: "string", valueHint: "<text>", description: "\u503C\uFF08\u5FC5\u586B\uFF0C\u975E\u7A7A\uFF09", required: true }
|
|
3562
|
+
],
|
|
3563
|
+
examples: ["morula object-meta set --object <id> --key pr_url --value <url>"],
|
|
3564
|
+
writes: "remote"
|
|
3565
|
+
}
|
|
3566
|
+
]
|
|
3567
|
+
},
|
|
3568
|
+
{
|
|
3569
|
+
resource: "document",
|
|
3570
|
+
summary: "document artifacts (ag_artifact; id is a numeric auto-increment id, not a uuid)",
|
|
3571
|
+
actions: [
|
|
3572
|
+
{
|
|
3573
|
+
action: "list",
|
|
3574
|
+
summary: "list document artifacts",
|
|
3575
|
+
flags: [
|
|
3576
|
+
{ name: "page", type: "number", valueHint: "<n>", description: "\u9875\u7801" },
|
|
3577
|
+
{ name: "page-size", type: "number", valueHint: "<n>", description: "\u6BCF\u9875\u6761\u6570" }
|
|
3578
|
+
],
|
|
3579
|
+
examples: ["morula document list"],
|
|
3580
|
+
writes: "none",
|
|
3581
|
+
idempotent: true
|
|
3582
|
+
},
|
|
3583
|
+
{
|
|
3584
|
+
action: "get",
|
|
3585
|
+
summary: "get document metadata (\u4E0D\u542B\u5B8C\u6574\u6B63\u6587)",
|
|
3586
|
+
args: [{ name: "id", required: true, description: "\u6587\u6863 ID\uFF08\u6570\u5B57\uFF0C\u6765\u81EA document list\uFF09" }],
|
|
3587
|
+
flags: [],
|
|
3588
|
+
examples: ["morula document get <id>"],
|
|
3589
|
+
writes: "none",
|
|
3590
|
+
idempotent: true
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
action: "artifacts",
|
|
3594
|
+
summary: "read the parsed document content (markdown)",
|
|
3595
|
+
args: [{ name: "id", required: true, description: "\u6587\u6863 ID\uFF08\u6570\u5B57\uFF0C\u6765\u81EA document list\uFF09" }],
|
|
3596
|
+
flags: [],
|
|
3597
|
+
examples: ["morula document artifacts <id>"],
|
|
3598
|
+
writes: "none",
|
|
3599
|
+
idempotent: true
|
|
3600
|
+
}
|
|
3601
|
+
]
|
|
3602
|
+
},
|
|
3603
|
+
{
|
|
3604
|
+
resource: "comment",
|
|
3605
|
+
summary: "object comments (agent summaries live here)",
|
|
3606
|
+
actions: [
|
|
3607
|
+
{
|
|
3608
|
+
action: "list",
|
|
3609
|
+
summary: "list comments for an object",
|
|
3610
|
+
flags: [{ name: "object", type: "string", valueHint: "<id>", description: "\u5BF9\u8C61 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3611
|
+
examples: ["morula comment list --object <id>"],
|
|
3612
|
+
writes: "none",
|
|
3613
|
+
idempotent: true
|
|
3614
|
+
},
|
|
3615
|
+
{
|
|
3616
|
+
action: "get",
|
|
3617
|
+
summary: "get comment details",
|
|
3618
|
+
args: [{ name: "id", required: true, description: "\u8BC4\u8BBA ID" }],
|
|
3619
|
+
flags: [],
|
|
3620
|
+
examples: ["morula comment get <id>"],
|
|
3621
|
+
writes: "none",
|
|
3622
|
+
idempotent: true
|
|
3623
|
+
},
|
|
3624
|
+
{
|
|
3625
|
+
action: "add",
|
|
3626
|
+
summary: "add comment (format auto-detected; use markdown for agent final summaries). --mention \u53EF @ \u4EBA\uFF08\u89E6\u53D1\u901A\u77E5/\u50AC\u529E\uFF09",
|
|
3627
|
+
flags: [
|
|
3628
|
+
{ name: "object", type: "string", valueHint: "<id>", description: "\u5BF9\u8C61 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3629
|
+
{ name: "content", type: "string", valueHint: "<text>", description: "\u8BC4\u8BBA\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3630
|
+
{ name: "parent", type: "string", valueHint: "<id>", description: "\u7236\u8BC4\u8BBA\uFF08\u56DE\u590D\uFF09" },
|
|
3631
|
+
{ name: "format", type: "string", valueHint: "<markdown|plain|richtext>", enum: ["markdown", "plain", "richtext"], description: "\u5185\u5BB9\u683C\u5F0F\uFF08\u7F3A\u7701\u81EA\u52A8\u68C0\u6D4B\uFF09" },
|
|
3632
|
+
{ name: "mention", type: "string", valueHint: "<principalId[,principalId]>", repeatable: true, description: "@ \u4EBA\uFF1A\u88AB\u63D0\u53CA\u8005\u7684 principalId\uFF0C\u53EF\u91CD\u590D\u4F20\uFF08--mention a --mention b\uFF09\u6216\u7528\u9017\u53F7\u5206\u9694\u591A\u4E2A\uFF08\u4E24\u79CD\u6DF7\u7528\u81EA\u52A8\u5408\u5E76\u53BB\u91CD\u4FDD\u5E8F\uFF09\uFF1B\u53EA\u652F\u6301\u4EBA\uFF0C\u4E0D\u652F\u6301 @ \u8FD0\u884C\u65F6/\u667A\u80FD\u4F53" }
|
|
3633
|
+
],
|
|
3634
|
+
examples: ["morula comment add --object <id> --content <text>", "morula comment add --object <id> --content <text> --mention <principalId>", "morula comment add --object <id> --content <text> --mention <principalIdA> --mention <principalIdB>"],
|
|
3635
|
+
writes: "remote"
|
|
3636
|
+
},
|
|
3637
|
+
{
|
|
3638
|
+
action: "update",
|
|
3639
|
+
summary: "update comment",
|
|
3640
|
+
args: [{ name: "id", required: true, description: "\u8BC4\u8BBA ID" }],
|
|
3641
|
+
flags: [{ name: "content", type: "string", valueHint: "<text>", description: "\u65B0\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3642
|
+
examples: ["morula comment update <id> --content <text>"],
|
|
3643
|
+
writes: "remote"
|
|
3644
|
+
},
|
|
3645
|
+
{
|
|
3646
|
+
action: "delete",
|
|
3647
|
+
summary: "hard delete comment",
|
|
3648
|
+
args: [{ name: "id", required: true, description: "\u8BC4\u8BBA ID" }],
|
|
3649
|
+
flags: [],
|
|
3650
|
+
examples: ["morula comment delete <id>"],
|
|
3651
|
+
writes: "remote"
|
|
3652
|
+
}
|
|
3653
|
+
]
|
|
3654
|
+
},
|
|
3655
|
+
{
|
|
3656
|
+
resource: "review",
|
|
3657
|
+
summary: "Code Review verdict submission (review task machine protocol)",
|
|
3658
|
+
actions: [
|
|
3659
|
+
{
|
|
3660
|
+
action: "submit",
|
|
3661
|
+
summary: "submit Code Review verdict (machine protocol; review task token)",
|
|
3662
|
+
args: [{ name: "objectId", required: true, description: "\u8BC4\u5BA1\u5BF9\u8C61 ID\uFF08\u7CFB\u7EDF\u63D0\u793A\u300C\u8BC4\u5BA1\u5BF9\u8C61\u300D\uFF09" }],
|
|
3663
|
+
flags: [
|
|
3664
|
+
{ name: "verdict", type: "string", valueHint: "<approved|changes_requested|commented>", enum: ["approved", "changes_requested", "commented"], description: "\u8BC4\u5BA1\u7ED3\u8BBA\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3665
|
+
{ name: "details", type: "string", valueHint: "<text>", description: "\u8BC4\u5BA1\u610F\u89C1\uFF08changes_requested \u5FC5\u586B\uFF1A\u6587\u4EF6/\u884C/\u95EE\u9898/\u5EFA\u8BAE\uFF09" },
|
|
3666
|
+
{ name: "pr-number", type: "number", valueHint: "<n>", description: "\u8BC4\u5BA1\u5BF9\u8C61\u7684 PR \u7F16\u53F7\uFF08\u6709\u5219\u586B\uFF09" }
|
|
3667
|
+
],
|
|
3668
|
+
examples: ["morula review submit <objectId> --verdict approved"],
|
|
3669
|
+
writes: "remote"
|
|
3670
|
+
}
|
|
3671
|
+
]
|
|
3672
|
+
},
|
|
3673
|
+
{
|
|
3674
|
+
resource: "report",
|
|
3675
|
+
summary: "autopilot reports (conclusion-heavy output of automated runs)",
|
|
3676
|
+
actions: [
|
|
3677
|
+
{
|
|
3678
|
+
action: "create",
|
|
3679
|
+
summary: "create an autopilot report (\u6B63\u6587\u4E8C\u9009\u4E00\uFF1A--content \u77ED\u6B63\u6587 / --content-file \u957F Markdown\uFF1B--autopilot \u7701\u7565\u65F6\u6309\u5F53\u524D\u4EFB\u52A1\u53CD\u67E5\u6240\u5C5E\u81EA\u52A8\u5316)",
|
|
3680
|
+
flags: [
|
|
3681
|
+
{ name: "title", type: "string", valueHint: "<text>", description: "\u6807\u9898\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3682
|
+
{ name: "content", type: "string", valueHint: "<markdown>", description: "\u77ED\u6B63\u6587\uFF08\u4E0E --content-file \u4E8C\u9009\u4E00\uFF09" },
|
|
3683
|
+
{ name: "content-file", type: "string", valueHint: "<path>", description: "\u957F Markdown \u62A5\u544A\u6587\u4EF6\uFF08\u4E0E --content \u4E8C\u9009\u4E00\uFF09" },
|
|
3684
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u5F52\u5C5E\u7A7A\u95F4" },
|
|
3685
|
+
{ name: "autopilot", type: "string", valueHint: "<id>", description: "\u6240\u5C5E\u81EA\u52A8\u5316\uFF08\u7701\u7565\u65F6\u6309\u5F53\u524D\u4EFB\u52A1\u53CD\u67E5\uFF09" }
|
|
3686
|
+
],
|
|
3687
|
+
examples: ["morula report create --title <t> --content-file <path>"],
|
|
3688
|
+
writes: "remote"
|
|
3689
|
+
},
|
|
3690
|
+
{
|
|
3691
|
+
action: "get",
|
|
3692
|
+
summary: "get report details (\u542B Markdown \u6B63\u6587)",
|
|
3693
|
+
args: [{ name: "id", required: true, description: "\u62A5\u544A ID" }],
|
|
3694
|
+
flags: [],
|
|
3695
|
+
examples: ["morula report get <id>"],
|
|
3696
|
+
writes: "none",
|
|
3697
|
+
idempotent: true
|
|
3698
|
+
},
|
|
3699
|
+
{
|
|
3700
|
+
action: "list",
|
|
3701
|
+
summary: "list reports of an autopilot",
|
|
3702
|
+
flags: [
|
|
3703
|
+
{ name: "autopilot", type: "string", valueHint: "<id>", description: "\u81EA\u52A8\u5316 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3704
|
+
{ name: "page", type: "number", valueHint: "<n>", description: "\u9875\u7801" },
|
|
3705
|
+
{ name: "page-size", type: "number", valueHint: "<n>", description: "\u6BCF\u9875\u6761\u6570" }
|
|
3706
|
+
],
|
|
3707
|
+
examples: ["morula report list --autopilot <id>"],
|
|
3708
|
+
writes: "none",
|
|
3709
|
+
idempotent: true
|
|
3710
|
+
}
|
|
3711
|
+
]
|
|
3712
|
+
},
|
|
3713
|
+
{
|
|
3714
|
+
resource: "repo",
|
|
3715
|
+
summary: "task-space VCS repos: materialize checkout / MR read & comment (server channel; never approves/merges)",
|
|
3716
|
+
actions: [
|
|
3717
|
+
{
|
|
3718
|
+
action: "list",
|
|
3719
|
+
summary: "list task-space VCS connections (the space repo list \u2014 the task prompt does NOT list repos; query this when a task may span multiple repos)",
|
|
3720
|
+
flags: [{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3721
|
+
examples: ["morula repo list --space <id>"],
|
|
3722
|
+
writes: "none",
|
|
3723
|
+
idempotent: true
|
|
3724
|
+
},
|
|
3725
|
+
{
|
|
3726
|
+
action: "checkout",
|
|
3727
|
+
summary: "materialize a repo into the task workdir (local daemon endpoint preferred, works same-turn; falls back to server machine protocol which resolves scope + workContext)",
|
|
3728
|
+
args: [
|
|
3729
|
+
{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" },
|
|
3730
|
+
{ name: "repoFullName", required: true, description: "\u4ED3\u5E93\u5168\u540D\uFF08owner/name\uFF09" }
|
|
3731
|
+
],
|
|
3732
|
+
flags: [{ name: "ref", type: "string", valueHint: "<branch|sha>", description: "\u68C0\u51FA\u5F15\u7528\uFF08\u7F3A\u7701 = \u7ED1\u5B9A\u5206\u652F\uFF09" }],
|
|
3733
|
+
examples: ["morula repo checkout <taskId> <owner/name>"],
|
|
3734
|
+
writes: "local",
|
|
3735
|
+
idempotent: true
|
|
3736
|
+
},
|
|
3737
|
+
{
|
|
3738
|
+
action: "prepare",
|
|
3739
|
+
summary: "materialize a secondary repo into the task workdir (same level as the primary repo dir)",
|
|
3740
|
+
args: [
|
|
3741
|
+
{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" },
|
|
3742
|
+
{ name: "repoFullName", required: true, description: "\u6B21\u4ED3\u5E93\u5168\u540D\uFF08owner/name\uFF09" }
|
|
3743
|
+
],
|
|
3744
|
+
flags: [],
|
|
3745
|
+
examples: ["morula repo prepare <taskId> <owner/name>"],
|
|
3746
|
+
writes: "local",
|
|
3747
|
+
idempotent: true
|
|
3748
|
+
},
|
|
3749
|
+
{
|
|
3750
|
+
action: "files",
|
|
3751
|
+
summary: "list files in a space repo via the server (read-only, non-task: no local checkout, no AgentTask context)",
|
|
3752
|
+
flags: [
|
|
3753
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3754
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u4ED3\u5E93\u5168\u540D\uFF08\u7F3A\u7701 = \u7A7A\u95F4\u4E3B\u4ED3\u5E93\uFF09" },
|
|
3755
|
+
{ name: "path", type: "string", valueHint: "<path>", description: "\u76EE\u5F55\u8DEF\u5F84\uFF08\u7F3A\u7701 = \u4ED3\u5E93\u6839\uFF09" },
|
|
3756
|
+
{ name: "ref", type: "string", valueHint: "<branch|sha>", description: "\u5206\u652F / tag / commit\uFF08\u7F3A\u7701 = \u9ED8\u8BA4\u5206\u652F\uFF09" }
|
|
3757
|
+
],
|
|
3758
|
+
examples: ["morula repo files --space <id>", "morula repo files --space <id> --path src"],
|
|
3759
|
+
writes: "none",
|
|
3760
|
+
idempotent: true
|
|
3761
|
+
},
|
|
3762
|
+
{
|
|
3763
|
+
action: "read",
|
|
3764
|
+
summary: "read a single file from a space repo via the server (read-only, non-task)",
|
|
3765
|
+
flags: [
|
|
3766
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3767
|
+
{ name: "path", type: "string", valueHint: "<path>", description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3768
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u4ED3\u5E93\u5168\u540D\uFF08\u7F3A\u7701 = \u7A7A\u95F4\u4E3B\u4ED3\u5E93\uFF09" },
|
|
3769
|
+
{ name: "ref", type: "string", valueHint: "<branch|sha>", description: "\u5206\u652F / tag / commit\uFF08\u7F3A\u7701 = \u9ED8\u8BA4\u5206\u652F\uFF09" }
|
|
3770
|
+
],
|
|
3771
|
+
examples: ["morula repo read --space <id> --path src/index.ts"],
|
|
3772
|
+
writes: "none",
|
|
3773
|
+
idempotent: true
|
|
3774
|
+
},
|
|
3775
|
+
{
|
|
3776
|
+
action: "mr create",
|
|
3777
|
+
summary: "create a GitLab MR for the task via the server channel (target project forced to the bound repo, PR linked immediately; push the branch first)",
|
|
3778
|
+
args: [{ name: "repoFullName", required: true, description: "\u4ED3\u5E93\u5168\u540D\uFF08owner/name\uFF09" }],
|
|
3779
|
+
flags: [
|
|
3780
|
+
{ name: "branch", type: "string", valueHint: "<branch>", description: "\u6E90\u5206\u652F\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3781
|
+
{ name: "base", type: "string", valueHint: "<base>", description: "\u76EE\u6807\u5206\u652F\uFF08\u7F3A\u7701 MORULA_BASE_BRANCH / main\uFF09" },
|
|
3782
|
+
{ name: "task-code", type: "string", valueHint: "<code>", description: "\u4EFB\u52A1\u7F16\u53F7\uFF08MR \u6807\u9898/\u6B63\u6587\u56DE\u94FE\uFF09" }
|
|
3783
|
+
],
|
|
3784
|
+
examples: ["morula repo mr create <owner/name> --branch <b> --task-code <code>"],
|
|
3785
|
+
writes: "remote"
|
|
3786
|
+
},
|
|
3787
|
+
{
|
|
3788
|
+
action: "mr list",
|
|
3789
|
+
summary: "list the task-space repos' merge requests (read-only; includes createdAt/updatedAt/author; never approves/merges)",
|
|
3790
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
|
|
3791
|
+
flags: [
|
|
3792
|
+
{ name: "state", type: "string", valueHint: "<opened|closed|merged>", enum: ["opened", "closed", "merged"], description: "MR \u72B6\u6001\u8FC7\u6EE4\uFF08\u7F3A\u7701 opened\uFF09" },
|
|
3793
|
+
{ name: "limit", type: "number", valueHint: "<n>", description: "\u6761\u6570\u4E0A\u9650" },
|
|
3794
|
+
{ name: "author", type: "string", valueHint: "<username>", description: "\u6309\u4F5C\u8005\u8FC7\u6EE4" },
|
|
3795
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u591A\u4ED3\u5E93\u7A7A\u95F4\uFF1A\u76EE\u6807\u4ED3\u5E93\uFF08\u7F3A\u7701\u4E3B\u4ED3\u5E93\uFF09" }
|
|
3796
|
+
],
|
|
3797
|
+
examples: ["morula repo mr list <taskId> --state opened"],
|
|
3798
|
+
writes: "none",
|
|
3799
|
+
idempotent: true
|
|
3800
|
+
},
|
|
3801
|
+
{
|
|
3802
|
+
action: "mr comment",
|
|
3803
|
+
summary: "write a review comment on an MR/PR (write-only: never approves or merges). \u5199\u4E4B\u524D\u5FC5\u987B\u5148 `morula repo mr get <taskId> --mr <iid> --notes` \u8BFB\u5DF2\u6709\u8BA8\u8BBA\uFF1A\u82E5\u672C\u81EA\u52A8\u5316\u5DF2\u5C31\u540C\u4E00\u95EE\u9898\u7559\u8FC7\u7ED3\u8BBA\u4E14\u7ED3\u8BBA\u672A\u53D8\uFF0C\u4E0D\u8981\u518D\u8BC4\u8BBA\u3002\u5185\u5BB9\u4E3A\u7ED3\u8BBA + \u5173\u952E\u95EE\u9898\uFF08\u6587\u4EF6:\u884C + \u5EFA\u8BAE\uFF09\uFF0C\u672B\u5C3E\u52A0\u7F72\u540D\u884C\uFF1B\u957F\u5185\u5BB9\u5199\u6210\u62A5\u544A\u540E\u7528\u6458\u8981\u5F15\u7528\uFF08morula report create \u2192 \u8BC4\u8BBA\u91CC\u7ED9\u8981\u70B9\uFF09",
|
|
3804
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
|
|
3805
|
+
flags: [
|
|
3806
|
+
{ name: "mr", type: "number", valueHint: "<iid>", description: "MR \u7F16\u53F7\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3807
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u591A\u4ED3\u5E93\u7A7A\u95F4\uFF1A\u76EE\u6807\u4ED3\u5E93\uFF08\u7F3A\u7701\u4E3B\u4ED3\u5E93\uFF09" },
|
|
3808
|
+
{ name: "connection", type: "string", valueHint: "<id>", description: "\u8FDE\u63A5 ID\uFF08\u591A\u8FDE\u63A5\u573A\u666F\uFF09" },
|
|
3809
|
+
{ name: "content", type: "string", valueHint: "<text>", description: "\u8BC4\u8BBA\u6B63\u6587\uFF08\u4E0E --content-file \u4E8C\u9009\u4E00\uFF09" },
|
|
3810
|
+
{ name: "content-file", type: "string", valueHint: "<path>", description: "\u8BC4\u8BBA\u6B63\u6587\u6587\u4EF6\uFF08\u4E0E --content \u4E8C\u9009\u4E00\uFF09" }
|
|
3811
|
+
],
|
|
3812
|
+
examples: ["morula repo mr comment <taskId> --mr <iid> --content-file <path>"],
|
|
3813
|
+
writes: "remote"
|
|
3814
|
+
},
|
|
3815
|
+
{
|
|
3816
|
+
action: "mr get",
|
|
3817
|
+
summary: "get a single MR detail (read-only; --ci attaches pipeline status where none = no pipeline, not an error; --notes attaches existing comments \u2014 read this before writing a comment to avoid duplicates)",
|
|
3818
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
|
|
3819
|
+
flags: [
|
|
3820
|
+
{ name: "mr", type: "number", valueHint: "<iid>", description: "MR \u7F16\u53F7\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3821
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u591A\u4ED3\u5E93\u7A7A\u95F4\uFF1A\u76EE\u6807\u4ED3\u5E93\uFF08\u7F3A\u7701\u4E3B\u4ED3\u5E93\uFF09" },
|
|
3822
|
+
{ name: "ci", type: "boolean", description: "\u9644\u5E26 pipeline \u72B6\u6001" },
|
|
3823
|
+
{ name: "notes", type: "boolean", description: "\u9644\u5E26\u5DF2\u6709\u8BC4\u8BBA\uFF08\u5199\u8BC4\u8BBA\u524D\u5148\u8BFB\uFF0C\u9632\u91CD\u590D\uFF09" }
|
|
3824
|
+
],
|
|
3825
|
+
examples: ["morula repo mr get <taskId> --mr <iid> --notes"],
|
|
3826
|
+
writes: "none",
|
|
3827
|
+
idempotent: true
|
|
3828
|
+
},
|
|
3829
|
+
{
|
|
3830
|
+
action: "mr diff",
|
|
3831
|
+
summary: "read an MR diff via the server (read-only; works without a local checkout; truncated flag is set when output was capped)",
|
|
3832
|
+
args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
|
|
3833
|
+
flags: [
|
|
3834
|
+
{ name: "mr", type: "number", valueHint: "<iid>", description: "MR \u7F16\u53F7\uFF08\u5FC5\u586B\uFF09", required: true },
|
|
3835
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u591A\u4ED3\u5E93\u7A7A\u95F4\uFF1A\u76EE\u6807\u4ED3\u5E93\uFF08\u7F3A\u7701\u4E3B\u4ED3\u5E93\uFF09" },
|
|
3836
|
+
{ name: "max-files", type: "number", valueHint: "<n>", description: "\u6587\u4EF6\u6570\u4E0A\u9650" },
|
|
3837
|
+
{ name: "max-chars", type: "number", valueHint: "<n>", description: "\u5B57\u7B26\u6570\u4E0A\u9650" }
|
|
3838
|
+
],
|
|
3839
|
+
examples: ["morula repo mr diff <taskId> --mr <iid>"],
|
|
3840
|
+
writes: "none",
|
|
3841
|
+
idempotent: true
|
|
3842
|
+
}
|
|
3843
|
+
],
|
|
3844
|
+
notes: [
|
|
3845
|
+
"mr \u662F\u591A\u7EA7\u5B50\u547D\u4EE4\u5F62\u6001\uFF08action=mr\u3001\u5B50\u547D\u4EE4\u4F4D\u5728 positionals[0]\uFF09\uFF1A\u5951\u7EA6\u6309\u5B50\u547D\u4EE4\u62C6\u5206\u58F0\u660E\uFF08mr create / mr list / ...\uFF09\uFF0C\u4E25\u683C\u6821\u9A8C\u53EA\u5BF9\u5355\u7EA7 action \u751F\u6548"
|
|
3846
|
+
]
|
|
3847
|
+
},
|
|
3848
|
+
{
|
|
3849
|
+
resource: "ci",
|
|
3850
|
+
summary: "CI/CD pipeline facts and pipeline operations: pipeline runs / job logs / deployments / failed test cases (read-only) plus retry of a failed pipeline or job (Phase 3 write, space face only). Read faces share one command surface \u2014 task face `<taskId>` (AgentTask credential, self-check your MR pipeline) vs space face `--space <spaceId>` (steward/session credential, space-wide inspection); the two are mutually exclusive. Retry is space-face ONLY (person + steward); trigger-deploy (play a manual job) stays person-only and is intentionally absent from the CLI",
|
|
3851
|
+
actions: [
|
|
3852
|
+
{
|
|
3853
|
+
action: "status",
|
|
3854
|
+
summary: 'pipeline runs + deployments for a task (or a space); hasCiConfig:false comes with a notice explaining the repo has no CI config (not an error); on the space face repos[] answers per repo (a multi-repo space never answers hasCiConfig for "the space" \u2014 it is null there); on the task face --repo filters runs client-side by repoFullName and echoes repoFilter (the server does NOT support repo filtering)',
|
|
3855
|
+
args: [{ name: "taskId", description: "\u672C\u4EFB\u52A1 AgentTask UUID\uFF08\u4E0E --space \u4E92\u65A5\uFF09" }],
|
|
3856
|
+
flags: [
|
|
3857
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u7BA1\u5BB6/\u7A7A\u95F4\u9762\uFF1B\u4E0E <taskId> \u4E92\u65A5\uFF09" },
|
|
3858
|
+
{ name: "repo", type: "string", valueHint: "<owner/name>", description: "\u591A\u4ED3\u5E93\u7A7A\u95F4\uFF1A\u53EA\u770B\u8BE5\u4ED3\u5E93\u7684\u8FD0\u884C\uFF08\u4EFB\u52A1\u9762\u4E13\u7528\uFF0C\u5BA2\u6237\u7AEF\u8FC7\u6EE4\uFF09" },
|
|
3859
|
+
{ name: "ref", type: "string", valueHint: "<branch>", description: "\u6309\u5206\u652F/tag \u8FC7\u6EE4\uFF08\u7A7A\u95F4\u9762\u4E13\u7528\uFF09" },
|
|
3860
|
+
{ name: "failed", type: "boolean", description: "\u53EA\u770B\u5931\u8D25\u7684\u8FD0\u884C\uFF08\u7A7A\u95F4\u9762\u4E13\u7528\uFF09" },
|
|
3861
|
+
{ name: "limit", type: "number", valueHint: "<n>", description: "\u8FD0\u884C\u6761\u6570\u4E0A\u9650\uFF08\u670D\u52A1\u7AEF\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 100\uFF09" }
|
|
3862
|
+
],
|
|
3863
|
+
examples: ["morula ci status <taskId>", "morula ci status --space <id> --failed"],
|
|
3864
|
+
writes: "none",
|
|
3865
|
+
idempotent: true,
|
|
3866
|
+
emits: ["ok", "taskId", "spaceId", "runs", "deployments", "repos", "hasCiConfig", "notice", "repoFilter"]
|
|
3867
|
+
},
|
|
3868
|
+
{
|
|
3869
|
+
action: "log",
|
|
3870
|
+
summary: "job log by job id (the server resolves the owning run; both faces use this same endpoint \u2014 the task/space flag only declares which face you are calling from). `truncated:true` means the log was tail-cut, not complete",
|
|
3871
|
+
args: [{ name: "taskId", description: "\u672C\u4EFB\u52A1 AgentTask UUID\uFF08\u4E0E --space \u4E92\u65A5\uFF09" }],
|
|
3872
|
+
flags: [
|
|
3873
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u7BA1\u5BB6/\u7A7A\u95F4\u9762\uFF1B\u4E0E <taskId> \u4E92\u65A5\uFF09" },
|
|
3874
|
+
{ name: "job", type: "string", valueHint: "<jobId>", description: "job ID\uFF08\u5FC5\u586B\uFF1B\u6765\u81EA ci status \u7684 jobs[].id\uFF09", required: true },
|
|
3875
|
+
{ name: "tail", type: "number", valueHint: "<n>", description: "\u4FDD\u7559\u7684\u65E5\u5FD7\u5C3E\u90E8\u5B57\u8282\u6570\uFF08\u670D\u52A1\u7AEF\u9ED8\u8BA4 64KB\uFF09" }
|
|
3876
|
+
],
|
|
3877
|
+
examples: ["morula ci log <taskId> --job <jobId> --tail 20000"],
|
|
3878
|
+
writes: "none",
|
|
3879
|
+
idempotent: true,
|
|
3880
|
+
emits: ["ok", "runId", "jobId", "log", "truncated"]
|
|
3881
|
+
},
|
|
3882
|
+
{
|
|
3883
|
+
action: "deployments",
|
|
3884
|
+
summary: "deployment records for a task (or a space) \u2014 display only: deployments do NOT drive lifecycle state (released is driven solely by a successful production-branch pipeline run)",
|
|
3885
|
+
args: [{ name: "taskId", description: "\u672C\u4EFB\u52A1 AgentTask UUID\uFF08\u4E0E --space \u4E92\u65A5\uFF09" }],
|
|
3886
|
+
flags: [
|
|
3887
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u7BA1\u5BB6/\u7A7A\u95F4\u9762\uFF1B\u4E0E <taskId> \u4E92\u65A5\uFF09" }
|
|
3888
|
+
],
|
|
3889
|
+
examples: ["morula ci deployments <taskId>"],
|
|
3890
|
+
writes: "none",
|
|
3891
|
+
idempotent: true,
|
|
3892
|
+
emits: ["ok", "spaceId", "deployments"]
|
|
3893
|
+
},
|
|
3894
|
+
{
|
|
3895
|
+
action: "test-report",
|
|
3896
|
+
summary: "failed test cases of a run; --run omitted resolves to the newest run in the CI view \u2014 no run at all is CI_RUN_NOT_FOUND (exit 3), never an empty report; provider without this capability (GitHub) returns CAPABILITY_UNSUPPORTED",
|
|
3897
|
+
args: [{ name: "taskId", description: "\u672C\u4EFB\u52A1 AgentTask UUID\uFF08\u4E0E --space \u4E92\u65A5\uFF09" }],
|
|
3898
|
+
flags: [
|
|
3899
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u7BA1\u5BB6/\u7A7A\u95F4\u9762\uFF1B\u4E0E <taskId> \u4E92\u65A5\uFF09" },
|
|
3900
|
+
{ name: "run", type: "string", valueHint: "<runId>", description: "\u8FD0\u884C ID\uFF08\u7F3A\u7701 = CI \u89C6\u56FE\u91CC\u6700\u65B0\u4E00\u6761\u8FD0\u884C\uFF09" }
|
|
3901
|
+
],
|
|
3902
|
+
examples: ["morula ci test-report <taskId>"],
|
|
3903
|
+
writes: "none",
|
|
3904
|
+
idempotent: true,
|
|
3905
|
+
emits: ["ok", "runId", "report"]
|
|
3906
|
+
},
|
|
3907
|
+
{
|
|
3908
|
+
action: "retry",
|
|
3909
|
+
summary: "re-run a failed pipeline (Phase 3; space face ONLY \u2014 a positional <taskId> is a usage error, retry is not open to the task face; person + steward, and a steward must go through an elicitation confirm card). Omit both --run and --job to retry the newest FAILED run of the space (no failed run at all \u2192 CI_RUN_NOT_FOUND exit 3). Only failure/cancelled runs or jobs can be retried (CI_RETRY_NOT_ALLOWED exit 6). NOT idempotent: every call starts another platform re-run",
|
|
3910
|
+
flags: [
|
|
3911
|
+
{ name: "space", type: "string", valueHint: "<id>", description: "\u7A7A\u95F4 ID\uFF08\u5FC5\u586B\uFF1Bretry \u53EA\u6709\u7A7A\u95F4\u9762\uFF0C\u4EFB\u52A1\u9762\u4E0D\u5F00\u653E\uFF09", required: true },
|
|
3912
|
+
{ name: "run", type: "string", valueHint: "<runId>", description: "\u8981\u91CD\u8DD1\u7684\u8FD0\u884C ID\uFF08\u4E0E --job \u4E92\u65A5\uFF1B\u7F3A\u7701 = \u8BE5\u7A7A\u95F4\u6700\u65B0\u4E00\u6761\u5931\u8D25\u8FD0\u884C\uFF09" },
|
|
3913
|
+
{ name: "job", type: "string", valueHint: "<jobId>", description: "\u8981\u91CD\u8DD1\u7684\u5355\u4E2A job ID\uFF08\u4E0E --run \u4E92\u65A5\uFF1B\u6765\u81EA ci status \u7684 jobs[].id\uFF09" }
|
|
3914
|
+
],
|
|
3915
|
+
examples: ["morula ci retry --space <id>", "morula ci retry --space <id> --job <jobId>"],
|
|
3916
|
+
writes: "remote",
|
|
3917
|
+
idempotent: false,
|
|
3918
|
+
emits: ["ok", "action", "runId", "jobId", "externalId", "ref", "acceptedAt"]
|
|
3919
|
+
}
|
|
3920
|
+
],
|
|
3921
|
+
notes: [
|
|
3922
|
+
"\u4E24\u4E2A\u9762\uFF08\u4EFB\u52A1\u9762 <taskId> / \u7A7A\u95F4\u9762 --space <spaceId>\uFF09\u4E92\u65A5\uFF1A\u540C\u65F6\u7ED9 \u2192 usage error(2)\uFF0C\u90FD\u4E0D\u7ED9\u540C\u6837 usage error\u3002\u51ED\u8BC1\u4E0E\u4F5C\u7528\u57DF\u4E0D\u540C\uFF0C\u731C\u9519\u4F1A\u53D8\u6210\u9759\u9ED8\u8D8A\u6743\u6216\u9759\u9ED8\u5931\u8D25",
|
|
3923
|
+
"\u53EA\u8BFB\u9762\uFF1Astatus / log / deployments / test-report \u53EA\u8BFB\uFF08writes: none\uFF09\uFF1Bretry \u662F Phase 3 \u7684\u552F\u4E00\u5199\u9762\uFF08writes: remote\uFF0C\u975E\u5E42\u7B49\uFF09\uFF0C\u4E14**\u53EA\u6709\u7A7A\u95F4\u9762**\u2014\u2014\u7ED9\u4F4D\u7F6E\u53C2\u6570 <taskId> \u2192 usage error(2)",
|
|
3924
|
+
"\u89E6\u53D1\u624B\u52A8 job\uFF08play\uFF0C\u90E8\u7F72\uFF09\u4E0D\u8FDB CLI\uFF1A\u4EC5\u4EBA\u9762\uFF08D5/D6\uFF09\uFF0C\u5165\u53E3\u53EA\u5728 web \u786E\u8BA4\u5F39\u7A97",
|
|
3925
|
+
"\u670D\u52A1\u7AEF\u4E1A\u52A1\u5931\u8D25\u662F HTTP 200 + `{ ok:false, code, message }`\uFF1ACI_JOB_NOT_FOUND / CI_RUN_NOT_FOUND / TASK_NOT_FOUND / OBJECT_NOT_FOUND \u2192 exit 3\uFF1BCI_RETRY_NOT_ALLOWED / CI_JOB_NOT_MANUAL \u2192 exit 6\uFF1BCAPABILITY_UNSUPPORTED / NO_CONNECTION / \u5176\u4F59 \u2192 exit 1"
|
|
3926
|
+
]
|
|
3927
|
+
},
|
|
3928
|
+
{
|
|
3929
|
+
resource: "attachment",
|
|
3930
|
+
summary: "file attachments for objects / comments / chat messages (local file upload)",
|
|
3931
|
+
actions: [
|
|
3932
|
+
{
|
|
3933
|
+
action: "upload",
|
|
3934
|
+
summary: "upload a local file and attach it to a task (--object) / comment (--comment) / chat message (--ag-message); for pure chat tasks pass --task <agentTaskId> to attach to the chat session; omit target to auto-merge (object if bound, else chat message)",
|
|
3935
|
+
args: [{ name: "file", required: true, description: "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84" }],
|
|
3936
|
+
flags: [
|
|
3937
|
+
{ name: "object", type: "string", valueHint: "<id>", description: "\u6302\u5230\u5BF9\u8C61" },
|
|
3938
|
+
{ name: "comment", type: "string", valueHint: "<id>", description: "\u6302\u5230\u8BC4\u8BBA" },
|
|
3939
|
+
{ name: "ag-message", type: "boolean", description: "\u6302\u5230 Chat \u4F1A\u8BDD\u6700\u65B0\u6D88\u606F" },
|
|
3940
|
+
{ name: "task", type: "string", valueHint: "<agentTaskId>", description: "\u7EAF Chat \u4EFB\u52A1\u5F52\u5E76\u5230\u4F1A\u8BDD\u6D88\u606F" },
|
|
3941
|
+
{ name: "inline", type: "boolean", description: "\u5185\u8054\u9644\u4EF6" }
|
|
3942
|
+
],
|
|
3943
|
+
examples: ["morula attachment upload <file> --object <id>"],
|
|
3944
|
+
writes: "remote"
|
|
3945
|
+
},
|
|
3946
|
+
{
|
|
3947
|
+
action: "list",
|
|
3948
|
+
summary: "list attachments for an object",
|
|
3949
|
+
flags: [{ name: "object", type: "string", valueHint: "<id>", description: "\u5BF9\u8C61 ID\uFF08\u5FC5\u586B\uFF09", required: true }],
|
|
3950
|
+
examples: ["morula attachment list --object <id>"],
|
|
3951
|
+
writes: "none",
|
|
3952
|
+
idempotent: true
|
|
3953
|
+
},
|
|
3954
|
+
{
|
|
3955
|
+
action: "get",
|
|
3956
|
+
summary: "get attachment details",
|
|
3957
|
+
args: [{ name: "id", required: true, description: "\u9644\u4EF6 ID" }],
|
|
3958
|
+
flags: [],
|
|
3959
|
+
examples: ["morula attachment get <id>"],
|
|
3960
|
+
writes: "none",
|
|
3961
|
+
idempotent: true
|
|
3962
|
+
},
|
|
3963
|
+
{
|
|
3964
|
+
action: "content",
|
|
3965
|
+
summary: "read the parsed text content (MinerU markdown) of an attachment; unparsed/parsing/failed is reported in the body, not as an HTTP error",
|
|
3966
|
+
args: [{ name: "id", required: true, description: "\u9644\u4EF6 ID" }],
|
|
3967
|
+
flags: [{ name: "max-chars", type: "number", valueHint: "<n>", description: "\u6700\u5927\u8FD4\u56DE\u5B57\u7B26\u6570\uFF08\u670D\u52A1\u7AEF\u7F3A\u7701 8000\uFF09" }],
|
|
3968
|
+
examples: ["morula attachment content <id>", "morula attachment content <id> --max-chars 20000"],
|
|
3969
|
+
writes: "none",
|
|
3970
|
+
idempotent: true
|
|
3971
|
+
},
|
|
3972
|
+
{
|
|
3973
|
+
action: "download-url",
|
|
3974
|
+
summary: "get signed download URL for an attachment",
|
|
3975
|
+
args: [{ name: "id", required: true, description: "\u9644\u4EF6 ID" }],
|
|
3976
|
+
flags: [],
|
|
3977
|
+
examples: ["morula attachment download-url <id>"],
|
|
3978
|
+
writes: "none",
|
|
3979
|
+
idempotent: true
|
|
3980
|
+
},
|
|
3981
|
+
{
|
|
3982
|
+
action: "delete",
|
|
3983
|
+
summary: "delete attachment",
|
|
3984
|
+
args: [{ name: "id", required: true, description: "\u9644\u4EF6 ID" }],
|
|
3985
|
+
flags: [],
|
|
3986
|
+
examples: ["morula attachment delete <id>"],
|
|
3987
|
+
writes: "remote"
|
|
3988
|
+
}
|
|
3989
|
+
]
|
|
3990
|
+
}
|
|
3991
|
+
]
|
|
3992
|
+
};
|
|
3993
|
+
|
|
3994
|
+
// ../morula-cli/src/commands/steward.ts
|
|
3995
|
+
async function stewardCommand(context) {
|
|
3996
|
+
if (!context.runtime.token)
|
|
3997
|
+
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
3998
|
+
const { action, flags } = context.parsed;
|
|
3999
|
+
if (action === "get") {
|
|
4000
|
+
rejectUnknownFlags(flags, ["space"]);
|
|
4001
|
+
return context.client.stewards.get(requireFlag(flags, "space"));
|
|
4002
|
+
}
|
|
4003
|
+
if (action === "dispatch") {
|
|
4004
|
+
rejectUnknownFlags(flags, ["space", "title", "description", "state", "assignee", "repo", "client-message-id"]);
|
|
4005
|
+
const projectId = requireFlag(flags, "space");
|
|
4006
|
+
const title = requireFlag(flags, "title");
|
|
4007
|
+
const state = flagString(flags, "state");
|
|
4008
|
+
const description = flagString(flags, "description");
|
|
4009
|
+
const assigneePrincipalId = flagString(flags, "assignee");
|
|
4010
|
+
const repoFullName = flagString(flags, "repo");
|
|
4011
|
+
const clientMessageId = flagString(flags, "client-message-id");
|
|
4012
|
+
return context.client.stewards.dispatch(projectId, {
|
|
4013
|
+
title,
|
|
4014
|
+
...description !== void 0 ? { description } : {},
|
|
4015
|
+
...state !== void 0 ? { state } : {},
|
|
4016
|
+
...assigneePrincipalId !== void 0 ? { assigneePrincipalId } : {},
|
|
4017
|
+
...repoFullName !== void 0 ? { repoFullName } : {},
|
|
4018
|
+
...clientMessageId !== void 0 ? { clientMessageId } : {}
|
|
4019
|
+
});
|
|
4020
|
+
}
|
|
4021
|
+
throw usage("usage: morula steward <get|dispatch>");
|
|
2503
4022
|
}
|
|
2504
4023
|
|
|
2505
4024
|
// ../morula-cli/src/commands/task.ts
|
|
@@ -2598,7 +4117,7 @@ async function taskCommand(context) {
|
|
|
2598
4117
|
morula task get <objectId> # \u4E1A\u52A1\u4EFB\u52A1\uFF08Object\uFF09\u8BE6\u60C5\uFF08\u542B\u81EA\u5B9A\u4E49\u5B57\u6BB5\u503C\uFF09`);
|
|
2599
4118
|
return context.client.http.get(`/agent-tasks/${encodeURIComponent(taskId)}`);
|
|
2600
4119
|
}
|
|
2601
|
-
const id =
|
|
4120
|
+
const id = requireId4(positionals, "task get");
|
|
2602
4121
|
const task = await context.client.tasks.get(id);
|
|
2603
4122
|
if (!task)
|
|
2604
4123
|
return null;
|
|
@@ -2633,7 +4152,7 @@ async function taskCommand(context) {
|
|
|
2633
4152
|
}
|
|
2634
4153
|
if (action === "update") {
|
|
2635
4154
|
rejectUnknownFlags(flags, WRITE_FLAGS.filter((flag) => flag !== "space" && flag !== "owner"));
|
|
2636
|
-
const id =
|
|
4155
|
+
const id = requireId4(positionals, "task update");
|
|
2637
4156
|
const input = taskInput(flags, false);
|
|
2638
4157
|
const fieldSpecs = parseFieldFlags(flagString(flags, "field"));
|
|
2639
4158
|
if (!Object.keys(input).length && !flagString(flags, "field"))
|
|
@@ -2664,12 +4183,12 @@ async function taskCommand(context) {
|
|
|
2664
4183
|
}
|
|
2665
4184
|
if (action === "delete") {
|
|
2666
4185
|
rejectUnknownFlags(flags, []);
|
|
2667
|
-
const id =
|
|
4186
|
+
const id = requireId4(positionals, "task delete");
|
|
2668
4187
|
return context.client.tasks.delete(id);
|
|
2669
4188
|
}
|
|
2670
4189
|
if (action === "branch") {
|
|
2671
4190
|
rejectUnknownFlags(flags, ["name", "repo"]);
|
|
2672
|
-
const id =
|
|
4191
|
+
const id = requireId4(positionals, "task branch");
|
|
2673
4192
|
const branchName = requireFlag(flags, "name");
|
|
2674
4193
|
const runtimeId = context.env.MORULA_RUNTIME_ID;
|
|
2675
4194
|
if (!runtimeId)
|
|
@@ -2683,7 +4202,7 @@ async function taskCommand(context) {
|
|
|
2683
4202
|
}
|
|
2684
4203
|
if (action === "state") {
|
|
2685
4204
|
rejectUnknownFlags(flags, ["state"]);
|
|
2686
|
-
const id =
|
|
4205
|
+
const id = requireId4(positionals, "task state");
|
|
2687
4206
|
const stateCode = requireFlag(flags, "state");
|
|
2688
4207
|
const runtimeId = context.env.MORULA_RUNTIME_ID;
|
|
2689
4208
|
if (!runtimeId)
|
|
@@ -2695,7 +4214,7 @@ async function taskCommand(context) {
|
|
|
2695
4214
|
}
|
|
2696
4215
|
if (action === "analysis-verdict") {
|
|
2697
4216
|
rejectUnknownFlags(flags, ["verdict", "reason"]);
|
|
2698
|
-
const id =
|
|
4217
|
+
const id = requireId4(positionals, "task analysis-verdict");
|
|
2699
4218
|
const verdict = requireFlag(flags, "verdict");
|
|
2700
4219
|
if (verdict !== "ready" && verdict !== "blocked")
|
|
2701
4220
|
throw usage(`--verdict \u5FC5\u987B\u662F ready / blocked
|
|
@@ -2758,7 +4277,37 @@ async function taskCommand(context) {
|
|
|
2758
4277
|
...messages.length === 0 ? { hint: "\u672C\u7EBF\u7A0B\u6CA1\u6709\u65B0\u6D88\u606F\uFF1A\u6309\u539F\u8BA1\u5212\u7EE7\u7EED\uFF0C\u4E0D\u8981\u5C55\u5F00\u8BA8\u8BBA\u3002" } : {}
|
|
2759
4278
|
};
|
|
2760
4279
|
}
|
|
2761
|
-
|
|
4280
|
+
if (action === "agent-tasks") {
|
|
4281
|
+
rejectUnknownFlags(flags, ["object", "status", "page", "page-size"]);
|
|
4282
|
+
const objectId = requireFlag(flags, "object");
|
|
4283
|
+
return context.client.agentTasks.list(compact3({
|
|
4284
|
+
objectId,
|
|
4285
|
+
status: flagString(flags, "status"),
|
|
4286
|
+
page: flagNumber(flags, "page"),
|
|
4287
|
+
pageSize: flagNumber(flags, "page-size")
|
|
4288
|
+
}));
|
|
4289
|
+
}
|
|
4290
|
+
if (action === "cancel") {
|
|
4291
|
+
rejectUnknownFlags(flags, []);
|
|
4292
|
+
const id = requireId4(positionals, "task cancel");
|
|
4293
|
+
return context.client.agentTasks.cancel(id);
|
|
4294
|
+
}
|
|
4295
|
+
if (action === "rerun") {
|
|
4296
|
+
rejectUnknownFlags(flags, []);
|
|
4297
|
+
const id = requireId4(positionals, "task rerun");
|
|
4298
|
+
const result = await context.client.agentTasks.rerun(id);
|
|
4299
|
+
if (result?.ok === false)
|
|
4300
|
+
throw new CliError(result.code ?? "RERUN_FAILED", rerunExitCode(result.code), `\u91CD\u8DD1\u5931\u8D25: ${result.message ?? result.code ?? "\u672A\u77E5\u539F\u56E0"}`);
|
|
4301
|
+
return result;
|
|
4302
|
+
}
|
|
4303
|
+
throw usage("usage: morula task <list|create|get|update|delete|branch|state|analysis-verdict|inbox|agent-tasks|cancel|rerun>");
|
|
4304
|
+
}
|
|
4305
|
+
function rerunExitCode(code) {
|
|
4306
|
+
if (code === "NOT_FOUND")
|
|
4307
|
+
return EXIT.NOT_FOUND;
|
|
4308
|
+
if (code === "CONFLICT" || code === "ALREADY_QUEUED")
|
|
4309
|
+
return EXIT.CONFLICT;
|
|
4310
|
+
return EXIT.OTHER;
|
|
2762
4311
|
}
|
|
2763
4312
|
function taskInput(flags, create) {
|
|
2764
4313
|
const description = flagString(flags, "description");
|
|
@@ -2782,7 +4331,7 @@ function taskInput(flags, create) {
|
|
|
2782
4331
|
}
|
|
2783
4332
|
return values;
|
|
2784
4333
|
}
|
|
2785
|
-
function
|
|
4334
|
+
function requireId4(positionals, command) {
|
|
2786
4335
|
const id = positionals[0]?.trim();
|
|
2787
4336
|
if (!id)
|
|
2788
4337
|
throw usage(`usage: morula ${command} <id>`);
|
|
@@ -2799,13 +4348,19 @@ function unauthenticated() {
|
|
|
2799
4348
|
var COMMANDS = {
|
|
2800
4349
|
"attachment": attachmentCommand,
|
|
2801
4350
|
"auth": authCommand,
|
|
4351
|
+
"autopilot": autopilotCommand,
|
|
4352
|
+
"ci": ciCommand,
|
|
2802
4353
|
"comment": commentCommand,
|
|
4354
|
+
"dashboard": dashboardCommand,
|
|
4355
|
+
"document": documentCommand,
|
|
4356
|
+
"object-meta": objectMetaCommand,
|
|
2803
4357
|
"report": reportCommand,
|
|
2804
4358
|
"repo": repoCommand,
|
|
2805
4359
|
"review": reviewCommand,
|
|
2806
4360
|
"task": taskCommand,
|
|
2807
4361
|
"space": spaceCommand,
|
|
2808
|
-
"space-member": spaceMemberCommand
|
|
4362
|
+
"space-member": spaceMemberCommand,
|
|
4363
|
+
"steward": stewardCommand
|
|
2809
4364
|
};
|
|
2810
4365
|
var HELP = {
|
|
2811
4366
|
name: "morula",
|
|
@@ -2813,70 +4368,12 @@ var HELP = {
|
|
|
2813
4368
|
build: BUILD_ID,
|
|
2814
4369
|
summary: "Agent-first CLI for Morula task management. Success output is JSON on stdout; errors are JSON on stderr.",
|
|
2815
4370
|
usage: "morula <resource> <action> [id] [--flags]",
|
|
2816
|
-
commands:
|
|
2817
|
-
"auth": {
|
|
2818
|
-
login: "browser SkyDoor SSO using PKCE and loopback callback",
|
|
2819
|
-
logout: "remove the current context token",
|
|
2820
|
-
status: "show context, endpoint, and authentication state"
|
|
2821
|
-
},
|
|
2822
|
-
"task": {
|
|
2823
|
-
"list [--space <id>] [--mine] [--assignee <id>] [--page <n>] [--page-size <n>] [--search <text>] [--priority <low|medium|high|urgent>] [--state <id>]": "list tasks (--mine: \u4EC5\u6211\u8D1F\u8D23\u7684)",
|
|
2824
|
-
"create --space <id> --title <text> [--priority <value>] [--assignee <id>]": "create task",
|
|
2825
|
-
"get [id]": "get task details. No argument = the current AgentTask (id resolved from MORULA_TASK_ID / .morula/checkout.json). With an id = the business Object (title/description)",
|
|
2826
|
-
"update <id> [--title <text>] [--state <id>] [--priority <value>] [--sort-order <n>]": "update task (--state here is the business Object state id, NOT the AgentTask state code \u2014 see `task state`)",
|
|
2827
|
-
"delete <id>": "hard delete task",
|
|
2828
|
-
"branch <id> --name <name> [--repo <owner/name>]": "report the final branch name for an agent task (machine protocol; agent naming, runtimeId from env; --repo: secondary repo in a multi-repo task)",
|
|
2829
|
-
"state <id> --state <code>": "self-transition task state (machine protocol; agent drives todo\u2192in_progress\u2192review, runtimeId from env)",
|
|
2830
|
-
"analysis-verdict <taskId> --verdict <ready|blocked> [--reason <text>]": "report the task-analysis verdict (machine protocol; ready \u2192 platform auto-starts development, blocked \u2192 task stays in analysis)",
|
|
2831
|
-
"inbox [--peek]": "checkpoint: pull this thread's unconsumed messages into the current turn (machine protocol; --peek reads without marking them consumed)"
|
|
2832
|
-
},
|
|
2833
|
-
"space": {
|
|
2834
|
-
"list [--page <n>] [--page-size <n>] [--keyword <text>]": "list visible spaces",
|
|
2835
|
-
"get <id>": "get space details"
|
|
2836
|
-
},
|
|
2837
|
-
"space-member": {
|
|
2838
|
-
"list --space <id>": "list space members",
|
|
2839
|
-
"add --space <id> --principal <id> [--role <admin|member>]": "add member to space",
|
|
2840
|
-
"update <member-id> --role <admin|member>": "update member role",
|
|
2841
|
-
"remove <member-id>": "remove member from space"
|
|
2842
|
-
},
|
|
2843
|
-
"comment": {
|
|
2844
|
-
"list --object <id>": "list comments for an object",
|
|
2845
|
-
"get <id>": "get comment details",
|
|
2846
|
-
"add --object <id> --content <text> [--parent <id>] [--format markdown|plain|richtext]": "add comment (format auto-detected; use markdown for agent final summaries)",
|
|
2847
|
-
"update <id> --content <text>": "update comment",
|
|
2848
|
-
"delete <id>": "hard delete comment"
|
|
2849
|
-
},
|
|
2850
|
-
"review": {
|
|
2851
|
-
"submit <objectId> --verdict <approved|changes_requested|commented> [--details <text>] [--pr-number <n>]": "submit Code Review verdict (machine protocol; review task token)"
|
|
2852
|
-
},
|
|
2853
|
-
"report": {
|
|
2854
|
-
"create --title <text> [--content <markdown> | --content-file <path>] [--space <id>] [--autopilot <id>]": "create an autopilot report (\u6B63\u6587\u4E8C\u9009\u4E00\uFF1A--content \u77ED\u6B63\u6587 / --content-file \u957F Markdown\uFF1B--autopilot \u7701\u7565\u65F6\u6309\u5F53\u524D\u4EFB\u52A1\u53CD\u67E5\u6240\u5C5E\u81EA\u52A8\u5316)",
|
|
2855
|
-
"get <id>": "get report details (\u542B Markdown \u6B63\u6587)",
|
|
2856
|
-
"list --autopilot <id> [--page <n>] [--page-size <n>]": "list reports of an autopilot"
|
|
2857
|
-
},
|
|
2858
|
-
"repo": {
|
|
2859
|
-
"list --space <id>": "list task-space VCS connections (available repos; note: the system prompt already lists them for a task \u2014 you rarely need this)",
|
|
2860
|
-
"checkout <taskId> <repoFullName> [--ref <branch|sha>]": "materialize a repo into the task workdir (local daemon endpoint preferred, works same-turn; falls back to server machine protocol which resolves scope + workContext)",
|
|
2861
|
-
"prepare <taskId> <repoFullName>": "materialize a secondary repo into the task workdir (same level as the primary repo dir)",
|
|
2862
|
-
"mr create <repoFullName> --branch <branch> [--base <base>] [--task-code <code>]": "create a GitLab MR for the task via the server channel (target project forced to the bound repo, PR linked immediately; push the branch first)",
|
|
2863
|
-
"mr list <taskId> [--state opened|closed|merged] [--limit <n>] [--author <username>] [--repo <owner/name>]": "list the task-space repos' merge requests (read-only; includes createdAt/updatedAt/author; never approves/merges)",
|
|
2864
|
-
"mr comment <taskId> --mr <iid> [--repo <owner/name>] (--content <text> | --content-file <path>)": "write a review comment on an MR/PR (write-only: never approves or merges). \u5199\u4E4B\u524D\u5FC5\u987B\u5148 `morula repo mr get <taskId> --mr <iid> --notes` \u8BFB\u5DF2\u6709\u8BA8\u8BBA\uFF1A\u82E5\u672C\u81EA\u52A8\u5316\u5DF2\u5C31\u540C\u4E00\u95EE\u9898\u7559\u8FC7\u7ED3\u8BBA\u4E14\u7ED3\u8BBA\u672A\u53D8\uFF0C\u4E0D\u8981\u518D\u8BC4\u8BBA\u3002\u5185\u5BB9\u4E3A\u7ED3\u8BBA + \u5173\u952E\u95EE\u9898\uFF08\u6587\u4EF6:\u884C + \u5EFA\u8BAE\uFF09\uFF0C\u672B\u5C3E\u52A0\u7F72\u540D\u884C\u300C> \u7531\u81EA\u52A8\u5316\u300C<\u540D\u79F0>\u300D\u4E8E <\u65E5\u671F> \u751F\u6210\u300D\uFF1B\u4E0D\u8981\u8D34\u5927\u6BB5\u65E5\u5FD7\u6216\u590D\u8FF0 diff\uFF1B\u957F\u5185\u5BB9\u5199\u6210\u62A5\u544A\u540E\u7528\u6458\u8981\u5F15\u7528\uFF08morula report create \u2192 \u8BC4\u8BBA\u91CC\u7ED9\u8981\u70B9\uFF09",
|
|
2865
|
-
"mr get <taskId> --mr <iid> [--repo <owner/name>] [--ci] [--notes]": "get a single MR detail (read-only; --ci attaches pipeline status where none = no pipeline, not an error; --notes attaches existing comments \u2014 read this before writing a comment to avoid duplicates)",
|
|
2866
|
-
"mr diff <taskId> --mr <iid> [--repo <owner/name>] [--max-files <n>] [--max-chars <n>]": "read an MR diff via the server (read-only; works without a local checkout; truncated flag is set when output was capped)"
|
|
2867
|
-
},
|
|
2868
|
-
"attachment": {
|
|
2869
|
-
"upload <file> [--object <id>] [--comment <id>] [--ag-message] [--task <agentTaskId>] [--inline]": "upload a local file and attach it to a task (--object) / comment (--comment) / chat message (--ag-message); for pure chat tasks pass --task <agentTaskId> to attach to the chat session; omit target to auto-merge (object if bound, else chat message)",
|
|
2870
|
-
"list --object <id>": "list attachments for an object",
|
|
2871
|
-
"get <id>": "get attachment details",
|
|
2872
|
-
"download-url <id>": "get signed download URL for an attachment",
|
|
2873
|
-
"delete <id>": "delete attachment"
|
|
2874
|
-
}
|
|
2875
|
-
},
|
|
4371
|
+
commands: renderTopLevelCommands(SPEC),
|
|
2876
4372
|
globalFlags: {
|
|
2877
4373
|
"--env <context>": "select test or a saved context",
|
|
2878
4374
|
"--url <baseUrl>": "override API base URL",
|
|
2879
4375
|
"--token <token>": "override token for this invocation (not persisted)",
|
|
4376
|
+
"--json": "no-op; output is always JSON (accepted for familiarity)",
|
|
2880
4377
|
"--help": "print this machine-readable command surface",
|
|
2881
4378
|
"--version": "print version JSON"
|
|
2882
4379
|
},
|
|
@@ -2893,4 +4390,4 @@ var HELP = {
|
|
|
2893
4390
|
};
|
|
2894
4391
|
|
|
2895
4392
|
// ../morula-cli/src/index.ts
|
|
2896
|
-
runEntry({ commands: COMMANDS, help: HELP });
|
|
4393
|
+
runEntry({ commands: COMMANDS, help: HELP, spec: SPEC });
|