openclaw-weiyuan-init 1.0.130 → 1.0.131
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/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weiyuan-openclaw-skill",
|
|
3
|
-
"displayName": "
|
|
4
|
-
"version": "0.3.
|
|
5
|
-
"description": "
|
|
3
|
+
"displayName": "微元 OpenClaw Skill",
|
|
4
|
+
"version": "0.3.1049",
|
|
5
|
+
"description": "微元系统技能入口,支持 action 和 text 两种调用方式。",
|
|
6
6
|
"entry": {
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "npm run weiyuan:skill"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"join",
|
|
20
20
|
"project.list",
|
|
21
21
|
"project.rename",
|
|
22
|
+
"project.link_task",
|
|
22
23
|
"project.delete",
|
|
23
24
|
"project.storage",
|
|
24
25
|
"project.storage_set",
|
|
@@ -42,6 +43,9 @@
|
|
|
42
43
|
"task.update",
|
|
43
44
|
"task.take",
|
|
44
45
|
"task.delete",
|
|
46
|
+
"task.link_project",
|
|
47
|
+
"task.unlink_project",
|
|
48
|
+
"task.list_linked_project",
|
|
45
49
|
"task.submit",
|
|
46
50
|
"task.evolution_list",
|
|
47
51
|
"task.evolution_add",
|
|
@@ -141,4 +145,4 @@
|
|
|
141
145
|
"rawError": ""
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
|
-
}
|
|
148
|
+
}
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"join",
|
|
20
20
|
"project.list",
|
|
21
21
|
"project.rename",
|
|
22
|
+
"project.link_task",
|
|
23
|
+
"project.link-task",
|
|
22
24
|
"project.delete",
|
|
23
25
|
"project.storage",
|
|
24
26
|
"project.storage_set",
|
|
@@ -52,6 +54,12 @@
|
|
|
52
54
|
"task.update",
|
|
53
55
|
"task.take",
|
|
54
56
|
"task.delete",
|
|
57
|
+
"task.link_project",
|
|
58
|
+
"task.link-project",
|
|
59
|
+
"task.unlink_project",
|
|
60
|
+
"task.unlink-project",
|
|
61
|
+
"task.list_linked_project",
|
|
62
|
+
"task.list-linked-project",
|
|
55
63
|
"task.submit",
|
|
56
64
|
"task.evolution_list",
|
|
57
65
|
"task.evolution_add",
|
|
@@ -154,6 +162,10 @@
|
|
|
154
162
|
"identity": { "type": "string" },
|
|
155
163
|
"projectId": { "type": "string" },
|
|
156
164
|
"taskId": { "type": "string" },
|
|
165
|
+
"linkedProjectId": { "type": "string" },
|
|
166
|
+
"linkedProjectIds": { "type": "string" },
|
|
167
|
+
"targetProjectId": { "type": "string" },
|
|
168
|
+
"mode": { "type": "string" },
|
|
157
169
|
"docId": { "type": "string" },
|
|
158
170
|
"eventId": { "type": "string" },
|
|
159
171
|
"backupId": { "type": "string" },
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { runCli } from "./cli"
|
|
2
2
|
import { CAPABILITY_ACTION } from "./capabilityActions"
|
|
3
|
-
import { normalizeServerBaseUrl } from "./weiyuanFile"
|
|
3
|
+
import { normalizeServerBaseUrl, writeIdentity } from "./weiyuanFile"
|
|
4
4
|
import { DELETE_DOC_CONFIRM_NOTICE, DELETE_TASK_CONFIRM_NOTICE } from "./copyStandards"
|
|
5
5
|
import { parseInteractiveTaskStatusFromText } from "./taskStandards"
|
|
6
|
+
import type { WeiyuanIdentityFileV1 } from "./types"
|
|
6
7
|
import * as fs from "node:fs/promises"
|
|
7
8
|
import * as nodePath from "node:path"
|
|
8
9
|
import { createHash } from "node:crypto"
|
|
@@ -15,6 +16,10 @@ type SkillInput = {
|
|
|
15
16
|
identity?: string
|
|
16
17
|
projectId?: string
|
|
17
18
|
taskId?: string
|
|
19
|
+
linkedProjectId?: string
|
|
20
|
+
linkedProjectIds?: string
|
|
21
|
+
targetProjectId?: string
|
|
22
|
+
mode?: string
|
|
18
23
|
to?: string
|
|
19
24
|
targetLobsterId?: string
|
|
20
25
|
memberId?: string
|
|
@@ -102,6 +107,17 @@ type MicroContextState = {
|
|
|
102
107
|
lastObservedSkillVersion?: string
|
|
103
108
|
}
|
|
104
109
|
|
|
110
|
+
type SongXingManifest = {
|
|
111
|
+
version: number
|
|
112
|
+
partnerCode: string
|
|
113
|
+
accountSeries: string
|
|
114
|
+
lobsterId?: string
|
|
115
|
+
accountId?: string
|
|
116
|
+
deliveryCode: string
|
|
117
|
+
serverBaseUrl?: string
|
|
118
|
+
runtimeBindPath?: string
|
|
119
|
+
}
|
|
120
|
+
|
|
105
121
|
type PendingFlowState =
|
|
106
122
|
| { kind: "member_profile_rename"; step: "await_project" }
|
|
107
123
|
| { kind: "member_profile_rename"; step: "await_nickname"; projectId: string; projectName?: string }
|
|
@@ -568,6 +584,96 @@ function getExpressionPolicyPath(identityPath: string): string {
|
|
|
568
584
|
return nodePath.join(nodePath.dirname(abs), ".weiyuan-expression-policy.json")
|
|
569
585
|
}
|
|
570
586
|
|
|
587
|
+
function getSongXingManifestCandidatePaths(identityPath: string): string[] {
|
|
588
|
+
const abs = nodePath.resolve(identityPath)
|
|
589
|
+
const identityDir = nodePath.dirname(abs)
|
|
590
|
+
return [
|
|
591
|
+
nodePath.join(identityDir, "songxing.manifest.json"),
|
|
592
|
+
nodePath.join(identityDir, "workspace-weiyuan", "songxing.manifest.json"),
|
|
593
|
+
nodePath.resolve(process.cwd(), "songxing.manifest.json"),
|
|
594
|
+
nodePath.resolve(process.cwd(), "workspace-weiyuan", "songxing.manifest.json"),
|
|
595
|
+
]
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async function readSongXingManifest(identityPath: string): Promise<SongXingManifest | undefined> {
|
|
599
|
+
for (const p of getSongXingManifestCandidatePaths(identityPath)) {
|
|
600
|
+
try {
|
|
601
|
+
const raw = await fs.readFile(p, "utf8")
|
|
602
|
+
const parsed = JSON.parse(raw) as Partial<SongXingManifest>
|
|
603
|
+
if (!parsed || typeof parsed !== "object") continue
|
|
604
|
+
const partnerCode = String(parsed.partnerCode ?? "").trim().toLowerCase()
|
|
605
|
+
const accountSeries = String(parsed.accountSeries ?? "").trim().toLowerCase()
|
|
606
|
+
const deliveryCode = String(parsed.deliveryCode ?? "").trim()
|
|
607
|
+
if (partnerCode !== "songxing" || accountSeries !== "songxing" || !deliveryCode) continue
|
|
608
|
+
return {
|
|
609
|
+
version: Number(parsed.version ?? 1),
|
|
610
|
+
partnerCode,
|
|
611
|
+
accountSeries,
|
|
612
|
+
lobsterId: String(parsed.lobsterId ?? "").trim() || undefined,
|
|
613
|
+
accountId: String(parsed.accountId ?? "").trim() || undefined,
|
|
614
|
+
deliveryCode,
|
|
615
|
+
serverBaseUrl: String(parsed.serverBaseUrl ?? "").trim() || undefined,
|
|
616
|
+
runtimeBindPath: String(parsed.runtimeBindPath ?? "").trim() || undefined,
|
|
617
|
+
}
|
|
618
|
+
} catch {
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return undefined
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function buildSongXingWorkspaceFingerprint(identityPath: string): string {
|
|
625
|
+
const abs = nodePath.resolve(identityPath)
|
|
626
|
+
return `${process.cwd()}|${nodePath.dirname(abs)}`
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async function ensureSongXingBindingIfNeeded(
|
|
630
|
+
identityPath: string,
|
|
631
|
+
input: SkillInput,
|
|
632
|
+
text: string,
|
|
633
|
+
rules: MicroRulesConfig,
|
|
634
|
+
state: MicroContextState,
|
|
635
|
+
): Promise<void> {
|
|
636
|
+
const manifest = await readSongXingManifest(identityPath)
|
|
637
|
+
if (!manifest) return
|
|
638
|
+
const shouldEnsure =
|
|
639
|
+
Boolean(input.action) ||
|
|
640
|
+
isMicroSceneText(text, rules) ||
|
|
641
|
+
shouldPreferMicroByContext(text, state) ||
|
|
642
|
+
/(songxing|微元|驾驶舱|项目|任务|待办|看板)/i.test(text)
|
|
643
|
+
if (!shouldEnsure) return
|
|
644
|
+
const serverBaseUrl = normalizeServerBaseUrl(String(manifest.serverBaseUrl || "https://api.magon.com.cn/api"))
|
|
645
|
+
const runtimePath = String(manifest.runtimeBindPath || "/v1/songxing/runtime/ensure-binding").trim() || "/v1/songxing/runtime/ensure-binding"
|
|
646
|
+
const res = await fetch(`${serverBaseUrl}${runtimePath}`, {
|
|
647
|
+
method: "POST",
|
|
648
|
+
headers: { "Content-Type": "application/json" },
|
|
649
|
+
body: JSON.stringify({
|
|
650
|
+
manifest,
|
|
651
|
+
workspaceFingerprint: buildSongXingWorkspaceFingerprint(identityPath),
|
|
652
|
+
workspaceLabel: process.cwd(),
|
|
653
|
+
}),
|
|
654
|
+
})
|
|
655
|
+
const raw = await res.text()
|
|
656
|
+
let json: any
|
|
657
|
+
try {
|
|
658
|
+
json = JSON.parse(raw)
|
|
659
|
+
} catch {
|
|
660
|
+
json = { msg: raw }
|
|
661
|
+
}
|
|
662
|
+
if (!res.ok) {
|
|
663
|
+
throw new Error(String(json?.msg || json?.message || "songxing_runtime_bind_failed"))
|
|
664
|
+
}
|
|
665
|
+
const payload = json?.data && typeof json.data === "object" ? json.data : json
|
|
666
|
+
const identityRaw = payload?.identity
|
|
667
|
+
if (!identityRaw || typeof identityRaw !== "object" || Array.isArray(identityRaw)) {
|
|
668
|
+
throw new Error("songxing_identity_payload_missing")
|
|
669
|
+
}
|
|
670
|
+
const nextIdentity = {
|
|
671
|
+
...(identityRaw as Record<string, unknown>),
|
|
672
|
+
serverBaseUrl: normalizeServerBaseUrl(String((identityRaw as any).serverBaseUrl || serverBaseUrl)),
|
|
673
|
+
} as WeiyuanIdentityFileV1
|
|
674
|
+
await writeIdentity(identityPath, nextIdentity)
|
|
675
|
+
}
|
|
676
|
+
|
|
571
677
|
function mergeRules(raw: unknown): MicroRulesConfig {
|
|
572
678
|
const src = (raw && typeof raw === "object" ? (raw as Record<string, unknown>) : {}) as Record<string, unknown>
|
|
573
679
|
const arr = (v: unknown, fallback: string[]): string[] =>
|
|
@@ -1004,7 +1110,7 @@ async function enforceLinkContractByCommand(raw: unknown, argv: string[], identi
|
|
|
1004
1110
|
const identityRaw = await fs.readFile(identityPath, "utf8")
|
|
1005
1111
|
const identity = JSON.parse(identityRaw) as Record<string, unknown>
|
|
1006
1112
|
const serverBase = normalizeServerBaseUrl(String(identity.serverBaseUrl ?? ""))
|
|
1007
|
-
const lobsterId = String(identity.lobsterId ?? "")
|
|
1113
|
+
const lobsterId = String(identity.effectiveLobsterId ?? identity.lobsterId ?? "")
|
|
1008
1114
|
if (serverBase && lobsterId) {
|
|
1009
1115
|
const u = new URL(serverBase)
|
|
1010
1116
|
const dashboardLink = `${u.origin}/api/dashboard-web?lobsterId=${encodeURIComponent(lobsterId)}`
|
|
@@ -1053,6 +1159,7 @@ const DEFAULT_REPLY_TEMPLATES: ReplyTemplateMap = {
|
|
|
1053
1159
|
}
|
|
1054
1160
|
|
|
1055
1161
|
const WEIYUAN_OFFICIAL_WEBSITE = "https://www.magon.com.cn/"
|
|
1162
|
+
const WEIYUAN_AGENT_SKILL_DOWNLOAD_URL = "https://api.magon.com.cn/api/downloads/weiyuan-agent-cli-skill.md"
|
|
1056
1163
|
|
|
1057
1164
|
function withOfficialWebsiteIfLinkPresent(obj: Record<string, unknown>, message: string): { message: string; officialWebsite: string } | null {
|
|
1058
1165
|
const hasDashboard = typeof obj.dashboardLink === "string" && obj.dashboardLink.trim().length > 0
|
|
@@ -1130,10 +1237,11 @@ function buildMandatoryDisplay(actionKey: string | undefined, core: unknown): st
|
|
|
1130
1237
|
const dashboardLink = typeof c.dashboardLink === "string" ? c.dashboardLink.trim() : (typeof c.dashboardUrl === "string" ? c.dashboardUrl.trim() : "")
|
|
1131
1238
|
const bindPhoneUrl = typeof c.bindPhoneUrl === "string" ? c.bindPhoneUrl.trim() : ""
|
|
1132
1239
|
const loginUrl = typeof c.loginUrl === "string" ? c.loginUrl.trim() : ""
|
|
1240
|
+
const skillDownloadUrl = typeof c.skillDownloadUrl === "string" ? c.skillDownloadUrl.trim() : WEIYUAN_AGENT_SKILL_DOWNLOAD_URL
|
|
1133
1241
|
const officialWebsite = typeof c.officialWebsite === "string"
|
|
1134
1242
|
? c.officialWebsite.trim()
|
|
1135
1243
|
: (typeof c.officialSiteUrl === "string" ? c.officialSiteUrl.trim() : WEIYUAN_OFFICIAL_WEBSITE)
|
|
1136
|
-
if (account && (dashboardLink || bindPhoneUrl || loginUrl || officialWebsite)) {
|
|
1244
|
+
if (account && (dashboardLink || bindPhoneUrl || loginUrl || officialWebsite || skillDownloadUrl)) {
|
|
1137
1245
|
return [
|
|
1138
1246
|
`### 🌀 微元系统账号入口 (不可修改)(数据时间:${time} | Trace_ID:${traceId})`,
|
|
1139
1247
|
"| 项目 | 内容 |",
|
|
@@ -1143,8 +1251,10 @@ function buildMandatoryDisplay(actionKey: string | undefined, core: unknown): st
|
|
|
1143
1251
|
`| 手机号绑定 | ${bindPhoneUrl || "未返回"} |`,
|
|
1144
1252
|
`| 登录页面 | ${loginUrl || "未返回"} |`,
|
|
1145
1253
|
`| 官方网站 | ${officialWebsite || WEIYUAN_OFFICIAL_WEBSITE} |`,
|
|
1254
|
+
`| Skill 下载 | ${skillDownloadUrl || WEIYUAN_AGENT_SKILL_DOWNLOAD_URL} |`,
|
|
1146
1255
|
"",
|
|
1147
1256
|
`- **重点提醒**:个人驾驶舱链接是重点入口,请立即保存:${dashboardLink || "当前未返回,请重新获取一次加入成功结果"}`,
|
|
1257
|
+
`- **Skill 下载**:如果你要把微元系统能力导入到 workbuddy、豆包等智能体,请下载并导入:${skillDownloadUrl || WEIYUAN_AGENT_SKILL_DOWNLOAD_URL}`,
|
|
1148
1258
|
"-----------------------------------",
|
|
1149
1259
|
].join("\n")
|
|
1150
1260
|
}
|
|
@@ -1230,8 +1340,155 @@ function pickInviteCode(core: unknown): string {
|
|
|
1230
1340
|
return ""
|
|
1231
1341
|
}
|
|
1232
1342
|
|
|
1343
|
+
function normalizeProjectQueryText(raw: unknown): string {
|
|
1344
|
+
return String(raw ?? "")
|
|
1345
|
+
.trim()
|
|
1346
|
+
.toLowerCase()
|
|
1347
|
+
.replace(/[“”"'`]/g, "")
|
|
1348
|
+
.replace(/\s+/g, "")
|
|
1349
|
+
.replace(/[,。!?!?::、()()【】\[\]\-—_]/g, "")
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function isProjectTaskQueryText(text: string): boolean {
|
|
1353
|
+
const compact = normalizeProjectQueryText(text)
|
|
1354
|
+
return /(我的任务|我有哪些任务|我有什么任务|看看任务|看看我的任务|查任务|任务列表|任务情况)/i.test(compact)
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
function isProjectMemberQueryText(text: string): boolean {
|
|
1358
|
+
const compact = normalizeProjectQueryText(text)
|
|
1359
|
+
return /(成员列表|成员情况|项目成员|看看成员|查看成员|人员情况|人员列表)/i.test(compact)
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
function summarizeMemberRole(roleRaw: unknown): string {
|
|
1363
|
+
const role = String(roleRaw ?? "").trim().toLowerCase()
|
|
1364
|
+
if (role === "founder") return "创始人"
|
|
1365
|
+
if (role === "leader") return "负责人"
|
|
1366
|
+
return "成员"
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function buildDeterministicTaskListMessage(core: unknown): string | undefined {
|
|
1370
|
+
const c = asRecord(core)
|
|
1371
|
+
const tasks = Array.isArray(c.executionTasks)
|
|
1372
|
+
? (c.executionTasks as Array<Record<string, unknown>>)
|
|
1373
|
+
: Array.isArray(c.tasks)
|
|
1374
|
+
? (c.tasks as Array<Record<string, unknown>>)
|
|
1375
|
+
: []
|
|
1376
|
+
if (!tasks.length) return undefined
|
|
1377
|
+
const projectName = pickProjectName(c)
|
|
1378
|
+
const mine = tasks.filter((t) => t.claimedByMe === true || t.mine === true)
|
|
1379
|
+
const focus = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "focus")
|
|
1380
|
+
const waiting = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "waiting")
|
|
1381
|
+
const unclaimed = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "unclaimed")
|
|
1382
|
+
const done = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "done")
|
|
1383
|
+
const mineLines = mine.map((t, idx) => {
|
|
1384
|
+
const title = String(t.title ?? t.taskTitle ?? t.taskId ?? "").trim() || `任务${idx + 1}`
|
|
1385
|
+
const status = String(t.statusText ?? t.status ?? t.taskBoardBucket ?? "未标记").trim()
|
|
1386
|
+
return `${idx + 1}. ${title}(${status})`
|
|
1387
|
+
})
|
|
1388
|
+
const lines = [
|
|
1389
|
+
`已获取「${projectName}」任务实况:共 ${tasks.length} 个执行任务,你认领 ${mine.length} 个,聚焦 ${focus.length} 个,等待依赖 ${waiting.length} 个,待认领 ${unclaimed.length} 个,已完成 ${done.length} 个。`,
|
|
1390
|
+
]
|
|
1391
|
+
if (mineLines.length > 0) lines.push(`你当前认领的任务:\n${mineLines.join("\n")}`)
|
|
1392
|
+
return lines.join("\n")
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
function buildDeterministicMemberTaskOverviewMessage(memberRaw: unknown, taskRaw: unknown): string {
|
|
1396
|
+
const memberData = asRecord(memberRaw)
|
|
1397
|
+
const taskData = asRecord(taskRaw)
|
|
1398
|
+
const projectName = pickProjectName(taskData) || pickProjectName(memberData)
|
|
1399
|
+
const members = Array.isArray(memberData.members) ? (memberData.members as Array<Record<string, unknown>>) : []
|
|
1400
|
+
const tasks = Array.isArray(taskData.executionTasks)
|
|
1401
|
+
? (taskData.executionTasks as Array<Record<string, unknown>>)
|
|
1402
|
+
: Array.isArray(taskData.tasks)
|
|
1403
|
+
? (taskData.tasks as Array<Record<string, unknown>>)
|
|
1404
|
+
: []
|
|
1405
|
+
const me = members.find((m) => m.isMe === true)
|
|
1406
|
+
const founderCount = members.filter((m) => String(m.role ?? "").trim().toLowerCase() === "founder").length
|
|
1407
|
+
const leaderCount = members.filter((m) => String(m.role ?? "").trim().toLowerCase() === "leader").length
|
|
1408
|
+
const memberCount = members.filter((m) => String(m.role ?? "").trim().toLowerCase() === "member").length
|
|
1409
|
+
const memberLines = members.map((m, idx) => {
|
|
1410
|
+
const displayName = String(m.displayName ?? m.nickname ?? m.lobsterId ?? "").trim() || `成员${idx + 1}`
|
|
1411
|
+
return `${idx + 1}. ${displayName}(${summarizeMemberRole(m.role)})`
|
|
1412
|
+
})
|
|
1413
|
+
const mine = tasks.filter((t) => t.claimedByMe === true || t.mine === true)
|
|
1414
|
+
const focus = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "focus")
|
|
1415
|
+
const waiting = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "waiting")
|
|
1416
|
+
const unclaimed = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "unclaimed")
|
|
1417
|
+
const done = tasks.filter((t) => String(t.taskBoardBucket ?? "").trim() === "done")
|
|
1418
|
+
const mineLines = mine.map((t, idx) => {
|
|
1419
|
+
const title = String(t.title ?? t.taskTitle ?? t.taskId ?? "").trim() || `任务${idx + 1}`
|
|
1420
|
+
const status = String(t.statusText ?? t.status ?? t.taskBoardBucket ?? "未标记").trim()
|
|
1421
|
+
return `${idx + 1}. ${title}(${status})`
|
|
1422
|
+
})
|
|
1423
|
+
const lines = [
|
|
1424
|
+
`已获取「${projectName}」成员与任务实况。`,
|
|
1425
|
+
`项目成员共 ${members.length} 人:创始人 ${founderCount} 人,负责人 ${leaderCount} 人,成员 ${memberCount} 人。`,
|
|
1426
|
+
memberLines.length > 0 ? `成员名单:\n${memberLines.join("\n")}` : "成员名单:暂无数据。",
|
|
1427
|
+
me ? `你当前在该项目中的角色:${summarizeMemberRole(me.role)}。` : "你当前在该项目中的角色:未识别。",
|
|
1428
|
+
`任务共 ${tasks.length} 个执行任务:你认领 ${mine.length} 个,聚焦 ${focus.length} 个,等待依赖 ${waiting.length} 个,待认领 ${unclaimed.length} 个,已完成 ${done.length} 个。`,
|
|
1429
|
+
mineLines.length > 0 ? `你当前认领的任务:\n${mineLines.join("\n")}` : "你当前认领的任务:0 个。",
|
|
1430
|
+
]
|
|
1431
|
+
return lines.join("\n")
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
function buildDeterministicMemberListMessage(memberRaw: unknown): string {
|
|
1435
|
+
const memberData = asRecord(memberRaw)
|
|
1436
|
+
const projectName = pickProjectName(memberData)
|
|
1437
|
+
const members = Array.isArray(memberData.members) ? (memberData.members as Array<Record<string, unknown>>) : []
|
|
1438
|
+
const me = members.find((m) => m.isMe === true)
|
|
1439
|
+
const founderCount = members.filter((m) => String(m.role ?? "").trim().toLowerCase() === "founder").length
|
|
1440
|
+
const leaderCount = members.filter((m) => String(m.role ?? "").trim().toLowerCase() === "leader").length
|
|
1441
|
+
const memberCount = members.filter((m) => String(m.role ?? "").trim().toLowerCase() === "member").length
|
|
1442
|
+
const memberLines = members.map((m, idx) => {
|
|
1443
|
+
const displayName = String(m.displayName ?? m.nickname ?? m.lobsterId ?? "").trim() || `成员${idx + 1}`
|
|
1444
|
+
return `${idx + 1}. ${displayName}(${summarizeMemberRole(m.role)})`
|
|
1445
|
+
})
|
|
1446
|
+
const lines = [
|
|
1447
|
+
`已获取「${projectName}」成员实况:共 ${members.length} 人,创始人 ${founderCount} 人,负责人 ${leaderCount} 人,成员 ${memberCount} 人。`,
|
|
1448
|
+
memberLines.length > 0 ? `成员名单:\n${memberLines.join("\n")}` : "成员名单:暂无数据。",
|
|
1449
|
+
me ? `你当前在该项目中的角色:${summarizeMemberRole(me.role)}。` : "你当前在该项目中的角色:未识别。",
|
|
1450
|
+
]
|
|
1451
|
+
return lines.join("\n")
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
function shortenSummaryText(raw: unknown, maxChars = 12): string {
|
|
1455
|
+
const text = String(raw ?? "").trim()
|
|
1456
|
+
if (!text) return ""
|
|
1457
|
+
return text.length > maxChars ? `${text.slice(0, maxChars)}...` : text
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
function buildTaskListSummary(core: unknown, actionKey?: string): string | undefined {
|
|
1461
|
+
if (actionKey !== "task.list" && actionKey !== "task.list_all") return undefined
|
|
1462
|
+
const c = asRecord(core)
|
|
1463
|
+
const tasks = Array.isArray(c.tasks) ? (c.tasks as Array<Record<string, unknown>>) : []
|
|
1464
|
+
if (!tasks.length) {
|
|
1465
|
+
return actionKey === "task.list_all" ? "已汇总多项目任务,当前没有可展示的任务。" : `已获取「${pickProjectName(c)}」任务,当前没有任务。`
|
|
1466
|
+
}
|
|
1467
|
+
const mine = tasks.filter((t) => t.claimedByMe === true || t.mine === true)
|
|
1468
|
+
const focus = tasks.filter((t) => String(t.taskBoardBucket ?? t.bucket ?? "").trim() === "focus")
|
|
1469
|
+
const waiting = tasks.filter((t) => {
|
|
1470
|
+
const bucket = String(t.taskBoardBucket ?? t.bucket ?? "").trim()
|
|
1471
|
+
const status = String(t.status ?? "").trim()
|
|
1472
|
+
return bucket === "waiting" || status === "blocked" || status === "waiting"
|
|
1473
|
+
})
|
|
1474
|
+
const unclaimed = tasks.filter((t) => !String(t.assigneeLobsterId ?? "").trim() && t.claimedByMe !== true && t.mine !== true)
|
|
1475
|
+
const done = tasks.filter((t) => String(t.status ?? "").trim() === "done")
|
|
1476
|
+
const mineTitles = mine
|
|
1477
|
+
.map((t) => shortenSummaryText(t.title ?? t.taskTitle ?? t.taskId, 10))
|
|
1478
|
+
.filter(Boolean)
|
|
1479
|
+
.slice(0, 3)
|
|
1480
|
+
const prefix = actionKey === "task.list_all" ? "已汇总多项目任务" : `已获取「${pickProjectName(c)}」任务`
|
|
1481
|
+
let out = `${prefix}:共 ${tasks.length} 个,你认领 ${mine.length} 个,聚焦 ${focus.length} 个,待认领 ${unclaimed.length} 个`
|
|
1482
|
+
if (waiting.length > 0) out += `,等待依赖 ${waiting.length} 个`
|
|
1483
|
+
if (done.length > 0) out += `,已完成 ${done.length} 个`
|
|
1484
|
+
out += "。"
|
|
1485
|
+
if (mineTitles.length > 0) out += `你当前认领:${mineTitles.join("、")}。`
|
|
1486
|
+
return out
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1233
1489
|
function detectReplyActionKey(argv: string[], core: unknown): string | undefined {
|
|
1234
1490
|
if (!argv.length) return undefined
|
|
1491
|
+
let actionKey: string | undefined
|
|
1235
1492
|
if (argv[0] === "runtime-upgrade") return "runtime.upgrade"
|
|
1236
1493
|
if (argv[0] === "dashboard-web") return "dashboard.web"
|
|
1237
1494
|
if (argv[0] === "dashboard-all") return "todo.plan"
|
|
@@ -1261,18 +1518,20 @@ function detectReplyActionKey(argv: string[], core: unknown): string | undefined
|
|
|
1261
1518
|
if (argv[0] === "task" && argv[1] === "update") return "task.update"
|
|
1262
1519
|
if (argv[0] === "task" && argv[1] === "delete") return CAPABILITY_ACTION.TASK_DELETE
|
|
1263
1520
|
if (argv[0] === "task" && argv[1] === "submit") return "task.complete"
|
|
1521
|
+
if (argv[0] === "task" && argv[1] === "list") actionKey = "task.list"
|
|
1522
|
+
if (argv[0] === "task" && argv[1] === "list-all") actionKey = "task.list_all"
|
|
1264
1523
|
if (argv[0] === "member" && argv[1] === "remove") return "member.remove"
|
|
1265
1524
|
if (argv[0] === "member" && argv[1] === "leave") return "member.leave"
|
|
1266
1525
|
if (argv[0] === "doc" && argv[1] === "upload") return "doc.upload"
|
|
1267
1526
|
if (argv[0] === "doc" && argv[1] === "delete") return CAPABILITY_ACTION.DOC_DELETE
|
|
1268
|
-
if (
|
|
1527
|
+
if (actionKey === "task.list" || actionKey === "task.list_all") {
|
|
1269
1528
|
const c = asRecord(core)
|
|
1270
1529
|
const tasks = Array.isArray(c.tasks) ? c.tasks as Array<Record<string, unknown>> : []
|
|
1271
1530
|
const now = Date.now()
|
|
1272
1531
|
const overdue = tasks.find((t) => typeof t.dueAt === "number" && Number(t.dueAt) < now && String(t.status ?? "") !== "done")
|
|
1273
1532
|
if (overdue) return "task.list.overdue"
|
|
1274
1533
|
}
|
|
1275
|
-
return
|
|
1534
|
+
return actionKey
|
|
1276
1535
|
}
|
|
1277
1536
|
|
|
1278
1537
|
function renderReplyTemplate(actionKey: string | undefined, core: unknown, templates: ReplyTemplateMap): string | undefined {
|
|
@@ -1313,6 +1572,8 @@ function conciseMessageFromCore(core: unknown, actionKey?: string, templates: Re
|
|
|
1313
1572
|
if ((actionKey === "invite.generate" || actionKey === "account.invite-join-template") && typeof c.inviteText === "string" && c.inviteText.trim()) {
|
|
1314
1573
|
return c.inviteText.trim()
|
|
1315
1574
|
}
|
|
1575
|
+
const taskSummary = buildTaskListSummary(c, actionKey)
|
|
1576
|
+
if (taskSummary) return taskSummary
|
|
1316
1577
|
const todoPlan = c.currentTodoPlan as { items?: Array<Record<string, unknown>> } | undefined
|
|
1317
1578
|
if (todoPlan && Array.isArray(todoPlan.items)) {
|
|
1318
1579
|
const items = todoPlan.items
|
|
@@ -2575,6 +2836,64 @@ function fromAction(input: SkillInput): string[] {
|
|
|
2575
2836
|
case "task.delete":
|
|
2576
2837
|
if (!input.projectId || !input.taskId) throw new Error("missing_projectId_or_taskId")
|
|
2577
2838
|
return ["task", "delete", "--identity", identity, "--project", input.projectId, "--task", input.taskId]
|
|
2839
|
+
case "task.link-project":
|
|
2840
|
+
case "task.link_project":
|
|
2841
|
+
if (!input.projectId || !input.taskId) throw new Error("missing_projectId_or_taskId")
|
|
2842
|
+
{
|
|
2843
|
+
const linkedProjectIds = String(input.linkedProjectIds ?? input.linkedProjectId ?? "").trim()
|
|
2844
|
+
if (!linkedProjectIds) throw new Error("missing_linkedProjectId")
|
|
2845
|
+
return [
|
|
2846
|
+
"task",
|
|
2847
|
+
"link-project",
|
|
2848
|
+
"--identity",
|
|
2849
|
+
identity,
|
|
2850
|
+
"--project",
|
|
2851
|
+
input.projectId,
|
|
2852
|
+
"--task",
|
|
2853
|
+
input.taskId,
|
|
2854
|
+
"--linkedProject",
|
|
2855
|
+
linkedProjectIds,
|
|
2856
|
+
...(input.mode ? ["--mode", String(input.mode)] : []),
|
|
2857
|
+
]
|
|
2858
|
+
}
|
|
2859
|
+
case "task.unlink-project":
|
|
2860
|
+
case "task.unlink_project":
|
|
2861
|
+
if (!input.projectId || !input.taskId) throw new Error("missing_projectId_or_taskId")
|
|
2862
|
+
return [
|
|
2863
|
+
"task",
|
|
2864
|
+
"unlink-project",
|
|
2865
|
+
"--identity",
|
|
2866
|
+
identity,
|
|
2867
|
+
"--project",
|
|
2868
|
+
input.projectId,
|
|
2869
|
+
"--task",
|
|
2870
|
+
input.taskId,
|
|
2871
|
+
...(input.linkedProjectIds || input.linkedProjectId ? ["--linkedProject", String(input.linkedProjectIds ?? input.linkedProjectId)] : []),
|
|
2872
|
+
]
|
|
2873
|
+
case "task.list-linked-project":
|
|
2874
|
+
case "task.list_linked_project":
|
|
2875
|
+
if (!input.projectId || !input.taskId) throw new Error("missing_projectId_or_taskId")
|
|
2876
|
+
return ["task", "list-linked-project", "--identity", identity, "--project", input.projectId, "--task", input.taskId]
|
|
2877
|
+
case "project.link-task":
|
|
2878
|
+
case "project.link_task":
|
|
2879
|
+
{
|
|
2880
|
+
const linkedProjectId = String(input.linkedProjectId ?? input.projectId ?? "").trim()
|
|
2881
|
+
const targetProjectId = String(input.targetProjectId ?? "").trim()
|
|
2882
|
+
if (!linkedProjectId || !targetProjectId || !input.taskId) throw new Error("missing_linkedProjectId_or_targetProjectId_or_taskId")
|
|
2883
|
+
return [
|
|
2884
|
+
"project",
|
|
2885
|
+
"link-task",
|
|
2886
|
+
"--identity",
|
|
2887
|
+
identity,
|
|
2888
|
+
"--project",
|
|
2889
|
+
linkedProjectId,
|
|
2890
|
+
"--targetProject",
|
|
2891
|
+
targetProjectId,
|
|
2892
|
+
"--task",
|
|
2893
|
+
input.taskId,
|
|
2894
|
+
...(input.mode ? ["--mode", String(input.mode)] : []),
|
|
2895
|
+
]
|
|
2896
|
+
}
|
|
2578
2897
|
case "task.suggest":
|
|
2579
2898
|
{
|
|
2580
2899
|
const target = String(input.to ?? input.targetLobsterId ?? input.memberId ?? "").trim()
|
|
@@ -3356,6 +3675,7 @@ async function main(): Promise<void> {
|
|
|
3356
3675
|
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`)
|
|
3357
3676
|
return
|
|
3358
3677
|
}
|
|
3678
|
+
await ensureSongXingBindingIfNeeded(identityPath, input, text, rules, contextState)
|
|
3359
3679
|
const runCliCaptured = async (argv: string[]): Promise<unknown> => {
|
|
3360
3680
|
let captured = ""
|
|
3361
3681
|
const originalWrite = process.stdout.write.bind(process.stdout)
|
|
@@ -3378,6 +3698,23 @@ async function main(): Promise<void> {
|
|
|
3378
3698
|
return captured.trim()
|
|
3379
3699
|
}
|
|
3380
3700
|
}
|
|
3701
|
+
if (!input.projectId && !input.action) {
|
|
3702
|
+
try {
|
|
3703
|
+
const projectList = (await runCliCaptured(["project", "list", "--identity", identityPath])) as any
|
|
3704
|
+
const projects = Array.isArray(projectList?.projects) ? projectList.projects : []
|
|
3705
|
+
const queryText = normalizeProjectQueryText(text)
|
|
3706
|
+
const matched =
|
|
3707
|
+
projects.find((p: any) => normalizeProjectQueryText(String(p?.projectId ?? "")) && queryText.includes(normalizeProjectQueryText(String(p?.projectId ?? "")))) ??
|
|
3708
|
+
projects.find((p: any) => {
|
|
3709
|
+
const nameKey = normalizeProjectQueryText(String(p?.name ?? ""))
|
|
3710
|
+
return nameKey && queryText.includes(nameKey)
|
|
3711
|
+
})
|
|
3712
|
+
if (matched?.projectId) {
|
|
3713
|
+
input.projectId = String(matched.projectId)
|
|
3714
|
+
}
|
|
3715
|
+
} catch {
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3381
3718
|
|
|
3382
3719
|
const pendingFlow = parsePendingFlow(contextState.unfinishedIntent)
|
|
3383
3720
|
if (pendingFlow?.kind === "p0_project_delete_confirm") {
|
|
@@ -3565,7 +3902,7 @@ async function main(): Promise<void> {
|
|
|
3565
3902
|
const projects = Array.isArray(projectList?.projects) ? projectList.projects : []
|
|
3566
3903
|
const answer = text.trim()
|
|
3567
3904
|
if (!answer) {
|
|
3568
|
-
const listText = projects.
|
|
3905
|
+
const listText = projects.map((p: any, idx: number) => `${idx + 1}. ${p.name}(${p.projectId})`).join("\n")
|
|
3569
3906
|
const out: SkillSuccess = {
|
|
3570
3907
|
ok: true,
|
|
3571
3908
|
data: {
|
|
@@ -3585,7 +3922,7 @@ async function main(): Promise<void> {
|
|
|
3585
3922
|
projects.find((p: any) => String(p.name) === answer) ??
|
|
3586
3923
|
projects.find((p: any) => String(p.name ?? "").includes(answer))
|
|
3587
3924
|
if (!matched) {
|
|
3588
|
-
const listText = projects.
|
|
3925
|
+
const listText = projects.map((p: any, idx: number) => `${idx + 1}. ${p.name}(${p.projectId})`).join("\n")
|
|
3589
3926
|
const out: SkillSuccess = {
|
|
3590
3927
|
ok: true,
|
|
3591
3928
|
data: {
|
|
@@ -3674,7 +4011,7 @@ async function main(): Promise<void> {
|
|
|
3674
4011
|
const projectList = (await runCliCaptured(["project", "list", "--identity", identityPath])) as any
|
|
3675
4012
|
const projects = Array.isArray(projectList?.projects) ? projectList.projects : []
|
|
3676
4013
|
const answer = text.trim()
|
|
3677
|
-
const listText = projects.
|
|
4014
|
+
const listText = projects.map((p: any, idx: number) => `${idx + 1}. ${p.name}(${p.projectId})`).join("\n")
|
|
3678
4015
|
if (!answer) {
|
|
3679
4016
|
const out: SkillSuccess = {
|
|
3680
4017
|
ok: true,
|
|
@@ -3744,7 +4081,7 @@ async function main(): Promise<void> {
|
|
|
3744
4081
|
: Array.isArray(taskList?.data?.tasks)
|
|
3745
4082
|
? taskList.data.tasks
|
|
3746
4083
|
: []
|
|
3747
|
-
const listText = tasks.
|
|
4084
|
+
const listText = tasks.map((t: any, idx: number) => `${idx + 1}. ${t.title || t.taskId}(${t.taskId})`).join("\n")
|
|
3748
4085
|
if (!answer) {
|
|
3749
4086
|
const out: SkillSuccess = {
|
|
3750
4087
|
ok: true,
|
|
@@ -4056,7 +4393,7 @@ async function main(): Promise<void> {
|
|
|
4056
4393
|
const projectList = (await runCliCaptured(["project", "list", "--identity", identityPath])) as any
|
|
4057
4394
|
const projects = Array.isArray(projectList?.projects) ? projectList.projects : []
|
|
4058
4395
|
const answer = text.trim()
|
|
4059
|
-
const listText = projects.
|
|
4396
|
+
const listText = projects.map((p: any, idx: number) => `${idx + 1}. ${p.name}(${p.projectId})`).join("\n")
|
|
4060
4397
|
if (!answer) {
|
|
4061
4398
|
const out: SkillSuccess = {
|
|
4062
4399
|
ok: true,
|
|
@@ -4125,7 +4462,7 @@ async function main(): Promise<void> {
|
|
|
4125
4462
|
: Array.isArray(docList?.data?.docs)
|
|
4126
4463
|
? docList.data.docs
|
|
4127
4464
|
: []
|
|
4128
|
-
const listText = docs.
|
|
4465
|
+
const listText = docs.map((d: any, idx: number) => `${idx + 1}. ${d.docName || d.docId}(${d.docId})`).join("\n")
|
|
4129
4466
|
if (!answer) {
|
|
4130
4467
|
const out: SkillSuccess = {
|
|
4131
4468
|
ok: true,
|
|
@@ -4227,7 +4564,7 @@ async function main(): Promise<void> {
|
|
|
4227
4564
|
}
|
|
4228
4565
|
const projectList = (await runCliCaptured(["project", "list", "--identity", identityPath])) as any
|
|
4229
4566
|
const projects = Array.isArray(projectList?.projects) ? projectList.projects : []
|
|
4230
|
-
const listText = projects.
|
|
4567
|
+
const listText = projects.map((p: any, idx: number) => `${idx + 1}. ${p.name}(${p.projectId})`).join("\n")
|
|
4231
4568
|
const nextState: MicroContextState = {
|
|
4232
4569
|
...contextState,
|
|
4233
4570
|
lastMicroTaskTime: Date.now(),
|
|
@@ -4318,6 +4655,89 @@ async function main(): Promise<void> {
|
|
|
4318
4655
|
return
|
|
4319
4656
|
}
|
|
4320
4657
|
}
|
|
4658
|
+
if (!input.action && input.projectId && isProjectMemberQueryText(text) && isProjectTaskQueryText(text)) {
|
|
4659
|
+
const memberData = await runCliCaptured(["member", "list", "--identity", identityPath, "--project", input.projectId])
|
|
4660
|
+
const taskData = await runCliCaptured(["task", "list", "--identity", identityPath, "--project", input.projectId, "--includeContainers", "true", "--includeArchived", "true"])
|
|
4661
|
+
const mergedCore = {
|
|
4662
|
+
...(asRecord(taskData) as Record<string, unknown>),
|
|
4663
|
+
projectId: input.projectId,
|
|
4664
|
+
projectName: pickProjectName(taskData) || pickProjectName(memberData) || input.projectId,
|
|
4665
|
+
members: Array.isArray((asRecord(memberData) as any).members) ? (asRecord(memberData) as any).members : [],
|
|
4666
|
+
}
|
|
4667
|
+
const out: SkillSuccess = {
|
|
4668
|
+
ok: true,
|
|
4669
|
+
data: {
|
|
4670
|
+
result: ensureDialogueResultContract({
|
|
4671
|
+
...(mergedCore as any),
|
|
4672
|
+
message: buildDeterministicMemberTaskOverviewMessage(memberData, taskData),
|
|
4673
|
+
card: {
|
|
4674
|
+
result: "成员与任务实况已同步",
|
|
4675
|
+
status: "已按真实数据生成",
|
|
4676
|
+
nextAction: "下一步:可继续说“只看我认领的任务”或“只看待认领任务”",
|
|
4677
|
+
},
|
|
4678
|
+
protocol: { channel: "weiyuan", version: 1, prefix: MICRO_PREFIX, actionKey: "project.member_task.overview" },
|
|
4679
|
+
}),
|
|
4680
|
+
contextTip: "项目成员与任务",
|
|
4681
|
+
detailMode: detailed ? "full" : "core",
|
|
4682
|
+
},
|
|
4683
|
+
}
|
|
4684
|
+
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`)
|
|
4685
|
+
return
|
|
4686
|
+
}
|
|
4687
|
+
if (!input.action && input.projectId && isProjectTaskQueryText(text) && !isProjectMemberQueryText(text)) {
|
|
4688
|
+
const taskData = await runCliCaptured(["task", "list", "--identity", identityPath, "--project", input.projectId, "--includeContainers", "true", "--includeArchived", "true"])
|
|
4689
|
+
const taskCore = {
|
|
4690
|
+
...(asRecord(taskData) as Record<string, unknown>),
|
|
4691
|
+
projectId: input.projectId,
|
|
4692
|
+
projectName: pickProjectName(taskData) || input.projectId,
|
|
4693
|
+
}
|
|
4694
|
+
const out: SkillSuccess = {
|
|
4695
|
+
ok: true,
|
|
4696
|
+
data: {
|
|
4697
|
+
result: ensureDialogueResultContract({
|
|
4698
|
+
...(taskCore as any),
|
|
4699
|
+
message: buildDeterministicTaskListMessage(taskData) ?? `已获取「${input.projectId}」任务实况。`,
|
|
4700
|
+
card: {
|
|
4701
|
+
result: "任务实况已同步",
|
|
4702
|
+
status: "已按真实数据生成",
|
|
4703
|
+
nextAction: "下一步:可继续说“只看我认领的任务”或“看待认领任务”",
|
|
4704
|
+
},
|
|
4705
|
+
protocol: { channel: "weiyuan", version: 1, prefix: MICRO_PREFIX, actionKey: "project.task.overview" },
|
|
4706
|
+
}),
|
|
4707
|
+
contextTip: "项目任务",
|
|
4708
|
+
detailMode: detailed ? "full" : "core",
|
|
4709
|
+
},
|
|
4710
|
+
}
|
|
4711
|
+
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`)
|
|
4712
|
+
return
|
|
4713
|
+
}
|
|
4714
|
+
if (!input.action && input.projectId && isProjectMemberQueryText(text) && !isProjectTaskQueryText(text)) {
|
|
4715
|
+
const memberData = await runCliCaptured(["member", "list", "--identity", identityPath, "--project", input.projectId])
|
|
4716
|
+
const memberCore = {
|
|
4717
|
+
...(asRecord(memberData) as Record<string, unknown>),
|
|
4718
|
+
projectId: input.projectId,
|
|
4719
|
+
projectName: pickProjectName(memberData) || input.projectId,
|
|
4720
|
+
}
|
|
4721
|
+
const out: SkillSuccess = {
|
|
4722
|
+
ok: true,
|
|
4723
|
+
data: {
|
|
4724
|
+
result: ensureDialogueResultContract({
|
|
4725
|
+
...(memberCore as any),
|
|
4726
|
+
message: buildDeterministicMemberListMessage(memberData),
|
|
4727
|
+
card: {
|
|
4728
|
+
result: "成员实况已同步",
|
|
4729
|
+
status: "已按真实数据生成",
|
|
4730
|
+
nextAction: "下一步:可继续说“看成员详情”或“看人员和任务情况”",
|
|
4731
|
+
},
|
|
4732
|
+
protocol: { channel: "weiyuan", version: 1, prefix: MICRO_PREFIX, actionKey: "project.member.overview" },
|
|
4733
|
+
}),
|
|
4734
|
+
contextTip: "项目成员",
|
|
4735
|
+
detailMode: detailed ? "full" : "core",
|
|
4736
|
+
},
|
|
4737
|
+
}
|
|
4738
|
+
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`)
|
|
4739
|
+
return
|
|
4740
|
+
}
|
|
4321
4741
|
if (!input.action && /(确认)?(删除|移除).*(项目)/.test(compactText)) {
|
|
4322
4742
|
if (!input.projectId) {
|
|
4323
4743
|
const out: SkillSuccess = {
|
|
@@ -4615,6 +5035,27 @@ function mapError(err: unknown, detailed: boolean): SkillError {
|
|
|
4615
5035
|
if (rawError.includes("init_cancelled_existing_identity")) {
|
|
4616
5036
|
return { ok: false, errorCode: "CLI_ERROR", message: "已取消初始化。当前身份保持不变。", ...(detailed ? { rawError } : {}) }
|
|
4617
5037
|
}
|
|
5038
|
+
if (rawError.includes("songxing_manifest_required")) {
|
|
5039
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "当前 SongXing 专供实例缺少 manifest 文件,无法自动完成绑定。", ...(detailed ? { rawError } : {}) }
|
|
5040
|
+
}
|
|
5041
|
+
if (rawError.includes("songxing_workspace_required")) {
|
|
5042
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "当前工作空间标识缺失,暂时无法执行 SongXing 自动绑定。", ...(detailed ? { rawError } : {}) }
|
|
5043
|
+
}
|
|
5044
|
+
if (rawError.includes("songxing_account_not_found")) {
|
|
5045
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "SongXing 交付账号不存在,请重新导出该实例的交付包。", ...(detailed ? { rawError } : {}) }
|
|
5046
|
+
}
|
|
5047
|
+
if (rawError.includes("songxing_account_deleted")) {
|
|
5048
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "这个 SongXing 账号已被后台停用,请更换新的交付账号。", ...(detailed ? { rawError } : {}) }
|
|
5049
|
+
}
|
|
5050
|
+
if (rawError.includes("songxing_delivery_code_invalid")) {
|
|
5051
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "SongXing 校验码无效,当前实例不能自动绑定。", ...(detailed ? { rawError } : {}) }
|
|
5052
|
+
}
|
|
5053
|
+
if (rawError.includes("songxing_manifest_workspace_mismatch")) {
|
|
5054
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "当前 SongXing 清单已绑定到其他工作空间,系统已拒绝串号。", ...(detailed ? { rawError } : {}) }
|
|
5055
|
+
}
|
|
5056
|
+
if (rawError.includes("songxing_secret_missing") || rawError.includes("songxing_identity_payload_missing")) {
|
|
5057
|
+
return { ok: false, errorCode: "CLI_ERROR", message: "SongXing 身份回填数据缺失,请重新导出并注入交付包。", ...(detailed ? { rawError } : {}) }
|
|
5058
|
+
}
|
|
4618
5059
|
if (rawError.includes("init_requires_confirm_existing_identity_use_yes")) {
|
|
4619
5060
|
return { ok: false, errorCode: "CLI_ERROR", message: "检测到已有身份。请先完成人工确认后再初始化。", ...(detailed ? { rawError } : {}) }
|
|
4620
5061
|
}
|