openclaw-weiyuan-init 1.0.116 → 1.0.118
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/lib/downloader.js
CHANGED
|
@@ -12,6 +12,44 @@ function toZipFilename(downloadUrl) {
|
|
|
12
12
|
return parts[parts.length - 1] || 'weiyuan-openclaw-skill.zip';
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function parseVersionFromZipFilename(zipFilename) {
|
|
16
|
+
const match = /-v(\d+\.\d+\.\d+)\.zip$/i.exec(String(zipFilename || '').trim());
|
|
17
|
+
return match ? match[1] : '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function buildFallbackReleaseNotes(version) {
|
|
21
|
+
const normalizedVersion = String(version || '').trim();
|
|
22
|
+
if (!normalizedVersion) return null;
|
|
23
|
+
return {
|
|
24
|
+
version: normalizedVersion,
|
|
25
|
+
summary: '本次更新已完成发布,请先阅读必读文件后再执行命令。',
|
|
26
|
+
changes: [
|
|
27
|
+
'已同步最新微元系统运行时与规则文件。',
|
|
28
|
+
'执行任何命令前,请先阅读 CHANGELOG_OPENCLAW.md、release-notes/latest.json、docs/OPENCLAW_WEIYUAN_CLI_QUICKSTART.md、docs/WEIYUAN_AGENT_RULES.md。'
|
|
29
|
+
],
|
|
30
|
+
releasedAt: new Date().toISOString()
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeReleaseNotesForResolvedVersion(releaseNotes, resolvedVersion) {
|
|
35
|
+
const version = String(resolvedVersion || '').trim();
|
|
36
|
+
if (!version) return releaseNotes || null;
|
|
37
|
+
const fallback = buildFallbackReleaseNotes(version);
|
|
38
|
+
if (!releaseNotes || typeof releaseNotes !== 'object') return fallback;
|
|
39
|
+
const notesVersion = String(releaseNotes.version || '').trim();
|
|
40
|
+
const summary = String(releaseNotes.summary || '').trim();
|
|
41
|
+
const changes = Array.isArray(releaseNotes.changes)
|
|
42
|
+
? releaseNotes.changes.map((item) => String(item || '').trim()).filter(Boolean)
|
|
43
|
+
: [];
|
|
44
|
+
return {
|
|
45
|
+
version,
|
|
46
|
+
summary: summary || fallback.summary,
|
|
47
|
+
changes: changes.length > 0 ? changes : fallback.changes,
|
|
48
|
+
releasedAt: String(releaseNotes.releasedAt || '').trim() || fallback.releasedAt,
|
|
49
|
+
sourceVersion: notesVersion || undefined
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
15
53
|
async function fetchLatestReleaseNotes(base, nonce) {
|
|
16
54
|
const notesUrl = `${base}/RELEASE_NOTES_LATEST.json?nocache=${nonce}`;
|
|
17
55
|
try {
|
|
@@ -34,7 +72,16 @@ async function fetchLatestReleaseNotes(base, nonce) {
|
|
|
34
72
|
|
|
35
73
|
async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = null }) {
|
|
36
74
|
if (downloadUrl) {
|
|
37
|
-
|
|
75
|
+
const zipFilename = toZipFilename(downloadUrl);
|
|
76
|
+
const version = parseVersionFromZipFilename(zipFilename);
|
|
77
|
+
return {
|
|
78
|
+
downloadUrl,
|
|
79
|
+
zipFilename,
|
|
80
|
+
version: version || null,
|
|
81
|
+
sha256: '',
|
|
82
|
+
from: 'download_url',
|
|
83
|
+
releaseNotes: normalizeReleaseNotesForResolvedVersion(null, version)
|
|
84
|
+
};
|
|
38
85
|
}
|
|
39
86
|
|
|
40
87
|
const base = trimSlash(upgradeBaseUrl);
|
|
@@ -52,7 +99,14 @@ async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = nul
|
|
|
52
99
|
if (spinner) {
|
|
53
100
|
spinner.text = `已解析最新版本: v${version}`;
|
|
54
101
|
}
|
|
55
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
downloadUrl: resolvedDownloadUrl,
|
|
104
|
+
zipFilename,
|
|
105
|
+
version,
|
|
106
|
+
sha256,
|
|
107
|
+
from: 'latest_json',
|
|
108
|
+
releaseNotes: normalizeReleaseNotesForResolvedVersion(releaseNotes, version)
|
|
109
|
+
};
|
|
56
110
|
}
|
|
57
111
|
} catch (_e) {
|
|
58
112
|
// fallback to txt
|
|
@@ -66,7 +120,14 @@ async function resolvePackageSource({ downloadUrl, upgradeBaseUrl, spinner = nul
|
|
|
66
120
|
if (spinner) {
|
|
67
121
|
spinner.text = `已解析最新版本: v${version}`;
|
|
68
122
|
}
|
|
69
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
downloadUrl: resolvedDownloadUrl,
|
|
125
|
+
zipFilename,
|
|
126
|
+
version,
|
|
127
|
+
sha256: '',
|
|
128
|
+
from: 'latest_version',
|
|
129
|
+
releaseNotes: normalizeReleaseNotesForResolvedVersion(releaseNotes, version)
|
|
130
|
+
};
|
|
70
131
|
}
|
|
71
132
|
|
|
72
133
|
async function downloadFile(url, outputPath, spinner = null, expectedSha256 = '') {
|
package/package.json
CHANGED
|
@@ -145,7 +145,7 @@ npm run weiyuan -- doc sync-check --project <projectId>
|
|
|
145
145
|
|
|
146
146
|
说明:
|
|
147
147
|
|
|
148
|
-
-
|
|
148
|
+
- 文件上传已恢复 CLI 直传能力,可使用 `doc upload --project <projectId> --file <路径>`;未显式提供 `--name` 或 `--type` 时会自动补默认值。
|
|
149
149
|
- 文件删除保留 CLI 能力,智能体 CLI 与小精灵 CLI 都支持删除文件。
|
|
150
150
|
|
|
151
151
|
Todo 行动清单:
|
|
@@ -803,7 +803,7 @@ async function repairIdentityForCurrentBinding(args: Argv): Promise<boolean> {
|
|
|
803
803
|
JSON.stringify(nextJoinedProjectIds) !== JSON.stringify(identity.joinedProjectIds ?? []) ||
|
|
804
804
|
nextCurrentProjectId !== String(identity.currentProjectId || "").trim() ||
|
|
805
805
|
JSON.stringify(nextProjectCursors) !== JSON.stringify(identity.projectCursors ?? {})
|
|
806
|
-
if (!changed) return
|
|
806
|
+
if (!changed) return true
|
|
807
807
|
await backupCurrentIdentityIfExists(filePath)
|
|
808
808
|
await writeIdentity(filePath, nextIdentity)
|
|
809
809
|
return true
|
|
@@ -2137,25 +2137,57 @@ async function cmdTaskList(args: Argv): Promise<void> {
|
|
|
2137
2137
|
throw new Error(JSON.stringify(json))
|
|
2138
2138
|
}
|
|
2139
2139
|
const includeArchived = optBoolean(args.flags, "includeArchived", false)
|
|
2140
|
+
const includeContainers = optBoolean(args.flags, "includeContainers", false)
|
|
2140
2141
|
const originalTasks = Array.isArray((json.data as any)?.tasks) ? ((json.data as any).tasks as any[]) : []
|
|
2141
|
-
const
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
const
|
|
2145
|
-
const
|
|
2142
|
+
const archivedFilteredCount = includeArchived
|
|
2143
|
+
? 0
|
|
2144
|
+
: originalTasks.filter((t) => String(t?.status ?? "").trim().toLowerCase() === "archived").length
|
|
2145
|
+
const tasksAfterArchivePolicy = includeArchived ? originalTasks : originalTasks.filter((t) => String(t?.status ?? "") !== "archived")
|
|
2146
|
+
const taskById = buildTaskListTaskMap(tasksAfterArchivePolicy)
|
|
2147
|
+
const now = Date.now()
|
|
2148
|
+
const executionTasks = tasksAfterArchivePolicy
|
|
2149
|
+
.filter((t) => !isContainerTaskLike(t))
|
|
2150
|
+
.map((t) => {
|
|
2151
|
+
const dependencyReady = isTaskDependencyReadyForList(t, taskById)
|
|
2152
|
+
const taskBoardBucket = classifyTaskBoardBucketForList({ ...t, dependencyReady }, taskById, now)
|
|
2153
|
+
return {
|
|
2154
|
+
...t,
|
|
2155
|
+
dependencyReady,
|
|
2156
|
+
claimedByMe: isTaskMine(t, identity.lobsterId),
|
|
2157
|
+
taskBoardBucket,
|
|
2158
|
+
}
|
|
2159
|
+
})
|
|
2160
|
+
const containerTasks = tasksAfterArchivePolicy.filter((t) => isContainerTaskLike(t))
|
|
2161
|
+
const tasks = includeContainers ? tasksAfterArchivePolicy : executionTasks
|
|
2162
|
+
const doneCount = executionTasks.filter((t) => t.taskBoardBucket === "done").length
|
|
2163
|
+
const focusCount = executionTasks.filter((t) => t.taskBoardBucket === "focus").length
|
|
2164
|
+
const waitingCount = executionTasks.filter((t) => t.taskBoardBucket === "waiting").length
|
|
2165
|
+
const unclaimedCount = executionTasks.filter((t) => t.taskBoardBucket === "unclaimed").length
|
|
2166
|
+
const myTakenCount = executionTasks.filter((t) => ["focus", "waiting"].includes(String(t.taskBoardBucket || "")) && t.claimedByMe === true).length
|
|
2167
|
+
const takenCount = executionTasks.filter((t) => String(t?.status ?? "").toLowerCase() === "taken").length
|
|
2168
|
+
const openCount = executionTasks.filter((t) => String(t?.status ?? "").toLowerCase() === "open").length
|
|
2146
2169
|
print({
|
|
2147
2170
|
...(json.data as Record<string, unknown>),
|
|
2148
2171
|
projectId,
|
|
2149
2172
|
projectName: projectRef.projectName ?? (json.data as any)?.projectName,
|
|
2150
2173
|
tasks,
|
|
2174
|
+
executionTasks,
|
|
2175
|
+
containerTasks: includeContainers ? containerTasks : undefined,
|
|
2151
2176
|
summary: {
|
|
2152
2177
|
done: doneCount,
|
|
2178
|
+
focus: focusCount,
|
|
2179
|
+
waiting: waitingCount,
|
|
2180
|
+
unclaimed: unclaimedCount,
|
|
2181
|
+
mineTaken: myTakenCount,
|
|
2182
|
+
},
|
|
2183
|
+
rawStatusSummary: {
|
|
2153
2184
|
taken: takenCount,
|
|
2154
2185
|
open: openCount,
|
|
2155
|
-
mineTaken: myTakenCount,
|
|
2156
2186
|
},
|
|
2187
|
+
listView: includeContainers ? "all_tasks" : "execution_tasks_only",
|
|
2157
2188
|
listPolicy: includeArchived ? "all" : "exclude_archived",
|
|
2158
|
-
archivedFilteredCount
|
|
2189
|
+
archivedFilteredCount,
|
|
2190
|
+
containerExcludedCount: includeContainers ? 0 : containerTasks.length,
|
|
2159
2191
|
nextSuggestions: [
|
|
2160
2192
|
"如需查看剩余空间,执行:project storage-remaining",
|
|
2161
2193
|
"可继续上传文档或更新任务进度",
|
|
@@ -4093,6 +4125,7 @@ function buildTodoCliView(
|
|
|
4093
4125
|
const base = {
|
|
4094
4126
|
ok: true,
|
|
4095
4127
|
mode,
|
|
4128
|
+
fetchedAt: now,
|
|
4096
4129
|
count: enriched.length,
|
|
4097
4130
|
activeCount: enriched.filter((row) => String(row.status || "") !== "done").length,
|
|
4098
4131
|
todayItems,
|
|
@@ -4125,6 +4158,85 @@ function buildTodoCliView(
|
|
|
4125
4158
|
return base
|
|
4126
4159
|
}
|
|
4127
4160
|
|
|
4161
|
+
function buildMemberListCliView(raw: unknown, viewerLobsterId: string): Record<string, unknown> {
|
|
4162
|
+
const source = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : {}
|
|
4163
|
+
const members = Array.isArray(source.members) ? (source.members as Array<Record<string, unknown>>) : []
|
|
4164
|
+
const enriched = members.map((row) => {
|
|
4165
|
+
const lobsterId = String(row.lobsterId ?? "").trim()
|
|
4166
|
+
const role = String(row.role ?? "member").trim().toLowerCase()
|
|
4167
|
+
const memberType = String(row.memberType ?? "lobster").trim().toLowerCase()
|
|
4168
|
+
return {
|
|
4169
|
+
...row,
|
|
4170
|
+
lobsterId,
|
|
4171
|
+
role,
|
|
4172
|
+
memberType,
|
|
4173
|
+
isMe: lobsterId === viewerLobsterId,
|
|
4174
|
+
joinedAtText: typeof row.joinedAt === "number" && Number.isFinite(row.joinedAt) ? new Date(Number(row.joinedAt)).toLocaleString() : "",
|
|
4175
|
+
}
|
|
4176
|
+
})
|
|
4177
|
+
const summary = {
|
|
4178
|
+
members: enriched.length,
|
|
4179
|
+
founder: enriched.filter((row) => row.role === "founder").length,
|
|
4180
|
+
leader: enriched.filter((row) => row.role === "leader").length,
|
|
4181
|
+
member: enriched.filter((row) => row.role === "member").length,
|
|
4182
|
+
autoAgent: enriched.filter((row) => row.memberType === "lobster").length,
|
|
4183
|
+
manualMember: enriched.filter((row) => row.memberType === "member").length,
|
|
4184
|
+
}
|
|
4185
|
+
return {
|
|
4186
|
+
...source,
|
|
4187
|
+
fetchedAt: Date.now(),
|
|
4188
|
+
members: enriched,
|
|
4189
|
+
summary,
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
function buildDocListCliView(raw: unknown): Record<string, unknown> {
|
|
4194
|
+
const source = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : {}
|
|
4195
|
+
const docs = Array.isArray(source.docs) ? (source.docs as Array<Record<string, unknown>>) : []
|
|
4196
|
+
const enriched = docs.map((row) => {
|
|
4197
|
+
const fileSize = Number(row.fileSize ?? 0)
|
|
4198
|
+
const updatedAt = Number(row.updatedAt ?? 0)
|
|
4199
|
+
return {
|
|
4200
|
+
...row,
|
|
4201
|
+
fileSize,
|
|
4202
|
+
updatedAt,
|
|
4203
|
+
updatedAtText: updatedAt > 0 ? new Date(updatedAt).toLocaleString() : "",
|
|
4204
|
+
}
|
|
4205
|
+
})
|
|
4206
|
+
const totalBytes = enriched.reduce((sum, row) => sum + (Number.isFinite(Number(row.fileSize ?? 0)) ? Number(row.fileSize ?? 0) : 0), 0)
|
|
4207
|
+
const summary = {
|
|
4208
|
+
docs: enriched.length,
|
|
4209
|
+
totalBytes,
|
|
4210
|
+
latestUpdatedAt: enriched.reduce((max, row) => Math.max(max, Number(row.updatedAt ?? 0) || 0), 0),
|
|
4211
|
+
}
|
|
4212
|
+
return {
|
|
4213
|
+
...source,
|
|
4214
|
+
fetchedAt: Date.now(),
|
|
4215
|
+
docs: enriched,
|
|
4216
|
+
summary: {
|
|
4217
|
+
...summary,
|
|
4218
|
+
latestUpdatedAtText: summary.latestUpdatedAt > 0 ? new Date(summary.latestUpdatedAt).toLocaleString() : "",
|
|
4219
|
+
},
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
|
|
4223
|
+
function normalizeTodoMutationCliResult(raw: unknown): Record<string, unknown> | unknown {
|
|
4224
|
+
const source = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : undefined
|
|
4225
|
+
if (!source) return raw
|
|
4226
|
+
if (source.currentTodoPlan || Array.isArray(source.items)) {
|
|
4227
|
+
const view = buildTodoCliView(source, "plan")
|
|
4228
|
+
return {
|
|
4229
|
+
...source,
|
|
4230
|
+
...view,
|
|
4231
|
+
fetchedAt: Date.now(),
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
return {
|
|
4235
|
+
...source,
|
|
4236
|
+
fetchedAt: Date.now(),
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4128
4240
|
async function cmdTodoList(
|
|
4129
4241
|
args: Argv,
|
|
4130
4242
|
mode: "list" | "plan" | "today" | "tomorrow" | "calendar" = "list"
|
|
@@ -4183,7 +4295,7 @@ async function cmdTodoUpsert(args: Argv): Promise<void> {
|
|
|
4183
4295
|
body,
|
|
4184
4296
|
})
|
|
4185
4297
|
if (status === 200) {
|
|
4186
|
-
print(json.data)
|
|
4298
|
+
print(normalizeTodoMutationCliResult(json.data))
|
|
4187
4299
|
return
|
|
4188
4300
|
}
|
|
4189
4301
|
if (!isTodoRestRouteMissing(status, json)) throw new Error(JSON.stringify(json))
|
|
@@ -4192,7 +4304,7 @@ async function cmdTodoUpsert(args: Argv): Promise<void> {
|
|
|
4192
4304
|
buildPanelStyleCommand("todo", "upsert", body),
|
|
4193
4305
|
optString(args.flags, "password")
|
|
4194
4306
|
)
|
|
4195
|
-
print(data)
|
|
4307
|
+
print(normalizeTodoMutationCliResult(data))
|
|
4196
4308
|
}
|
|
4197
4309
|
|
|
4198
4310
|
async function cmdTodoComplete(args: Argv): Promise<void> {
|
|
@@ -4215,7 +4327,7 @@ async function cmdTodoComplete(args: Argv): Promise<void> {
|
|
|
4215
4327
|
body,
|
|
4216
4328
|
})
|
|
4217
4329
|
if (status === 200) {
|
|
4218
|
-
print(json.data)
|
|
4330
|
+
print(normalizeTodoMutationCliResult(json.data))
|
|
4219
4331
|
return
|
|
4220
4332
|
}
|
|
4221
4333
|
if (!isTodoRestRouteMissing(status, json)) throw new Error(JSON.stringify(json))
|
|
@@ -4224,7 +4336,7 @@ async function cmdTodoComplete(args: Argv): Promise<void> {
|
|
|
4224
4336
|
buildPanelStyleCommand("todo", "complete", body),
|
|
4225
4337
|
optString(args.flags, "password")
|
|
4226
4338
|
)
|
|
4227
|
-
print(data)
|
|
4339
|
+
print(normalizeTodoMutationCliResult(data))
|
|
4228
4340
|
}
|
|
4229
4341
|
|
|
4230
4342
|
async function cmdTodoDelete(args: Argv): Promise<void> {
|
|
@@ -4247,7 +4359,7 @@ async function cmdTodoDelete(args: Argv): Promise<void> {
|
|
|
4247
4359
|
body,
|
|
4248
4360
|
})
|
|
4249
4361
|
if (status === 200) {
|
|
4250
|
-
print(json.data)
|
|
4362
|
+
print(normalizeTodoMutationCliResult(json.data))
|
|
4251
4363
|
return
|
|
4252
4364
|
}
|
|
4253
4365
|
if (!isTodoRestRouteMissing(status, json)) throw new Error(JSON.stringify(json))
|
|
@@ -4256,7 +4368,7 @@ async function cmdTodoDelete(args: Argv): Promise<void> {
|
|
|
4256
4368
|
buildPanelStyleCommand("todo", "delete", body),
|
|
4257
4369
|
optString(args.flags, "password")
|
|
4258
4370
|
)
|
|
4259
|
-
print(data)
|
|
4371
|
+
print(normalizeTodoMutationCliResult(data))
|
|
4260
4372
|
}
|
|
4261
4373
|
|
|
4262
4374
|
async function cmdTodoRegenerate(args: Argv): Promise<void> {
|
|
@@ -4341,7 +4453,7 @@ async function cmdMemberList(args: Argv): Promise<void> {
|
|
|
4341
4453
|
secretKeyBase64: identity.secretKeyBase64,
|
|
4342
4454
|
})
|
|
4343
4455
|
if (status !== 200) throw new Error(JSON.stringify(json))
|
|
4344
|
-
print(json.data)
|
|
4456
|
+
print(buildMemberListCliView(json.data, identity.lobsterId))
|
|
4345
4457
|
}
|
|
4346
4458
|
|
|
4347
4459
|
async function cmdMemberAddWeb(args: Argv): Promise<void> {
|
|
@@ -4955,6 +5067,32 @@ function parseIssueAttachmentPaths(raw?: string): string[] {
|
|
|
4955
5067
|
.slice(0, ISSUE_ATTACHMENT_MAX_FILES)
|
|
4956
5068
|
}
|
|
4957
5069
|
|
|
5070
|
+
const DOC_UPLOAD_MIME_MAP: Record<string, string> = {
|
|
5071
|
+
".txt": "text/plain",
|
|
5072
|
+
".md": "text/markdown",
|
|
5073
|
+
".markdown": "text/markdown",
|
|
5074
|
+
".json": "application/json",
|
|
5075
|
+
".csv": "text/csv",
|
|
5076
|
+
".pdf": "application/pdf",
|
|
5077
|
+
".zip": "application/zip",
|
|
5078
|
+
".doc": "application/msword",
|
|
5079
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5080
|
+
".xls": "application/vnd.ms-excel",
|
|
5081
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
5082
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
5083
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
5084
|
+
".png": "image/png",
|
|
5085
|
+
".jpg": "image/jpeg",
|
|
5086
|
+
".jpeg": "image/jpeg",
|
|
5087
|
+
".gif": "image/gif",
|
|
5088
|
+
".webp": "image/webp",
|
|
5089
|
+
}
|
|
5090
|
+
|
|
5091
|
+
function guessDocMimeType(filePath: string): string {
|
|
5092
|
+
const ext = path.extname(String(filePath || "")).toLowerCase()
|
|
5093
|
+
return DOC_UPLOAD_MIME_MAP[ext] ?? "application/octet-stream"
|
|
5094
|
+
}
|
|
5095
|
+
|
|
4958
5096
|
async function loadIssueAttachments(attachmentPath?: string): Promise<Array<{ fileName: string; mimeType: string; contentBase64: string }>> {
|
|
4959
5097
|
const paths = parseIssueAttachmentPaths(attachmentPath)
|
|
4960
5098
|
if (!paths.length) return []
|
|
@@ -5292,10 +5430,73 @@ function parseProjectScope(flags: Record<string, string | boolean>, allProjects:
|
|
|
5292
5430
|
function isTaskMine(task: any, lobsterId: string): boolean {
|
|
5293
5431
|
if (!task || typeof task !== "object") return false
|
|
5294
5432
|
if (task.claimedByMe === true || task.mine === true) return true
|
|
5295
|
-
const assignee = String(task.assigneeLobsterId ??
|
|
5433
|
+
const assignee = String(task.assigneeLobsterId ?? "")
|
|
5296
5434
|
return assignee.length > 0 && assignee === lobsterId
|
|
5297
5435
|
}
|
|
5298
5436
|
|
|
5437
|
+
function isContainerTaskLike(task: any): boolean {
|
|
5438
|
+
return String(task?.taskKind ?? "").trim().toLowerCase() === "container"
|
|
5439
|
+
}
|
|
5440
|
+
|
|
5441
|
+
function normalizeTaskListStatus(task: any): "todo" | "in_progress" | "done" | "archived" | "blocked" {
|
|
5442
|
+
const raw = String(task?.status ?? "").trim().toLowerCase()
|
|
5443
|
+
if (raw === "done") return "done"
|
|
5444
|
+
if (raw === "archived") return "archived"
|
|
5445
|
+
if (raw === "blocked") return "blocked"
|
|
5446
|
+
if (raw === "taken" || raw === "review") return "in_progress"
|
|
5447
|
+
return "todo"
|
|
5448
|
+
}
|
|
5449
|
+
|
|
5450
|
+
function buildTaskListTaskMap(tasks: any[]): Map<string, any> {
|
|
5451
|
+
return new Map(
|
|
5452
|
+
tasks
|
|
5453
|
+
.map((task) => [String(task?.taskId ?? "").trim(), task] as const)
|
|
5454
|
+
.filter(([taskId]) => Boolean(taskId))
|
|
5455
|
+
)
|
|
5456
|
+
}
|
|
5457
|
+
|
|
5458
|
+
function isTaskDependencyReadyForList(task: any, taskById: Map<string, any>): boolean {
|
|
5459
|
+
if (typeof task?.dependencyReady === "boolean") return Boolean(task.dependencyReady)
|
|
5460
|
+
const deps = Array.isArray(task?.dependsOnTaskIds) ? task.dependsOnTaskIds.map((x: any) => String(x ?? "").trim()).filter(Boolean) : []
|
|
5461
|
+
if (!deps.length) return true
|
|
5462
|
+
return deps.every((depId) => {
|
|
5463
|
+
const dep = taskById.get(depId)
|
|
5464
|
+
if (!dep) return false
|
|
5465
|
+
if (isContainerTaskLike(dep)) {
|
|
5466
|
+
const childExecution = Array.from(taskById.values()).filter(
|
|
5467
|
+
(x) =>
|
|
5468
|
+
!isContainerTaskLike(x) &&
|
|
5469
|
+
String(x?.status ?? "").trim().toLowerCase() !== "archived" &&
|
|
5470
|
+
String(x?.parentContainerId ?? "").trim() === depId
|
|
5471
|
+
)
|
|
5472
|
+
if (!childExecution.length) return normalizeTaskListStatus(dep) === "done"
|
|
5473
|
+
return childExecution.every((x) => normalizeTaskListStatus(x) === "done")
|
|
5474
|
+
}
|
|
5475
|
+
return normalizeTaskListStatus(dep) === "done"
|
|
5476
|
+
})
|
|
5477
|
+
}
|
|
5478
|
+
|
|
5479
|
+
function isTaskActivatedForList(task: any, taskById: Map<string, any>, now = Date.now()): boolean {
|
|
5480
|
+
const status = normalizeTaskListStatus(task)
|
|
5481
|
+
if (status === "done" || status === "archived") return false
|
|
5482
|
+
const manualFocus = task?.showInFocus === true
|
|
5483
|
+
const depReady = isTaskDependencyReadyForList(task, taskById)
|
|
5484
|
+
const plannedStartAt = Number(task?.plannedStartAt ?? 0)
|
|
5485
|
+
const hasDeps = Array.isArray(task?.dependsOnTaskIds) && task.dependsOnTaskIds.length > 0
|
|
5486
|
+
const timeReady = hasDeps || !(plannedStartAt > 0) || plannedStartAt <= now
|
|
5487
|
+
if (manualFocus) return depReady
|
|
5488
|
+
return depReady && timeReady
|
|
5489
|
+
}
|
|
5490
|
+
|
|
5491
|
+
function classifyTaskBoardBucketForList(task: any, taskById: Map<string, any>, now = Date.now()): "focus" | "waiting" | "unclaimed" | "done" | "archived" {
|
|
5492
|
+
const status = normalizeTaskListStatus(task)
|
|
5493
|
+
if (status === "done") return "done"
|
|
5494
|
+
if (status === "archived") return "archived"
|
|
5495
|
+
const hasAssignee = Boolean(String(task?.assigneeLobsterId ?? "").trim())
|
|
5496
|
+
if (!hasAssignee) return "unclaimed"
|
|
5497
|
+
return isTaskActivatedForList(task, taskById, now) ? "focus" : "waiting"
|
|
5498
|
+
}
|
|
5499
|
+
|
|
5299
5500
|
async function cmdTaskListCross(args: Argv): Promise<void> {
|
|
5300
5501
|
const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
|
|
5301
5502
|
const identity = await readIdentity(filePath)
|
|
@@ -5581,12 +5782,97 @@ async function cmdDocList(args: Argv): Promise<void> {
|
|
|
5581
5782
|
secretKeyBase64: identity.secretKeyBase64,
|
|
5582
5783
|
})
|
|
5583
5784
|
if (status !== 200) throw new Error(JSON.stringify(json))
|
|
5584
|
-
print(json.data)
|
|
5785
|
+
print(buildDocListCliView(json.data))
|
|
5585
5786
|
}
|
|
5586
5787
|
|
|
5587
5788
|
async function cmdDocCreate(args: Argv): Promise<void> {
|
|
5588
|
-
|
|
5589
|
-
|
|
5789
|
+
const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
|
|
5790
|
+
const identity = await readIdentity(filePath)
|
|
5791
|
+
const resolved = await resolveProjectRef(args.flags, identity)
|
|
5792
|
+
const interactive = Boolean(args.flags.interactive || args.flags.prompt)
|
|
5793
|
+
const sourceFileRaw = interactive
|
|
5794
|
+
? await askRequired("请选择要上传的文件路径: ", optString(args.flags, "file"))
|
|
5795
|
+
: mustString(args.flags, "file")
|
|
5796
|
+
const sourceFile = path.resolve(sourceFileRaw)
|
|
5797
|
+
let stat
|
|
5798
|
+
try {
|
|
5799
|
+
stat = await fs.stat(sourceFile)
|
|
5800
|
+
} catch {
|
|
5801
|
+
throw new Error(`invalid_doc_file_path: ${sourceFileRaw}`)
|
|
5802
|
+
}
|
|
5803
|
+
if (!stat.isFile()) throw new Error(`invalid_doc_file_path: ${sourceFileRaw}`)
|
|
5804
|
+
const bytes = await fs.readFile(sourceFile)
|
|
5805
|
+
const uploadNameFallback = path.basename(sourceFile)
|
|
5806
|
+
const uploadTypeFallback = guessDocMimeType(sourceFile)
|
|
5807
|
+
const docName = interactive
|
|
5808
|
+
? await askRequired("请输入上传后显示的文件名: ", optString(args.flags, "name") ?? optString(args.flags, "title") ?? uploadNameFallback)
|
|
5809
|
+
: String(optString(args.flags, "name") ?? optString(args.flags, "title") ?? uploadNameFallback).trim()
|
|
5810
|
+
const docType = interactive
|
|
5811
|
+
? await askRequired("请输入文件类型(可留空走自动识别): ", optString(args.flags, "type") ?? optString(args.flags, "docType") ?? uploadTypeFallback)
|
|
5812
|
+
: String(optString(args.flags, "type") ?? optString(args.flags, "docType") ?? uploadTypeFallback).trim()
|
|
5813
|
+
const fileSize = bytes.length
|
|
5814
|
+
if (fileSize > MAX_SHARED_DOC_FILE_BYTES) {
|
|
5815
|
+
throw new Error("doc_file_too_large: 单文件超过 10MB,请压缩后重试。")
|
|
5816
|
+
}
|
|
5817
|
+
const contentHash = sha256Hex(bytes)
|
|
5818
|
+
const storageType = optString(args.flags, "storageType")
|
|
5819
|
+
const storageProvider = optString(args.flags, "storageProvider")
|
|
5820
|
+
const storagePath = optString(args.flags, "storagePath") ?? sourceFile
|
|
5821
|
+
const tags = String(optString(args.flags, "tags") ?? "")
|
|
5822
|
+
.split(",")
|
|
5823
|
+
.map((x) => x.trim())
|
|
5824
|
+
.filter(Boolean)
|
|
5825
|
+
const relatedTaskIds = String(optString(args.flags, "tasks") ?? optString(args.flags, "task") ?? "")
|
|
5826
|
+
.split(",")
|
|
5827
|
+
.map((x) => x.trim())
|
|
5828
|
+
.filter(Boolean)
|
|
5829
|
+
const version = optString(args.flags, "version") ?? "v1"
|
|
5830
|
+
const description = optString(args.flags, "desc") ?? optString(args.flags, "description")
|
|
5831
|
+
const changeSummary = optString(args.flags, "summary")
|
|
5832
|
+
const idempotencyKey = createRequestId(`doc_upload_${path.basename(sourceFile)}`)
|
|
5833
|
+
const { status, json } = await signedRequest({
|
|
5834
|
+
serverBaseUrl: identity.serverBaseUrl,
|
|
5835
|
+
method: "POST",
|
|
5836
|
+
pathWithQuery: `/v1/projects/${resolved.projectId}/docs`,
|
|
5837
|
+
lobsterId: identity.lobsterId,
|
|
5838
|
+
secretKeyBase64: identity.secretKeyBase64,
|
|
5839
|
+
idempotencyKey,
|
|
5840
|
+
body: {
|
|
5841
|
+
docName,
|
|
5842
|
+
docType,
|
|
5843
|
+
fileSize,
|
|
5844
|
+
contentHash,
|
|
5845
|
+
contentBase64: bytes.toString("base64"),
|
|
5846
|
+
storageLocation: { type: storageType ?? undefined, provider: storageProvider, path: storagePath },
|
|
5847
|
+
version,
|
|
5848
|
+
relatedTaskIds,
|
|
5849
|
+
tags,
|
|
5850
|
+
description,
|
|
5851
|
+
changeSummary,
|
|
5852
|
+
},
|
|
5853
|
+
})
|
|
5854
|
+
if (status !== 200) {
|
|
5855
|
+
const msg = String((json as any)?.msg ?? (json as any)?.message ?? "")
|
|
5856
|
+
if (status === 409 && msg.includes("storage_quota_exceeded")) {
|
|
5857
|
+
throw new Error("storage_quota_exceeded: 存储空间已达上限,请联系 founder 升级容量后再上传。")
|
|
5858
|
+
}
|
|
5859
|
+
if (status === 409 && msg === "invalid_storage_profile") {
|
|
5860
|
+
throw new Error("invalid_storage_profile: 当前项目存储配置无效,请联系 founder 检查项目存储设置。")
|
|
5861
|
+
}
|
|
5862
|
+
throw new Error(JSON.stringify(json))
|
|
5863
|
+
}
|
|
5864
|
+
print({
|
|
5865
|
+
ok: true,
|
|
5866
|
+
projectId: resolved.projectId,
|
|
5867
|
+
projectName: resolved.projectName ?? "",
|
|
5868
|
+
docName,
|
|
5869
|
+
docType,
|
|
5870
|
+
sourceFile,
|
|
5871
|
+
fileSize,
|
|
5872
|
+
contentHash,
|
|
5873
|
+
uploadedAt: Date.now(),
|
|
5874
|
+
...(json.data && typeof json.data === "object" ? (json.data as Record<string, unknown>) : {}),
|
|
5875
|
+
})
|
|
5590
5876
|
}
|
|
5591
5877
|
|
|
5592
5878
|
async function cmdDocDetail(args: Argv): Promise<void> {
|
|
@@ -18,7 +18,9 @@ function toUserError(raw: string): string {
|
|
|
18
18
|
if (msg.includes("interactive_input_required")) return "必填内容不能为空,请补充参数后重试。"
|
|
19
19
|
if (msg.includes("missing_project_name")) return "项目名不能为空,请通过 --name 提供有效项目名。"
|
|
20
20
|
if (msg.includes("project_name_too_long")) return "项目名不能超过80个字符,请缩短后重试。"
|
|
21
|
-
if (msg.includes("doc_upload_board_only")) return "
|
|
21
|
+
if (msg.includes("doc_upload_board_only")) return "当前运行时版本过旧,文件上传仍被禁用。请先更新微元系统后再试。"
|
|
22
|
+
if (msg.includes("invalid_doc_file_path")) return "上传文件路径不存在,或该路径不是一个可读取的文件。请检查 --file 后重试。"
|
|
23
|
+
if (msg.includes("invalid_storage_profile")) return "当前项目的存储配置异常,暂时无法上传文件。请联系项目 founder 检查存储设置。"
|
|
22
24
|
if (msg.includes("invalid_project_name")) return "项目名仅支持中文/英文/数字/空格及 -_.()·,且不能包含HTML标签。"
|
|
23
25
|
if (msg.includes("task_title_contains_html")) return "任务标题不能包含HTML标签(如 <script>)。"
|
|
24
26
|
if (msg.includes("task_title_too_long")) return "任务标题不能超过120个字符,请缩短后重试。"
|
|
@@ -129,14 +129,20 @@ const DEFAULT_MICRO_RULES: MicroRulesConfig = {
|
|
|
129
129
|
commandList: [
|
|
130
130
|
"检查项目",
|
|
131
131
|
"查看任务",
|
|
132
|
+
"查看成员",
|
|
133
|
+
"查看文件",
|
|
132
134
|
"打开看板",
|
|
133
135
|
"打开驾驶舱",
|
|
134
136
|
"项目列表",
|
|
135
137
|
"任务列表",
|
|
138
|
+
"成员列表",
|
|
139
|
+
"文件列表",
|
|
136
140
|
"创建项目",
|
|
137
141
|
"创建任务",
|
|
138
142
|
"认领任务",
|
|
139
143
|
"完成任务",
|
|
144
|
+
"新增待办",
|
|
145
|
+
"删除待办",
|
|
140
146
|
"项目状态",
|
|
141
147
|
"微元回复用简洁模式",
|
|
142
148
|
"微元回复用详细模式",
|
|
@@ -153,7 +159,7 @@ const DEFAULT_MICRO_RULES: MicroRulesConfig = {
|
|
|
153
159
|
"通知中心",
|
|
154
160
|
"生成邀请码",
|
|
155
161
|
],
|
|
156
|
-
keywords: ["微元", "项目", "任务", "待办", "计划", "看板", "备份", "风险", "同步", "存储", "文档", "胶囊"],
|
|
162
|
+
keywords: ["微元", "项目", "任务", "待办", "计划", "看板", "成员", "文件", "备份", "风险", "同步", "存储", "文档", "胶囊"],
|
|
157
163
|
detailTriggers: ["详细", "详情", "全部", "原始", "完整", "debug", "调试", "json", "id", "地址"],
|
|
158
164
|
hardRequirements: [
|
|
159
165
|
"微元场景回复必须带【微元协作】前缀。",
|
|
@@ -356,6 +362,90 @@ function extractNicknameFromText(text: string): string | undefined {
|
|
|
356
362
|
return v || undefined
|
|
357
363
|
}
|
|
358
364
|
|
|
365
|
+
function extractMemberProfileFieldFromText(
|
|
366
|
+
text: string,
|
|
367
|
+
field: "roleTitle" | "specialties" | "experienceSummary" | "strengthsSummary" | "taskPreferenceTags"
|
|
368
|
+
): string | undefined {
|
|
369
|
+
const patterns: RegExp[] =
|
|
370
|
+
field === "roleTitle"
|
|
371
|
+
? [
|
|
372
|
+
/(?:设置|修改|更改|更新)(?:我的)?(?:团队)?角色(?:与能力)?(?:为|叫|成)?\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
373
|
+
/(?:我的)?(?:团队)?角色(?:是|为|叫|改成|改为)\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
374
|
+
]
|
|
375
|
+
: field === "specialties"
|
|
376
|
+
? [
|
|
377
|
+
/(?:设置|修改|更改|更新)(?:我的)?(?:擅长方向|擅长|专长|特长方向)(?:为|成)?\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
378
|
+
/(?:我的)?(?:擅长方向|擅长|专长|特长方向)(?:是|为|改成|改为)\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
379
|
+
]
|
|
380
|
+
: field === "experienceSummary"
|
|
381
|
+
? [
|
|
382
|
+
/(?:设置|修改|更改|更新)(?:我的)?(?:经历概述|经历简介|经验概述|经验简介|经历)(?:为|成)?\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
383
|
+
/(?:我的)?(?:经历概述|经历简介|经验概述|经验简介|经历)(?:是|为|改成|改为)\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
384
|
+
]
|
|
385
|
+
: field === "strengthsSummary"
|
|
386
|
+
? [
|
|
387
|
+
/(?:设置|修改|更改|更新)(?:我的)?(?:特长概述|优势概述|优势简介|特长简介|特长)(?:为|成)?\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
388
|
+
/(?:我的)?(?:特长概述|优势概述|优势简介|特长简介|特长)(?:是|为|改成|改为)\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
389
|
+
]
|
|
390
|
+
: [
|
|
391
|
+
/(?:设置|修改|更改|更新)(?:我的)?(?:偏好任务标签|任务偏好标签|偏好标签|任务标签)(?:为|成)?\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
392
|
+
/(?:我的)?(?:偏好任务标签|任务偏好标签|偏好标签|任务标签)(?:是|为|改成|改为)\s*[“"']?([^”"'\n]+)[”"']?$/u,
|
|
393
|
+
]
|
|
394
|
+
for (const pattern of patterns) {
|
|
395
|
+
const value = String(text.match(pattern)?.[1] ?? "").trim()
|
|
396
|
+
if (value) return value
|
|
397
|
+
}
|
|
398
|
+
return undefined
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function hasMemberProfileCardIntent(text: string): boolean {
|
|
402
|
+
const compact = text.toLowerCase().replace(/\s+/g, "")
|
|
403
|
+
return (
|
|
404
|
+
compact.includes("成员角色卡片") ||
|
|
405
|
+
compact.includes("角色卡片") ||
|
|
406
|
+
compact.includes("成员卡片") ||
|
|
407
|
+
compact.includes("编辑成员信息") ||
|
|
408
|
+
compact.includes("修改成员信息") ||
|
|
409
|
+
compact.includes("设置角色卡片") ||
|
|
410
|
+
compact.includes("角色与能力") ||
|
|
411
|
+
compact.includes("我的角色") ||
|
|
412
|
+
compact.includes("修改角色") ||
|
|
413
|
+
compact.includes("更新角色") ||
|
|
414
|
+
compact.includes("团队角色") ||
|
|
415
|
+
compact.includes("擅长方向") ||
|
|
416
|
+
compact.includes("我的擅长") ||
|
|
417
|
+
compact.includes("我的专长") ||
|
|
418
|
+
compact.includes("经历概述") ||
|
|
419
|
+
compact.includes("经历简介") ||
|
|
420
|
+
compact.includes("经验概述") ||
|
|
421
|
+
compact.includes("我的经历") ||
|
|
422
|
+
compact.includes("特长概述") ||
|
|
423
|
+
compact.includes("优势概述") ||
|
|
424
|
+
compact.includes("我的特长") ||
|
|
425
|
+
compact.includes("我的优势") ||
|
|
426
|
+
compact.includes("偏好任务标签") ||
|
|
427
|
+
compact.includes("任务偏好标签")
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function extractMemberRemarkIntent(text: string): { target?: string; remark?: string; clear?: boolean } | undefined {
|
|
432
|
+
const trimmed = String(text || "").trim()
|
|
433
|
+
if (!trimmed) return undefined
|
|
434
|
+
const clearMatch =
|
|
435
|
+
trimmed.match(/(?:清空|删除|移除|去掉)(?:我对)?\s*([^,,\s]+)\s*(?:的)?备注$/u) ??
|
|
436
|
+
trimmed.match(/(?:把)?\s*([^,,\s]+)\s*(?:的)?备注(?:清空|删除|移除|去掉)$/u)
|
|
437
|
+
if (clearMatch?.[1]) {
|
|
438
|
+
return { target: clearMatch[1].trim(), clear: true, remark: "-" }
|
|
439
|
+
}
|
|
440
|
+
const saveMatch =
|
|
441
|
+
trimmed.match(/(?:给|帮我给|把)?\s*([^,,\s]+)\s*(?:的)?备注(?:为|成)?\s*([^\n]+)$/u) ??
|
|
442
|
+
trimmed.match(/(?:把)?\s*([^,,\s]+)\s*(?:备注为|备注成)\s*([^\n]+)$/u)
|
|
443
|
+
if (saveMatch?.[1]) {
|
|
444
|
+
return { target: saveMatch[1].trim(), remark: String(saveMatch[2] ?? "").trim() }
|
|
445
|
+
}
|
|
446
|
+
return undefined
|
|
447
|
+
}
|
|
448
|
+
|
|
359
449
|
function extractProjectNameForCreate(text: string): string | undefined {
|
|
360
450
|
const m =
|
|
361
451
|
text.match(/(?:创建|新建)(?:一个|个)?[“"'《【((]?\s*([^”"'》】))\n]+?项目)\s*(?:吧|呀|呢|的)?$/) ??
|
|
@@ -674,9 +764,9 @@ async function syncRulesFromSource(identityPath: string, keepMode: boolean): Pro
|
|
|
674
764
|
function isMicroSceneText(text: string, rules: MicroRulesConfig): boolean {
|
|
675
765
|
const lower = text.toLowerCase()
|
|
676
766
|
if (/(团队负载|工作负载|团队负荷|团队工作量|谁最忙|team workload|workload)/i.test(lower)) return true
|
|
677
|
-
if (/(
|
|
767
|
+
if (/(添加成员|新增成员|加入成员|普通成员|成员列表|成员情况|项目成员|文件列表|文件情况|项目文件|项目资料)/i.test(lower)) return true
|
|
678
768
|
if (
|
|
679
|
-
/(
|
|
769
|
+
/(我的项目|我有哪些项目|看看项目|看看我的项目|项目列表|查项目|创建项目|新建项目|我的任务|我有哪些任务|看看任务|看看我的任务|任务列表|查任务|创建任务|新建任务|我的待办|看看待办|看看我的待办|待办列表|今日待办|明日计划|明天计划|未来事项|打开看板|项目状态|成员列表|成员情况|项目成员|文件列表|文件情况|项目文件|项目资料)/i.test(
|
|
680
770
|
text
|
|
681
771
|
)
|
|
682
772
|
) {
|
|
@@ -1315,6 +1405,18 @@ function shouldCreateTodoFromText(lower: string): boolean {
|
|
|
1315
1405
|
)
|
|
1316
1406
|
}
|
|
1317
1407
|
|
|
1408
|
+
function shouldDeleteTodoFromText(lower: string): boolean {
|
|
1409
|
+
if (!lower) return false
|
|
1410
|
+
if (!(lower.includes("待办") || lower.includes("计划") || lower.includes("提醒"))) return false
|
|
1411
|
+
return (
|
|
1412
|
+
lower.includes("删除") ||
|
|
1413
|
+
lower.includes("删掉") ||
|
|
1414
|
+
lower.includes("去掉") ||
|
|
1415
|
+
lower.includes("移除") ||
|
|
1416
|
+
lower.includes("取消")
|
|
1417
|
+
)
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1318
1420
|
function inferTodoWhenFromText(text: string): { when?: string; bucket?: string } {
|
|
1319
1421
|
const lower = text.toLowerCase()
|
|
1320
1422
|
if (lower.includes("明天下午")) return { when: "tomorrow 15:00", bucket: "tomorrow" }
|
|
@@ -1329,11 +1431,24 @@ function inferTodoWhenFromText(text: string): { when?: string; bucket?: string }
|
|
|
1329
1431
|
}
|
|
1330
1432
|
|
|
1331
1433
|
function buildTodoActionTextFromText(text: string): string {
|
|
1332
|
-
const trimmed = String(text || "")
|
|
1434
|
+
const trimmed = String(text || "")
|
|
1435
|
+
.trim()
|
|
1436
|
+
.replace(/[。!!]+$/g, "")
|
|
1437
|
+
.replace(/^(提醒我|提醒一下|安排个待办|加个待办|记个待办|记一下|记得)\s*/u, "")
|
|
1333
1438
|
if (!trimmed) return "新的待办"
|
|
1334
1439
|
return trimmed
|
|
1335
1440
|
}
|
|
1336
1441
|
|
|
1442
|
+
function buildTodoDeleteTargetFromText(text: string): string {
|
|
1443
|
+
const trimmed = String(text || "")
|
|
1444
|
+
.trim()
|
|
1445
|
+
.replace(/[。!!]+$/g, "")
|
|
1446
|
+
.replace(/^(请)?(帮我)?(把|将)?(这个|这条)?(待办|计划|提醒)?(删除|删掉|去掉|移除|取消)\s*/u, "")
|
|
1447
|
+
.replace(/^(删除|删掉|去掉|移除|取消)(这个|这条)?(待办|计划|提醒)?\s*/u, "")
|
|
1448
|
+
.trim()
|
|
1449
|
+
return trimmed || "当前待办"
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1337
1452
|
function isRiskActionKey(actionKey: string): boolean {
|
|
1338
1453
|
return /(delete|reject|fail|rollback|conflict|error|forbidden|denied)/i.test(actionKey)
|
|
1339
1454
|
}
|
|
@@ -1730,22 +1845,33 @@ function fromText(input: SkillInput): string[] {
|
|
|
1730
1845
|
if (!nickname) return ["member-profile-edit", "--identity", identity, "--project", input.projectId]
|
|
1731
1846
|
return ["member-profile-set", "--identity", identity, "--project", input.projectId, "--nickname", nickname]
|
|
1732
1847
|
}
|
|
1733
|
-
if (
|
|
1734
|
-
compact.includes("成员角色卡片") ||
|
|
1735
|
-
compact.includes("角色卡片") ||
|
|
1736
|
-
compact.includes("成员卡片") ||
|
|
1737
|
-
compact.includes("编辑成员信息") ||
|
|
1738
|
-
compact.includes("修改成员信息") ||
|
|
1739
|
-
compact.includes("设置角色卡片")
|
|
1740
|
-
) {
|
|
1848
|
+
if (hasMemberProfileCardIntent(text)) {
|
|
1741
1849
|
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
1742
|
-
|
|
1850
|
+
const roleTitle = extractMemberProfileFieldFromText(text, "roleTitle")
|
|
1851
|
+
const specialties = extractMemberProfileFieldFromText(text, "specialties")
|
|
1852
|
+
const experienceSummary = extractMemberProfileFieldFromText(text, "experienceSummary")
|
|
1853
|
+
const strengthsSummary = extractMemberProfileFieldFromText(text, "strengthsSummary")
|
|
1854
|
+
const taskPreferenceTags = extractMemberProfileFieldFromText(text, "taskPreferenceTags")
|
|
1855
|
+
const argv = ["member-profile-edit", "--identity", identity, "--project", input.projectId]
|
|
1856
|
+
if (roleTitle) argv.push("--roleTitle", roleTitle)
|
|
1857
|
+
if (specialties) argv.push("--specialties", specialties)
|
|
1858
|
+
if (experienceSummary) argv.push("--experienceSummary", experienceSummary)
|
|
1859
|
+
if (strengthsSummary) argv.push("--strengthsSummary", strengthsSummary)
|
|
1860
|
+
if (taskPreferenceTags) argv.push("--taskPreferenceTags", taskPreferenceTags)
|
|
1861
|
+
return argv
|
|
1743
1862
|
}
|
|
1744
|
-
if (
|
|
1863
|
+
if (
|
|
1864
|
+
compact.includes("成员备注") ||
|
|
1865
|
+
compact.includes("备注成员") ||
|
|
1866
|
+
compact.includes("给成员备注") ||
|
|
1867
|
+
compact.includes("修改备注") ||
|
|
1868
|
+
compact.includes("清空备注") ||
|
|
1869
|
+
compact.includes("删除备注")
|
|
1870
|
+
) {
|
|
1745
1871
|
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
1746
|
-
const
|
|
1747
|
-
const target = (
|
|
1748
|
-
const remark = (
|
|
1872
|
+
const remarkIntent = extractMemberRemarkIntent(text)
|
|
1873
|
+
const target = String(remarkIntent?.target ?? "").trim()
|
|
1874
|
+
const remark = String(remarkIntent?.remark ?? "").trim()
|
|
1749
1875
|
if (target && remark) {
|
|
1750
1876
|
return ["panel", "member-remark", "--identity", identity, "--project", input.projectId, "--target", target, "--remark", remark]
|
|
1751
1877
|
}
|
|
@@ -1953,6 +2079,8 @@ function fromText(input: SkillInput): string[] {
|
|
|
1953
2079
|
throw new Error("confirm_required_doc_delete")
|
|
1954
2080
|
}
|
|
1955
2081
|
if (
|
|
2082
|
+
lower.includes("添加普通成员") ||
|
|
2083
|
+
lower.includes("新增普通成员") ||
|
|
1956
2084
|
lower.includes("添加网页成员") ||
|
|
1957
2085
|
lower.includes("新增网页成员") ||
|
|
1958
2086
|
lower.includes("添加成员") ||
|
|
@@ -1961,14 +2089,14 @@ function fromText(input: SkillInput): string[] {
|
|
|
1961
2089
|
) {
|
|
1962
2090
|
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
1963
2091
|
const extractedName =
|
|
1964
|
-
text.match(/(?:添加|新增|加入)(
|
|
2092
|
+
text.match(/(?:添加|新增|加入)(?:网页|普通)?成员(?:[::\s]*|为|叫|是)?[“"']?([^”"'\n]+)[”"']?$/)?.[1]?.trim() ??
|
|
1965
2093
|
text.match(/成员(?:名称|名字|昵称)?(?:为|叫|是)\s*[“"']?([^”"'\n]+)[”"']?$/)?.[1]?.trim() ??
|
|
1966
2094
|
""
|
|
1967
2095
|
const memberName = String(input.memberName ?? input.title ?? extractedName).trim()
|
|
1968
2096
|
if (memberName) {
|
|
1969
|
-
return ["member", "add
|
|
2097
|
+
return ["member", "add", "--identity", identity, "--project", input.projectId, "--name", memberName]
|
|
1970
2098
|
}
|
|
1971
|
-
return ["member", "add
|
|
2099
|
+
return ["member", "add", "--identity", identity, "--project", input.projectId, "--interactive"]
|
|
1972
2100
|
}
|
|
1973
2101
|
if (/移除.*成员|踢出.*成员|删除.*成员/.test(lower)) {
|
|
1974
2102
|
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
@@ -2119,6 +2247,33 @@ function fromText(input: SkillInput): string[] {
|
|
|
2119
2247
|
if (lower.includes("我的待办") || lower.includes("看看待办") || lower.includes("看看我的待办") || lower.includes("待办列表") || lower.includes("todo列表")) {
|
|
2120
2248
|
return ["todo", "list", "--identity", identity]
|
|
2121
2249
|
}
|
|
2250
|
+
if (
|
|
2251
|
+
lower.includes("成员列表") ||
|
|
2252
|
+
lower.includes("成员情况") ||
|
|
2253
|
+
lower.includes("项目成员") ||
|
|
2254
|
+
lower.includes("看看成员") ||
|
|
2255
|
+
lower.includes("查看成员")
|
|
2256
|
+
) {
|
|
2257
|
+
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
2258
|
+
return ["member", "list", "--identity", identity, "--project", input.projectId]
|
|
2259
|
+
}
|
|
2260
|
+
if (
|
|
2261
|
+
lower.includes("文件列表") ||
|
|
2262
|
+
lower.includes("文件情况") ||
|
|
2263
|
+
lower.includes("项目文件") ||
|
|
2264
|
+
lower.includes("项目资料") ||
|
|
2265
|
+
lower.includes("文档列表") ||
|
|
2266
|
+
lower.includes("看看文件") ||
|
|
2267
|
+
lower.includes("查看文件") ||
|
|
2268
|
+
lower.includes("看看资料")
|
|
2269
|
+
) {
|
|
2270
|
+
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
2271
|
+
return ["doc", "list", "--identity", identity, "--project", input.projectId]
|
|
2272
|
+
}
|
|
2273
|
+
if (shouldDeleteTodoFromText(lower)) {
|
|
2274
|
+
const actionText = buildTodoDeleteTargetFromText(text)
|
|
2275
|
+
return ["todo", "delete", "--identity", identity, "--actionText", actionText]
|
|
2276
|
+
}
|
|
2122
2277
|
if (shouldCreateTodoFromText(lower)) {
|
|
2123
2278
|
const todoTime = inferTodoWhenFromText(text)
|
|
2124
2279
|
const actionText = buildTodoActionTextFromText(text)
|
|
@@ -2325,8 +2480,20 @@ function fromText(input: SkillInput): string[] {
|
|
|
2325
2480
|
}
|
|
2326
2481
|
if (lower.includes("上传文件") || lower.includes("上传文档") || lower.includes("新增文档")) {
|
|
2327
2482
|
if (!input.projectId) return ["context", "candidates", "--identity", identity]
|
|
2328
|
-
if (!input.file
|
|
2329
|
-
return [
|
|
2483
|
+
if (!input.file) return ["doc", "upload", "--identity", identity, "--project", input.projectId, "--interactive"]
|
|
2484
|
+
return [
|
|
2485
|
+
"doc",
|
|
2486
|
+
"upload",
|
|
2487
|
+
"--identity",
|
|
2488
|
+
identity,
|
|
2489
|
+
"--project",
|
|
2490
|
+
input.projectId,
|
|
2491
|
+
"--file",
|
|
2492
|
+
input.file,
|
|
2493
|
+
...(input.title ? ["--name", input.title] : []),
|
|
2494
|
+
...(input.docType ? ["--type", input.docType] : []),
|
|
2495
|
+
...(input.desc ? ["--desc", input.desc] : []),
|
|
2496
|
+
]
|
|
2330
2497
|
}
|
|
2331
2498
|
if (lower.includes("老规矩") || lower.includes("上次那套")) {
|
|
2332
2499
|
if (input.projectId) return ["capsule", "recommend", "--identity", identity, "--project", input.projectId, "--query", input.query ?? ""]
|
|
@@ -2517,15 +2684,22 @@ function fromAction(input: SkillInput): string[] {
|
|
|
2517
2684
|
case "account.invite-join-template":
|
|
2518
2685
|
case "micro.invite-join-template":
|
|
2519
2686
|
return ["account", "invite-join-template", "--identity", identity]
|
|
2687
|
+
case "member.add":
|
|
2688
|
+
case "member.add-member":
|
|
2689
|
+
case "member.add_member":
|
|
2520
2690
|
case "member.add-web":
|
|
2521
2691
|
case "member.add_web":
|
|
2692
|
+
case "project.member-add":
|
|
2693
|
+
case "project.member_add":
|
|
2694
|
+
case "project.member-add-member":
|
|
2695
|
+
case "project.member_add_member":
|
|
2522
2696
|
case "project.member-add-web":
|
|
2523
2697
|
case "project.member_add_web":
|
|
2524
2698
|
if (!input.projectId) throw new Error("missing_projectId")
|
|
2525
2699
|
{
|
|
2526
2700
|
const memberName = String(input.memberName ?? input.title ?? input.summary ?? "").trim()
|
|
2527
|
-
if (!memberName) throw new Error("
|
|
2528
|
-
return ["member", "add
|
|
2701
|
+
if (!memberName) throw new Error("member_name_required")
|
|
2702
|
+
return ["member", "add", "--identity", identity, "--project", input.projectId, "--name", memberName]
|
|
2529
2703
|
}
|
|
2530
2704
|
case "member.remove":
|
|
2531
2705
|
if (!input.projectId) throw new Error("missing_projectId")
|
|
@@ -2592,8 +2766,8 @@ function fromAction(input: SkillInput): string[] {
|
|
|
2592
2766
|
]
|
|
2593
2767
|
case "doc.upload":
|
|
2594
2768
|
if (!input.projectId) throw new Error("missing_projectId")
|
|
2595
|
-
if (!input.file
|
|
2596
|
-
return ["doc", "upload", "--identity", identity, "--project", input.projectId, "--interactive", ...(input.title ? ["--name", input.title] : []), ...(input.docType ? ["--type", input.docType] : [])
|
|
2769
|
+
if (!input.file) {
|
|
2770
|
+
return ["doc", "upload", "--identity", identity, "--project", input.projectId, "--interactive", ...(input.title ? ["--name", input.title] : []), ...(input.docType ? ["--type", input.docType] : [])]
|
|
2597
2771
|
}
|
|
2598
2772
|
return [
|
|
2599
2773
|
"doc",
|
|
@@ -2602,12 +2776,10 @@ function fromAction(input: SkillInput): string[] {
|
|
|
2602
2776
|
identity,
|
|
2603
2777
|
"--project",
|
|
2604
2778
|
input.projectId,
|
|
2605
|
-
"--name",
|
|
2606
|
-
input.title,
|
|
2607
|
-
"--type",
|
|
2608
|
-
input.docType,
|
|
2609
2779
|
"--file",
|
|
2610
2780
|
input.file,
|
|
2781
|
+
...(input.title ? ["--name", input.title] : []),
|
|
2782
|
+
...(input.docType ? ["--type", input.docType] : []),
|
|
2611
2783
|
...(input.desc ? ["--desc", input.desc] : []),
|
|
2612
2784
|
]
|
|
2613
2785
|
case "doc.delete":
|
|
@@ -4573,11 +4745,11 @@ function mapError(err: unknown, detailed: boolean): SkillError {
|
|
|
4573
4745
|
if (rawError.includes("storage_quota_exceeded")) {
|
|
4574
4746
|
return { ok: false, errorCode: "CLI_ERROR", message: "存储空间已满,请联系 founder 升级容量。", ...(detailed ? { rawError } : {}) }
|
|
4575
4747
|
}
|
|
4576
|
-
if (rawError.includes("web_member_name_required")) {
|
|
4577
|
-
return { ok: false, errorCode: "CLI_ERROR", message: "
|
|
4748
|
+
if (rawError.includes("member_name_required") || rawError.includes("web_member_name_required")) {
|
|
4749
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "请先告诉我成员的名称。", ...(detailed ? { rawError } : {}) }
|
|
4578
4750
|
}
|
|
4579
|
-
if (rawError.includes("invalid_web_member_role_supported_member_only")) {
|
|
4580
|
-
return { ok: false, errorCode: "CLI_ERROR", message: "
|
|
4751
|
+
if (rawError.includes("invalid_member_role_supported_member_only") || rawError.includes("invalid_web_member_role_supported_member_only")) {
|
|
4752
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "普通成员当前仅支持 member 角色。", ...(detailed ? { rawError } : {}) }
|
|
4581
4753
|
}
|
|
4582
4754
|
if (rawError.includes("doc_file_too_large")) {
|
|
4583
4755
|
return { ok: false, errorCode: "CLI_ERROR", message: "文件上传失败:单文件超过 10MB,请压缩后重试。", ...(detailed ? { rawError } : {}) }
|