openclaw-weiyuan-init 1.0.115 → 1.0.117

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/identity.js CHANGED
@@ -50,7 +50,7 @@ function normalizeBusinessServerUrl(serverUrl) {
50
50
  try {
51
51
  const url = new URL(String(serverUrl).trim());
52
52
  const rawPath = String(url.pathname || '/');
53
- if (/^\/(?:api\/)?agent-register(?:\/|$)/.test(rawPath)) {
53
+ if (/^\/(?:api\/)?agent-(?:register|bind)(?:\/|$)/.test(rawPath)) {
54
54
  url.pathname = '/api';
55
55
  } else if (!rawPath || rawPath === '/') {
56
56
  url.pathname = '/api';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-weiyuan-init",
3
- "version": "1.0.115",
3
+ "version": "1.0.117",
4
4
  "description": "OpenClaw Weiyuan Skill 一键初始化工具",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
@@ -4,7 +4,7 @@ import { createInterface } from "node:readline/promises"
4
4
  import { stdin as input, stdout as output } from "node:process"
5
5
  import { spawnSync } from "node:child_process"
6
6
  import type { WeiyuanIdentityFileV1 } from "./types"
7
- import { DEFAULT_IDENTITY_PATH, normalizeServerBaseUrl, readIdentity, writeIdentity } from "./weiyuanFile"
7
+ import { DEFAULT_IDENTITY_PATH, healIdentityServerBaseUrl, normalizeServerBaseUrl, readIdentity, writeIdentity } from "./weiyuanFile"
8
8
  import { sha256Hex } from "./crypto"
9
9
  import { signedFetch, signedRequest } from "./http"
10
10
  import { bodyHashBase64, canonicalRequest, newKeyPair, signEd25519Base64 } from "./signing"
@@ -1203,6 +1203,41 @@ async function fetchOptionalJson(url: string): Promise<Record<string, unknown> |
1203
1203
  }
1204
1204
  }
1205
1205
 
1206
+ async function reportAgentRuntimeVersion(options: {
1207
+ apiBaseUrl: string
1208
+ identity: WeiyuanIdentityFileV1
1209
+ skillVersion?: string
1210
+ targetAccountId?: string
1211
+ }): Promise<Record<string, unknown> | null> {
1212
+ const apiBaseUrl = normalizeServerBaseUrl(String(options.apiBaseUrl || "").trim())
1213
+ const skillVersion = String(options.skillVersion || "").trim().replace(/^v/i, "")
1214
+ const lobsterId = String(options.identity?.lobsterId || "").trim()
1215
+ const identityHash = String(options.identity?.identityHash || "").trim()
1216
+ const targetAccountId = String(options.targetAccountId || lobsterId).trim()
1217
+ if (!apiBaseUrl || !lobsterId || !identityHash) return null
1218
+ try {
1219
+ const res = await fetch(`${apiBaseUrl}/agent/version/report`, {
1220
+ method: "POST",
1221
+ headers: { "content-type": "application/json" },
1222
+ body: JSON.stringify({
1223
+ lobsterId,
1224
+ identityHash,
1225
+ skillVersion,
1226
+ targetAccountId,
1227
+ }),
1228
+ })
1229
+ const text = await res.text()
1230
+ const parsed = text.trim() ? (JSON.parse(text) as any) : {}
1231
+ if (!res.ok) throw new Error(String(parsed?.msg || parsed?.message || `http_${res.status}`))
1232
+ return parsed?.data && typeof parsed.data === "object" ? parsed.data : parsed
1233
+ } catch (error) {
1234
+ return {
1235
+ ok: false,
1236
+ reportError: String((error as Error)?.message ?? error ?? "version_report_failed"),
1237
+ }
1238
+ }
1239
+ }
1240
+
1206
1241
  async function downloadBinaryFile(url: string, outPath: string, errorPrefix: string): Promise<void> {
1207
1242
  let res: Response
1208
1243
  try {
@@ -2102,25 +2137,57 @@ async function cmdTaskList(args: Argv): Promise<void> {
2102
2137
  throw new Error(JSON.stringify(json))
2103
2138
  }
2104
2139
  const includeArchived = optBoolean(args.flags, "includeArchived", false)
2140
+ const includeContainers = optBoolean(args.flags, "includeContainers", false)
2105
2141
  const originalTasks = Array.isArray((json.data as any)?.tasks) ? ((json.data as any).tasks as any[]) : []
2106
- const tasks = includeArchived ? originalTasks : originalTasks.filter((t) => String(t?.status ?? "") !== "archived")
2107
- const doneCount = tasks.filter((t) => String(t?.status ?? "").toLowerCase() === "done").length
2108
- const takenCount = tasks.filter((t) => String(t?.status ?? "").toLowerCase() === "taken").length
2109
- const openCount = tasks.filter((t) => String(t?.status ?? "").toLowerCase() === "open").length
2110
- const myTakenCount = tasks.filter((t) => String(t?.status ?? "").toLowerCase() === "taken" && isTaskMine(t, identity.lobsterId)).length
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
2111
2169
  print({
2112
2170
  ...(json.data as Record<string, unknown>),
2113
2171
  projectId,
2114
2172
  projectName: projectRef.projectName ?? (json.data as any)?.projectName,
2115
2173
  tasks,
2174
+ executionTasks,
2175
+ containerTasks: includeContainers ? containerTasks : undefined,
2116
2176
  summary: {
2117
2177
  done: doneCount,
2178
+ focus: focusCount,
2179
+ waiting: waitingCount,
2180
+ unclaimed: unclaimedCount,
2181
+ mineTaken: myTakenCount,
2182
+ },
2183
+ rawStatusSummary: {
2118
2184
  taken: takenCount,
2119
2185
  open: openCount,
2120
- mineTaken: myTakenCount,
2121
2186
  },
2187
+ listView: includeContainers ? "all_tasks" : "execution_tasks_only",
2122
2188
  listPolicy: includeArchived ? "all" : "exclude_archived",
2123
- archivedFilteredCount: includeArchived ? 0 : Math.max(0, originalTasks.length - tasks.length),
2189
+ archivedFilteredCount,
2190
+ containerExcludedCount: includeContainers ? 0 : containerTasks.length,
2124
2191
  nextSuggestions: [
2125
2192
  "如需查看剩余空间,执行:project storage-remaining",
2126
2193
  "可继续上传文档或更新任务进度",
@@ -2909,14 +2976,14 @@ function printCommandHelp(command: string): boolean {
2909
2976
  command: "member",
2910
2977
  usage: [
2911
2978
  "member list --project <projectId>",
2912
- "member add-web --project <projectId> --name <显示名称>",
2979
+ "member add --project <projectId> --name <显示名称>",
2913
2980
  "member remove --project <projectId> --target <lobsterId> --reason <原因> [--confirm1 同意 --confirm2 同意]",
2914
2981
  "member leave --project <projectId> --reason <原因> [--confirm1 同意 --confirm2 同意]",
2915
2982
  "member profile-edit --project <projectId> [--nickname <项目昵称>] [--roleTitle <角色>] [--specialties <A,B>] [--taskPreferenceTags <X,Y>] [--syncAll]",
2916
2983
  "member profile-global-edit --project <projectId> [--nickname <全局昵称>] [--roleTitle <角色>] [--specialties <A,B>] [--taskPreferenceTags <X,Y>]",
2917
2984
  ],
2918
2985
  examples: [
2919
- "npm run weiyuan -- member add-web --project <projectId> --name 设计同学A",
2986
+ "npm run weiyuan -- member add --project <projectId> --name 设计同学A",
2920
2987
  "npm run weiyuan -- member profile-edit --project <projectId> --nickname 小李 --roleTitle 研发",
2921
2988
  "npm run weiyuan -- member-profile-edit --project <projectId> --specialties 前端,交互 --taskPreferenceTags 重构,性能 --syncAll",
2922
2989
  "npm run weiyuan -- member profile-global-edit --project <projectId> --nickname 全局小李 --roleTitle 项目经理",
@@ -3573,6 +3640,7 @@ async function cmdRuntimeUpgrade(args: Argv): Promise<void> {
3573
3640
  const upgradeBaseUrl = inferUpgradeBaseUrl(apiBaseUrl, optString(args.flags, "upgrade"))
3574
3641
  const upgradeRules = buildUpgradeRules(upgradeBaseUrl)
3575
3642
  const currentVersion = await readLocalRuntimeVersion(weiyuanPath)
3643
+ const targetAccountId = optString(args.flags, "target-account-id") ?? optString(args.flags, "target") ?? identity.lobsterId
3576
3644
  const latestVersion = await fetchRequiredText(upgradeRules.latestVersionUrl, "upgrade_latest_version_unavailable")
3577
3645
  const releaseNotes = await fetchOptionalJson(upgradeRules.releaseNotesUrl)
3578
3646
  const currentReady = await fs
@@ -3581,6 +3649,12 @@ async function cmdRuntimeUpgrade(args: Argv): Promise<void> {
3581
3649
  .catch(() => false)
3582
3650
  if (currentReady && currentVersion === latestVersion && !asBoolFlag(args.flags, "force", false)) {
3583
3651
  const persisted = await persistLocalUpgradeArtifacts(weiyuanPath, latestVersion, releaseNotes)
3652
+ const reportResult = await reportAgentRuntimeVersion({
3653
+ apiBaseUrl,
3654
+ identity,
3655
+ skillVersion: latestVersion,
3656
+ targetAccountId,
3657
+ })
3584
3658
  print({
3585
3659
  ok: true,
3586
3660
  action: "runtime_upgrade",
@@ -3590,6 +3664,8 @@ async function cmdRuntimeUpgrade(args: Argv): Promise<void> {
3590
3664
  currentVersion,
3591
3665
  latestVersion,
3592
3666
  apiBaseUrl,
3667
+ targetAccountId,
3668
+ versionReport: reportResult,
3593
3669
  ...upgradeRules,
3594
3670
  ...persisted,
3595
3671
  })
@@ -3609,6 +3685,13 @@ async function cmdRuntimeUpgrade(args: Argv): Promise<void> {
3609
3685
  }
3610
3686
  runProcessOrThrow(process.platform === "win32" ? "npm.cmd" : "npm", ["--prefix", weiyuanPath, "install"], workspacePath, "runtime_upgrade_install_failed")
3611
3687
  const persisted = await persistLocalUpgradeArtifacts(weiyuanPath, latestVersion, releaseNotes)
3688
+ const refreshedIdentity = await readIdentity(filePath)
3689
+ const reportResult = await reportAgentRuntimeVersion({
3690
+ apiBaseUrl,
3691
+ identity: refreshedIdentity,
3692
+ skillVersion: latestVersion,
3693
+ targetAccountId,
3694
+ })
3612
3695
  print({
3613
3696
  ok: true,
3614
3697
  action: "runtime_upgrade",
@@ -3617,6 +3700,8 @@ async function cmdRuntimeUpgrade(args: Argv): Promise<void> {
3617
3700
  currentVersionBefore: currentVersion ?? null,
3618
3701
  latestVersion,
3619
3702
  apiBaseUrl,
3703
+ targetAccountId,
3704
+ versionReport: reportResult,
3620
3705
  ...upgradeRules,
3621
3706
  ...persisted,
3622
3707
  })
@@ -4040,6 +4125,7 @@ function buildTodoCliView(
4040
4125
  const base = {
4041
4126
  ok: true,
4042
4127
  mode,
4128
+ fetchedAt: now,
4043
4129
  count: enriched.length,
4044
4130
  activeCount: enriched.filter((row) => String(row.status || "") !== "done").length,
4045
4131
  todayItems,
@@ -4072,6 +4158,85 @@ function buildTodoCliView(
4072
4158
  return base
4073
4159
  }
4074
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
+
4075
4240
  async function cmdTodoList(
4076
4241
  args: Argv,
4077
4242
  mode: "list" | "plan" | "today" | "tomorrow" | "calendar" = "list"
@@ -4130,7 +4295,7 @@ async function cmdTodoUpsert(args: Argv): Promise<void> {
4130
4295
  body,
4131
4296
  })
4132
4297
  if (status === 200) {
4133
- print(json.data)
4298
+ print(normalizeTodoMutationCliResult(json.data))
4134
4299
  return
4135
4300
  }
4136
4301
  if (!isTodoRestRouteMissing(status, json)) throw new Error(JSON.stringify(json))
@@ -4139,7 +4304,7 @@ async function cmdTodoUpsert(args: Argv): Promise<void> {
4139
4304
  buildPanelStyleCommand("todo", "upsert", body),
4140
4305
  optString(args.flags, "password")
4141
4306
  )
4142
- print(data)
4307
+ print(normalizeTodoMutationCliResult(data))
4143
4308
  }
4144
4309
 
4145
4310
  async function cmdTodoComplete(args: Argv): Promise<void> {
@@ -4162,7 +4327,7 @@ async function cmdTodoComplete(args: Argv): Promise<void> {
4162
4327
  body,
4163
4328
  })
4164
4329
  if (status === 200) {
4165
- print(json.data)
4330
+ print(normalizeTodoMutationCliResult(json.data))
4166
4331
  return
4167
4332
  }
4168
4333
  if (!isTodoRestRouteMissing(status, json)) throw new Error(JSON.stringify(json))
@@ -4171,7 +4336,7 @@ async function cmdTodoComplete(args: Argv): Promise<void> {
4171
4336
  buildPanelStyleCommand("todo", "complete", body),
4172
4337
  optString(args.flags, "password")
4173
4338
  )
4174
- print(data)
4339
+ print(normalizeTodoMutationCliResult(data))
4175
4340
  }
4176
4341
 
4177
4342
  async function cmdTodoDelete(args: Argv): Promise<void> {
@@ -4194,7 +4359,7 @@ async function cmdTodoDelete(args: Argv): Promise<void> {
4194
4359
  body,
4195
4360
  })
4196
4361
  if (status === 200) {
4197
- print(json.data)
4362
+ print(normalizeTodoMutationCliResult(json.data))
4198
4363
  return
4199
4364
  }
4200
4365
  if (!isTodoRestRouteMissing(status, json)) throw new Error(JSON.stringify(json))
@@ -4203,7 +4368,7 @@ async function cmdTodoDelete(args: Argv): Promise<void> {
4203
4368
  buildPanelStyleCommand("todo", "delete", body),
4204
4369
  optString(args.flags, "password")
4205
4370
  )
4206
- print(data)
4371
+ print(normalizeTodoMutationCliResult(data))
4207
4372
  }
4208
4373
 
4209
4374
  async function cmdTodoRegenerate(args: Argv): Promise<void> {
@@ -4288,7 +4453,7 @@ async function cmdMemberList(args: Argv): Promise<void> {
4288
4453
  secretKeyBase64: identity.secretKeyBase64,
4289
4454
  })
4290
4455
  if (status !== 200) throw new Error(JSON.stringify(json))
4291
- print(json.data)
4456
+ print(buildMemberListCliView(json.data, identity.lobsterId))
4292
4457
  }
4293
4458
 
4294
4459
  async function cmdMemberAddWeb(args: Argv): Promise<void> {
@@ -4297,15 +4462,15 @@ async function cmdMemberAddWeb(args: Argv): Promise<void> {
4297
4462
  const projectId = resolveProjectId(args.flags, identity)
4298
4463
  let name = optString(args.flags, "name") ?? optString(args.flags, "displayName") ?? optString(args.flags, "nickname")
4299
4464
  if (!name && canAskInteractively()) {
4300
- name = await askRequired("请填写网页成员显示名称:")
4465
+ name = await askRequired("请填写普通成员显示名称:")
4301
4466
  }
4302
4467
  const displayName = String(name ?? "").trim()
4303
- if (!displayName) throw new Error("web_member_name_required")
4468
+ if (!displayName) throw new Error("member_name_required")
4304
4469
  const capabilityToken = await issueCapabilityToken(identity, CAPABILITY_ACTION.MEMBER_ADD, { projectId }, { ttlSec: 120, maxRequests: 2, windowSec: 60 })
4305
4470
  const { status, json } = await signedRequest({
4306
4471
  serverBaseUrl: identity.serverBaseUrl,
4307
4472
  method: "POST",
4308
- pathWithQuery: `/v1/projects/${projectId}/members/web`,
4473
+ pathWithQuery: `/v1/projects/${projectId}/members/manual`,
4309
4474
  lobsterId: identity.lobsterId,
4310
4475
  secretKeyBase64: identity.secretKeyBase64,
4311
4476
  capabilityToken,
@@ -4323,15 +4488,15 @@ async function cmdMemberAddWeb(args: Argv): Promise<void> {
4323
4488
  lobsterId: member.lobsterId,
4324
4489
  displayName: member.displayName ?? displayName,
4325
4490
  role: member.role ?? "member",
4326
- memberType: member.memberType ?? "web",
4327
- isWeb: true,
4491
+ memberType: member.memberType ?? "member",
4492
+ isManualMember: true,
4328
4493
  },
4329
4494
  panelUrl,
4330
4495
  projectPanelLink: panelUrl,
4331
4496
  linkSummary: formatLinkSummary(panelUrl, undefined),
4332
4497
  message: panelUrl
4333
- ? `网页成员已添加。请直接转发以下看板链接给该成员:\n${panelUrl}`
4334
- : "网页成员已添加,项目看板将自动同步显示。",
4498
+ ? `普通成员已添加。请直接转发以下看板链接给该成员:\n${panelUrl}`
4499
+ : "普通成员已添加,项目看板将自动同步显示。",
4335
4500
  })
4336
4501
  }
4337
4502
 
@@ -5239,10 +5404,73 @@ function parseProjectScope(flags: Record<string, string | boolean>, allProjects:
5239
5404
  function isTaskMine(task: any, lobsterId: string): boolean {
5240
5405
  if (!task || typeof task !== "object") return false
5241
5406
  if (task.claimedByMe === true || task.mine === true) return true
5242
- const assignee = String(task.assigneeLobsterId ?? task.ownerLobsterId ?? task.claimedBy ?? "")
5407
+ const assignee = String(task.assigneeLobsterId ?? "")
5243
5408
  return assignee.length > 0 && assignee === lobsterId
5244
5409
  }
5245
5410
 
5411
+ function isContainerTaskLike(task: any): boolean {
5412
+ return String(task?.taskKind ?? "").trim().toLowerCase() === "container"
5413
+ }
5414
+
5415
+ function normalizeTaskListStatus(task: any): "todo" | "in_progress" | "done" | "archived" | "blocked" {
5416
+ const raw = String(task?.status ?? "").trim().toLowerCase()
5417
+ if (raw === "done") return "done"
5418
+ if (raw === "archived") return "archived"
5419
+ if (raw === "blocked") return "blocked"
5420
+ if (raw === "taken" || raw === "review") return "in_progress"
5421
+ return "todo"
5422
+ }
5423
+
5424
+ function buildTaskListTaskMap(tasks: any[]): Map<string, any> {
5425
+ return new Map(
5426
+ tasks
5427
+ .map((task) => [String(task?.taskId ?? "").trim(), task] as const)
5428
+ .filter(([taskId]) => Boolean(taskId))
5429
+ )
5430
+ }
5431
+
5432
+ function isTaskDependencyReadyForList(task: any, taskById: Map<string, any>): boolean {
5433
+ if (typeof task?.dependencyReady === "boolean") return Boolean(task.dependencyReady)
5434
+ const deps = Array.isArray(task?.dependsOnTaskIds) ? task.dependsOnTaskIds.map((x: any) => String(x ?? "").trim()).filter(Boolean) : []
5435
+ if (!deps.length) return true
5436
+ return deps.every((depId) => {
5437
+ const dep = taskById.get(depId)
5438
+ if (!dep) return false
5439
+ if (isContainerTaskLike(dep)) {
5440
+ const childExecution = Array.from(taskById.values()).filter(
5441
+ (x) =>
5442
+ !isContainerTaskLike(x) &&
5443
+ String(x?.status ?? "").trim().toLowerCase() !== "archived" &&
5444
+ String(x?.parentContainerId ?? "").trim() === depId
5445
+ )
5446
+ if (!childExecution.length) return normalizeTaskListStatus(dep) === "done"
5447
+ return childExecution.every((x) => normalizeTaskListStatus(x) === "done")
5448
+ }
5449
+ return normalizeTaskListStatus(dep) === "done"
5450
+ })
5451
+ }
5452
+
5453
+ function isTaskActivatedForList(task: any, taskById: Map<string, any>, now = Date.now()): boolean {
5454
+ const status = normalizeTaskListStatus(task)
5455
+ if (status === "done" || status === "archived") return false
5456
+ const manualFocus = task?.showInFocus === true
5457
+ const depReady = isTaskDependencyReadyForList(task, taskById)
5458
+ const plannedStartAt = Number(task?.plannedStartAt ?? 0)
5459
+ const hasDeps = Array.isArray(task?.dependsOnTaskIds) && task.dependsOnTaskIds.length > 0
5460
+ const timeReady = hasDeps || !(plannedStartAt > 0) || plannedStartAt <= now
5461
+ if (manualFocus) return depReady
5462
+ return depReady && timeReady
5463
+ }
5464
+
5465
+ function classifyTaskBoardBucketForList(task: any, taskById: Map<string, any>, now = Date.now()): "focus" | "waiting" | "unclaimed" | "done" | "archived" {
5466
+ const status = normalizeTaskListStatus(task)
5467
+ if (status === "done") return "done"
5468
+ if (status === "archived") return "archived"
5469
+ const hasAssignee = Boolean(String(task?.assigneeLobsterId ?? "").trim())
5470
+ if (!hasAssignee) return "unclaimed"
5471
+ return isTaskActivatedForList(task, taskById, now) ? "focus" : "waiting"
5472
+ }
5473
+
5246
5474
  async function cmdTaskListCross(args: Argv): Promise<void> {
5247
5475
  const filePath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
5248
5476
  const identity = await readIdentity(filePath)
@@ -5438,7 +5666,7 @@ function getPanelCliModules(): Array<{ name: string; actions: Array<{ action: st
5438
5666
  name: "成员",
5439
5667
  actions: [
5440
5668
  { action: "查看成员列表", command: "member list --project <projectId>" },
5441
- { action: "添加网页成员", command: "member add-web --project <projectId> --name <显示名称>" },
5669
+ { action: "添加成员", command: "member add --project <projectId> --name <显示名称>" },
5442
5670
  { action: "移除成员", command: "member remove --project <projectId> --target <lobsterId> --reason <原因>" },
5443
5671
  ],
5444
5672
  },
@@ -5528,7 +5756,7 @@ async function cmdDocList(args: Argv): Promise<void> {
5528
5756
  secretKeyBase64: identity.secretKeyBase64,
5529
5757
  })
5530
5758
  if (status !== 200) throw new Error(JSON.stringify(json))
5531
- print(json.data)
5759
+ print(buildDocListCliView(json.data))
5532
5760
  }
5533
5761
 
5534
5762
  async function cmdDocCreate(args: Argv): Promise<void> {
@@ -6597,6 +6825,10 @@ export async function runCli(argv: string[]): Promise<void> {
6597
6825
  const wantsHelp = Boolean(args.flags.help || args.flags.h || cmd === "help")
6598
6826
  const helpTarget = cmd === "help" ? sub : cmd
6599
6827
  if (wantsHelp && helpTarget && printCommandHelp(helpTarget)) return
6828
+ if (cmd && cmd !== "init" && cmd !== "help") {
6829
+ const identityPath = optString(args.flags, "identity") ?? DEFAULT_IDENTITY_PATH
6830
+ await healIdentityServerBaseUrl(identityPath)
6831
+ }
6600
6832
  if (!wantsHelp && cmd && optString(args.flags, "project")) {
6601
6833
  args = await normalizeProjectFlagForExecution(args)
6602
6834
  }
@@ -6689,7 +6921,7 @@ export async function runCli(argv: string[]): Promise<void> {
6689
6921
  if (cmd === "sync-reopen") return await cmdSyncReopen(args)
6690
6922
  if (cmd === "member") {
6691
6923
  if (sub === "list") return await cmdMemberList(args)
6692
- if (sub === "add-web" || sub === "add_web" || sub === "add") return await cmdMemberAddWeb(args)
6924
+ if (sub === "add" || sub === "add-member" || sub === "add_member" || sub === "add-web" || sub === "add_web") return await cmdMemberAddWeb(args)
6693
6925
  if (sub === "profile-edit") return await cmdMemberProfileEdit(args)
6694
6926
  if (sub === "profile-global-set") return await cmdMemberProfileGlobalSet(args)
6695
6927
  if (sub === "profile-global-edit") return await cmdMemberProfileGlobalEdit(args)
@@ -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
  "微元场景回复必须带【微元协作】前缀。",
@@ -674,9 +680,9 @@ async function syncRulesFromSource(identityPath: string, keepMode: boolean): Pro
674
680
  function isMicroSceneText(text: string, rules: MicroRulesConfig): boolean {
675
681
  const lower = text.toLowerCase()
676
682
  if (/(团队负载|工作负载|团队负荷|团队工作量|谁最忙|team workload|workload)/i.test(lower)) return true
677
- if (/(网页成员|添加成员|新增成员|加入成员)/i.test(lower)) return true
683
+ if (/(添加成员|新增成员|加入成员|普通成员|成员列表|成员情况|项目成员|文件列表|文件情况|项目文件|项目资料)/i.test(lower)) return true
678
684
  if (
679
- /(我的项目|我有哪些项目|看看项目|看看我的项目|项目列表|查项目|创建项目|新建项目|我的任务|我有哪些任务|看看任务|看看我的任务|任务列表|查任务|创建任务|新建任务|我的待办|看看待办|看看我的待办|待办列表|今日待办|明日计划|明天计划|未来事项|打开看板|项目状态)/i.test(
685
+ /(我的项目|我有哪些项目|看看项目|看看我的项目|项目列表|查项目|创建项目|新建项目|我的任务|我有哪些任务|看看任务|看看我的任务|任务列表|查任务|创建任务|新建任务|我的待办|看看待办|看看我的待办|待办列表|今日待办|明日计划|明天计划|未来事项|打开看板|项目状态|成员列表|成员情况|项目成员|文件列表|文件情况|项目文件|项目资料)/i.test(
680
686
  text
681
687
  )
682
688
  ) {
@@ -1315,6 +1321,18 @@ function shouldCreateTodoFromText(lower: string): boolean {
1315
1321
  )
1316
1322
  }
1317
1323
 
1324
+ function shouldDeleteTodoFromText(lower: string): boolean {
1325
+ if (!lower) return false
1326
+ if (!(lower.includes("待办") || lower.includes("计划") || lower.includes("提醒"))) return false
1327
+ return (
1328
+ lower.includes("删除") ||
1329
+ lower.includes("删掉") ||
1330
+ lower.includes("去掉") ||
1331
+ lower.includes("移除") ||
1332
+ lower.includes("取消")
1333
+ )
1334
+ }
1335
+
1318
1336
  function inferTodoWhenFromText(text: string): { when?: string; bucket?: string } {
1319
1337
  const lower = text.toLowerCase()
1320
1338
  if (lower.includes("明天下午")) return { when: "tomorrow 15:00", bucket: "tomorrow" }
@@ -1329,11 +1347,24 @@ function inferTodoWhenFromText(text: string): { when?: string; bucket?: string }
1329
1347
  }
1330
1348
 
1331
1349
  function buildTodoActionTextFromText(text: string): string {
1332
- const trimmed = String(text || "").trim().replace(/[。!!]+$/g, "")
1350
+ const trimmed = String(text || "")
1351
+ .trim()
1352
+ .replace(/[。!!]+$/g, "")
1353
+ .replace(/^(提醒我|提醒一下|安排个待办|加个待办|记个待办|记一下|记得)\s*/u, "")
1333
1354
  if (!trimmed) return "新的待办"
1334
1355
  return trimmed
1335
1356
  }
1336
1357
 
1358
+ function buildTodoDeleteTargetFromText(text: string): string {
1359
+ const trimmed = String(text || "")
1360
+ .trim()
1361
+ .replace(/[。!!]+$/g, "")
1362
+ .replace(/^(请)?(帮我)?(把|将)?(这个|这条)?(待办|计划|提醒)?(删除|删掉|去掉|移除|取消)\s*/u, "")
1363
+ .replace(/^(删除|删掉|去掉|移除|取消)(这个|这条)?(待办|计划|提醒)?\s*/u, "")
1364
+ .trim()
1365
+ return trimmed || "当前待办"
1366
+ }
1367
+
1337
1368
  function isRiskActionKey(actionKey: string): boolean {
1338
1369
  return /(delete|reject|fail|rollback|conflict|error|forbidden|denied)/i.test(actionKey)
1339
1370
  }
@@ -1953,6 +1984,8 @@ function fromText(input: SkillInput): string[] {
1953
1984
  throw new Error("confirm_required_doc_delete")
1954
1985
  }
1955
1986
  if (
1987
+ lower.includes("添加普通成员") ||
1988
+ lower.includes("新增普通成员") ||
1956
1989
  lower.includes("添加网页成员") ||
1957
1990
  lower.includes("新增网页成员") ||
1958
1991
  lower.includes("添加成员") ||
@@ -1961,14 +1994,14 @@ function fromText(input: SkillInput): string[] {
1961
1994
  ) {
1962
1995
  if (!input.projectId) return ["context", "candidates", "--identity", identity]
1963
1996
  const extractedName =
1964
- text.match(/(?:添加|新增|加入)(?:网页)?成员(?:[::\s]*|为|叫|是)?[“"']?([^”"'\n]+)[”"']?$/)?.[1]?.trim() ??
1997
+ text.match(/(?:添加|新增|加入)(?:网页|普通)?成员(?:[::\s]*|为|叫|是)?[“"']?([^”"'\n]+)[”"']?$/)?.[1]?.trim() ??
1965
1998
  text.match(/成员(?:名称|名字|昵称)?(?:为|叫|是)\s*[“"']?([^”"'\n]+)[”"']?$/)?.[1]?.trim() ??
1966
1999
  ""
1967
2000
  const memberName = String(input.memberName ?? input.title ?? extractedName).trim()
1968
2001
  if (memberName) {
1969
- return ["member", "add-web", "--identity", identity, "--project", input.projectId, "--name", memberName]
2002
+ return ["member", "add", "--identity", identity, "--project", input.projectId, "--name", memberName]
1970
2003
  }
1971
- return ["member", "add-web", "--identity", identity, "--project", input.projectId, "--interactive"]
2004
+ return ["member", "add", "--identity", identity, "--project", input.projectId, "--interactive"]
1972
2005
  }
1973
2006
  if (/移除.*成员|踢出.*成员|删除.*成员/.test(lower)) {
1974
2007
  if (!input.projectId) return ["context", "candidates", "--identity", identity]
@@ -2119,6 +2152,33 @@ function fromText(input: SkillInput): string[] {
2119
2152
  if (lower.includes("我的待办") || lower.includes("看看待办") || lower.includes("看看我的待办") || lower.includes("待办列表") || lower.includes("todo列表")) {
2120
2153
  return ["todo", "list", "--identity", identity]
2121
2154
  }
2155
+ if (
2156
+ lower.includes("成员列表") ||
2157
+ lower.includes("成员情况") ||
2158
+ lower.includes("项目成员") ||
2159
+ lower.includes("看看成员") ||
2160
+ lower.includes("查看成员")
2161
+ ) {
2162
+ if (!input.projectId) return ["context", "candidates", "--identity", identity]
2163
+ return ["member", "list", "--identity", identity, "--project", input.projectId]
2164
+ }
2165
+ if (
2166
+ lower.includes("文件列表") ||
2167
+ lower.includes("文件情况") ||
2168
+ lower.includes("项目文件") ||
2169
+ lower.includes("项目资料") ||
2170
+ lower.includes("文档列表") ||
2171
+ lower.includes("看看文件") ||
2172
+ lower.includes("查看文件") ||
2173
+ lower.includes("看看资料")
2174
+ ) {
2175
+ if (!input.projectId) return ["context", "candidates", "--identity", identity]
2176
+ return ["doc", "list", "--identity", identity, "--project", input.projectId]
2177
+ }
2178
+ if (shouldDeleteTodoFromText(lower)) {
2179
+ const actionText = buildTodoDeleteTargetFromText(text)
2180
+ return ["todo", "delete", "--identity", identity, "--actionText", actionText]
2181
+ }
2122
2182
  if (shouldCreateTodoFromText(lower)) {
2123
2183
  const todoTime = inferTodoWhenFromText(text)
2124
2184
  const actionText = buildTodoActionTextFromText(text)
@@ -2517,15 +2577,22 @@ function fromAction(input: SkillInput): string[] {
2517
2577
  case "account.invite-join-template":
2518
2578
  case "micro.invite-join-template":
2519
2579
  return ["account", "invite-join-template", "--identity", identity]
2580
+ case "member.add":
2581
+ case "member.add-member":
2582
+ case "member.add_member":
2520
2583
  case "member.add-web":
2521
2584
  case "member.add_web":
2585
+ case "project.member-add":
2586
+ case "project.member_add":
2587
+ case "project.member-add-member":
2588
+ case "project.member_add_member":
2522
2589
  case "project.member-add-web":
2523
2590
  case "project.member_add_web":
2524
2591
  if (!input.projectId) throw new Error("missing_projectId")
2525
2592
  {
2526
2593
  const memberName = String(input.memberName ?? input.title ?? input.summary ?? "").trim()
2527
- if (!memberName) throw new Error("web_member_name_required")
2528
- return ["member", "add-web", "--identity", identity, "--project", input.projectId, "--name", memberName]
2594
+ if (!memberName) throw new Error("member_name_required")
2595
+ return ["member", "add", "--identity", identity, "--project", input.projectId, "--name", memberName]
2529
2596
  }
2530
2597
  case "member.remove":
2531
2598
  if (!input.projectId) throw new Error("missing_projectId")
@@ -4573,11 +4640,11 @@ function mapError(err: unknown, detailed: boolean): SkillError {
4573
4640
  if (rawError.includes("storage_quota_exceeded")) {
4574
4641
  return { ok: false, errorCode: "CLI_ERROR", message: "存储空间已满,请联系 founder 升级容量。", ...(detailed ? { rawError } : {}) }
4575
4642
  }
4576
- if (rawError.includes("web_member_name_required")) {
4577
- return { ok: false, errorCode: "CLI_ERROR", message: "请先告诉我网页成员的名称。", ...(detailed ? { rawError } : {}) }
4643
+ if (rawError.includes("member_name_required") || rawError.includes("web_member_name_required")) {
4644
+ return { ok: false, errorCode: "CLI_ERROR", message: "请先告诉我成员的名称。", ...(detailed ? { rawError } : {}) }
4578
4645
  }
4579
- if (rawError.includes("invalid_web_member_role_supported_member_only")) {
4580
- return { ok: false, errorCode: "CLI_ERROR", message: "网页成员当前仅支持 member 角色。", ...(detailed ? { rawError } : {}) }
4646
+ if (rawError.includes("invalid_member_role_supported_member_only") || rawError.includes("invalid_web_member_role_supported_member_only")) {
4647
+ return { ok: false, errorCode: "CLI_ERROR", message: "普通成员当前仅支持 member 角色。", ...(detailed ? { rawError } : {}) }
4581
4648
  }
4582
4649
  if (rawError.includes("doc_file_too_large")) {
4583
4650
  return { ok: false, errorCode: "CLI_ERROR", message: "文件上传失败:单文件超过 10MB,请压缩后重试。", ...(detailed ? { rawError } : {}) }
@@ -20,7 +20,7 @@ export function normalizeServerBaseUrl(raw: string): string {
20
20
  if (host === "api.magon.com.cn") {
21
21
  url.protocol = "https:"
22
22
  const rawPath = String(url.pathname || "/")
23
- if (/^\/(?:api\/)?agent-register(?:\/|$)/.test(rawPath)) {
23
+ if (/^\/(?:api\/)?agent-(?:register|bind)(?:\/|$)/.test(rawPath)) {
24
24
  url.pathname = "/api"
25
25
  } else {
26
26
  if (!url.pathname || url.pathname === "/") url.pathname = "/api"
@@ -29,7 +29,7 @@ export function normalizeServerBaseUrl(raw: string): string {
29
29
  url.hash = ""
30
30
  return url.toString().replace(/\/$/, "")
31
31
  }
32
- if (/^\/(?:api\/)?agent-register(?:\/|$)/.test(String(url.pathname || "/"))) {
32
+ if (/^\/(?:api\/)?agent-(?:register|bind)(?:\/|$)/.test(String(url.pathname || "/"))) {
33
33
  url.pathname = "/api"
34
34
  }
35
35
  url.hash = ""
@@ -55,6 +55,22 @@ export async function readIdentity(filePath: string): Promise<WeiyuanIdentityFil
55
55
  return parsed
56
56
  }
57
57
 
58
+ export async function healIdentityServerBaseUrl(filePath: string): Promise<boolean> {
59
+ try {
60
+ const raw = await fs.readFile(filePath, "utf8")
61
+ const parsed = JSON.parse(raw) as WeiyuanIdentityFileV1
62
+ if (!parsed || parsed.version !== 1) return false
63
+ const before = String(parsed.serverBaseUrl || "")
64
+ const normalized = normalizeServerBaseUrl(before)
65
+ if (!normalized || normalized === before) return false
66
+ parsed.serverBaseUrl = normalized
67
+ await writeIdentity(filePath, parsed)
68
+ return true
69
+ } catch {
70
+ return false
71
+ }
72
+ }
73
+
58
74
  export async function writeIdentity(filePath: string, data: WeiyuanIdentityFileV1): Promise<void> {
59
75
  await fs.mkdir(path.dirname(path.resolve(filePath)), { recursive: true })
60
76
  const tmp = `${filePath}.tmp`