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
|
@@ -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
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
const
|
|
2110
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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("
|
|
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/
|
|
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 ?? "
|
|
4327
|
-
|
|
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
|
-
?
|
|
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 ??
|
|
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: "
|
|
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-
|
|
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 (/(
|
|
683
|
+
if (/(添加成员|新增成员|加入成员|普通成员|成员列表|成员情况|项目成员|文件列表|文件情况|项目文件|项目资料)/i.test(lower)) return true
|
|
678
684
|
if (
|
|
679
|
-
/(
|
|
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 || "")
|
|
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(/(?:添加|新增|加入)(
|
|
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
|
|
2002
|
+
return ["member", "add", "--identity", identity, "--project", input.projectId, "--name", memberName]
|
|
1970
2003
|
}
|
|
1971
|
-
return ["member", "add
|
|
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("
|
|
2528
|
-
return ["member", "add
|
|
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: "
|
|
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: "
|
|
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`
|