Skip to content

Commit d9134d6

Browse files
committed
feat(memory): 增加长度校验及限流提示并强化安全确认
- 在多条命令中统一添加对 userId 和 memoryLibraryId 长度的校验 - 新增 profile schema 名称、描述及属性字段长度断言 - 为 delete 内存节点命令添加高风险删除确认机制及 --yes 标志 - 增加账号级别限流说明,提示遇 HTTP 429 需降低请求频率 - 统一移除分散的长度常量,集中定义并使用共享常量 - 相关 E2E 测试覆盖长度限制和高风险操作确认情况 - 支持内存消息内容多样化,新增 MemoryContentPart 类型定义 - 内存文档中新增限流描述及删除命令风险提示,强调确认操作要求
1 parent 75a7100 commit d9134d6

26 files changed

Lines changed: 452 additions & 23 deletions

packages/commands/src/commands/memory/add.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import {
1313
import { emitResult, emitBare } from "bailian-cli-runtime";
1414
import {
1515
MEMORY_LIBRARY_FLAG,
16+
MEMORY_RATE_LIMIT_NOTE,
1617
MEMORY_WORKSPACE_NOTE,
18+
MAX_CUSTOM_CONTENT_LENGTH,
1719
PROJECT_ID_FLAG,
1820
WORKSPACE_FLAG,
21+
checkMemoryScopeLengths,
1922
parseJsonArrayFlag,
2023
parseJsonObjectFlag,
2124
resolveWorkspaceId,
@@ -71,10 +74,6 @@ type AddFlags = ParsedFlags<typeof ADD_FLAGS>;
7174

7275
/** Max messages accepted per AddMemory call (a Q&A pair counts as 2). */
7376
const MAX_MESSAGES = 50;
74-
/** Max characters accepted for custom_content. */
75-
const MAX_CONTENT_LENGTH = 512;
76-
/** Max characters accepted for user_id. */
77-
const MAX_USER_ID_LENGTH = 64;
7877

7978
export default defineCommand({
8079
description: {
@@ -96,6 +95,7 @@ export default defineCommand({
9695
"The response lists the changed memory nodes; one call can add, update or delete several at once.",
9796
"zh-CN": "返回结果是变更的记忆片段列表;一次调用可能同时新增、更新或删除多条。",
9897
},
98+
MEMORY_RATE_LIMIT_NOTE,
9999
],
100100
exampleArgs: [
101101
{
@@ -118,10 +118,10 @@ export default defineCommand({
118118
],
119119
validate: (flags: AddFlags) => {
120120
if (!flags.messages && !flags.content) return "Provide --messages or --content.";
121-
if (flags.userId.length > MAX_USER_ID_LENGTH)
122-
return `--user-id must be at most ${MAX_USER_ID_LENGTH} characters.`;
123-
if (flags.content && flags.content.length > MAX_CONTENT_LENGTH)
124-
return `--content must be at most ${MAX_CONTENT_LENGTH} characters.`;
121+
const scopeError = checkMemoryScopeLengths(flags);
122+
if (scopeError) return scopeError;
123+
if (flags.content && flags.content.length > MAX_CUSTOM_CONTENT_LENGTH)
124+
return `--content must be at most ${MAX_CUSTOM_CONTENT_LENGTH} characters.`;
125125
return undefined;
126126
},
127127
async run(ctx) {

packages/commands/src/commands/memory/delete.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
MEMORY_LIBRARY_FLAG,
1212
MEMORY_WORKSPACE_NOTE,
1313
WORKSPACE_FLAG,
14+
checkMemoryScopeLengths,
1415
resolveWorkspaceId,
1516
} from "./shared.ts";
1617

@@ -37,10 +38,28 @@ const DELETE_FLAGS = {
3738
export default defineCommand({
3839
description: { "en-US": "Delete a memory node", "zh-CN": "删除记忆节点" },
3940
auth: "apiKey",
41+
risk: {
42+
level: "high",
43+
message: {
44+
"en-US": "This permanently deletes the specified memory node and cannot be undone.",
45+
"zh-CN": "该操作会永久删除指定的记忆片段,且无法恢复。",
46+
},
47+
},
4048
usageArgs: "--node-id <id> --user-id <id> [flags]",
4149
flags: DELETE_FLAGS,
42-
notes: [MEMORY_WORKSPACE_NOTE],
43-
exampleArgs: ["--node-id node_xxx --user-id user1 --workspace-id ws_xxx"],
50+
notes: [
51+
MEMORY_WORKSPACE_NOTE,
52+
{
53+
"en-US":
54+
"Irreversible — the memory node is permanently removed. Run `memory list` first to confirm the node ID.",
55+
"zh-CN": "该操作不可撤销——记忆片段将被永久删除。建议先用 `memory list` 确认节点 ID。",
56+
},
57+
],
58+
exampleArgs: [
59+
"--node-id node_xxx --user-id user1 --workspace-id ws_xxx",
60+
"--node-id node_xxx --user-id user1 --yes",
61+
],
62+
validate: (flags) => checkMemoryScopeLengths(flags),
4463
async run(ctx) {
4564
const { settings, flags } = ctx;
4665
const nodeId = flags.nodeId;

packages/commands/src/commands/memory/list.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
MEMORY_WORKSPACE_NOTE,
1414
PROJECT_ID_FLAG,
1515
WORKSPACE_FLAG,
16+
checkMemoryScopeLengths,
1617
resolveWorkspaceId,
1718
} from "./shared.ts";
1819

@@ -53,6 +54,8 @@ export default defineCommand({
5354
"--user-id user1 --memory-library-id lib_xxx --output json",
5455
],
5556
validate: (flags) => {
57+
const scopeError = checkMemoryScopeLengths(flags);
58+
if (scopeError) return scopeError;
5659
if (flags.page !== undefined && flags.page < 1) return "--page must be at least 1.";
5760
if (flags.pageSize !== undefined && flags.pageSize < 1)
5861
return "--page-size must be at least 1.";

packages/commands/src/commands/memory/profile-create.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineCommand,
3+
UsageError,
34
memoryEndpoint,
45
profileSchemaPath,
56
detectOutputFormat,
@@ -15,6 +16,9 @@ import {
1516
MEMORY_WORKSPACE_NOTE,
1617
PLAN_VERSION_FLAG,
1718
WORKSPACE_FLAG,
19+
assertAttributeFieldLengths,
20+
checkMemoryScopeLengths,
21+
checkProfileSchemaTextLengths,
1822
parseJsonArrayFlag,
1923
resolveWorkspaceId,
2024
} from "./shared.ts";
@@ -76,10 +80,20 @@ export default defineCommand({
7680
'--name "user_basic" --attributes \'[{"name":"age"}]\' --plan-version lite --memory-library-id lib_xxx',
7781
},
7882
],
83+
validate: (flags) =>
84+
checkMemoryScopeLengths(flags) ?? checkProfileSchemaTextLengths(flags) ?? undefined,
7985
async run(ctx) {
8086
const { settings, flags } = ctx;
8187

8288
const attributes = parseJsonArrayFlag<ProfileAttribute>("--attributes", flags.attributes);
89+
if (attributes.length === 0) {
90+
throw new UsageError("--attributes must contain at least one attribute");
91+
}
92+
attributes.forEach((attribute, index) => {
93+
const position = `--attributes[${index}]`;
94+
if (!attribute.name) throw new UsageError(`${position}.name is required`);
95+
assertAttributeFieldLengths(position, attribute);
96+
});
8397

8498
const body: ProfileSchemaCreateRequest = { name: flags.name, attributes };
8599
if (flags.description) body.description = flags.description;

packages/commands/src/commands/memory/profile-delete.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
MEMORY_LIBRARY_FLAG,
1212
MEMORY_WORKSPACE_NOTE,
1313
WORKSPACE_FLAG,
14+
checkMemoryScopeLengths,
1415
resolveWorkspaceId,
1516
} from "./shared.ts";
1617

@@ -54,6 +55,7 @@ export default defineCommand({
5455
},
5556
],
5657
exampleArgs: ["--schema-id schema_xxx --workspace-id ws_xxx", "--schema-id schema_xxx --yes"],
58+
validate: (flags) => checkMemoryScopeLengths(flags),
5759
async run(ctx) {
5860
const { settings, flags } = ctx;
5961

packages/commands/src/commands/memory/profile-get.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
MEMORY_LIBRARY_FLAG,
1313
MEMORY_WORKSPACE_NOTE,
1414
WORKSPACE_FLAG,
15+
checkMemoryScopeLengths,
1516
resolveWorkspaceId,
1617
} from "./shared.ts";
1718

@@ -56,6 +57,7 @@ export default defineCommand({
5657
},
5758
],
5859
exampleArgs: ["--schema-id schema_xxx --user-id user1 --workspace-id ws_xxx"],
60+
validate: (flags) => checkMemoryScopeLengths(flags),
5961
async run(ctx) {
6062
const { settings, flags } = ctx;
6163

packages/commands/src/commands/memory/profile-list.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
MEMORY_LIBRARY_FLAG,
1313
MEMORY_WORKSPACE_NOTE,
1414
WORKSPACE_FLAG,
15+
checkMemoryScopeLengths,
1516
resolveWorkspaceId,
1617
} from "./shared.ts";
1718

@@ -42,6 +43,8 @@ export default defineCommand({
4243
"--memory-library-id lib_xxx --output json",
4344
],
4445
validate: (flags) => {
46+
const scopeError = checkMemoryScopeLengths(flags);
47+
if (scopeError) return scopeError;
4548
if (flags.page !== undefined && flags.page < 1) return "--page must be at least 1.";
4649
if (flags.pageSize !== undefined && flags.pageSize < 1)
4750
return "--page-size must be at least 1.";

packages/commands/src/commands/memory/profile-show.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
MEMORY_LIBRARY_FLAG,
1313
MEMORY_WORKSPACE_NOTE,
1414
WORKSPACE_FLAG,
15+
checkMemoryScopeLengths,
1516
resolveWorkspaceId,
1617
} from "./shared.ts";
1718

@@ -50,6 +51,7 @@ export default defineCommand({
5051
"--schema-id schema_xxx --workspace-id ws_xxx",
5152
"--schema-id schema_xxx --output json",
5253
],
54+
validate: (flags) => checkMemoryScopeLengths(flags),
5355
async run(ctx) {
5456
const { settings, flags } = ctx;
5557

packages/commands/src/commands/memory/profile-update.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import {
1313
MEMORY_LIBRARY_FLAG,
1414
MEMORY_WORKSPACE_NOTE,
1515
WORKSPACE_FLAG,
16+
assertAttributeFieldLengths,
17+
checkMemoryScopeLengths,
18+
checkProfileSchemaTextLengths,
1619
parseJsonArrayFlag,
1720
resolveWorkspaceId,
1821
} from "./shared.ts";
@@ -64,6 +67,7 @@ function validateOperations(operations: ProfileSchemaAttributeOperation[]): void
6467
if (operation.op !== "add" && !operation.attribute_id) {
6568
throw new UsageError(`${position}.attribute_id is required when op is "${operation.op}"`);
6669
}
70+
assertAttributeFieldLengths(position, operation);
6771
});
6872
}
6973

@@ -99,7 +103,7 @@ export default defineCommand({
99103
validate: (flags) => {
100104
if (!flags.name && !flags.description && !flags.attributesOperations)
101105
return "Provide --name, --description, or --attributes-operations.";
102-
return undefined;
106+
return checkMemoryScopeLengths(flags) ?? checkProfileSchemaTextLengths(flags) ?? undefined;
103107
},
104108
async run(ctx) {
105109
const { settings, flags } = ctx;

packages/commands/src/commands/memory/search.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import {
1313
import { emitResult, emitBare } from "bailian-cli-runtime";
1414
import {
1515
MEMORY_LIBRARY_FLAG,
16+
MEMORY_RATE_LIMIT_NOTE,
1617
MEMORY_WORKSPACE_NOTE,
1718
PLAN_VERSION_FLAG,
1819
WORKSPACE_FLAG,
20+
checkMemoryScopeLengths,
1921
parseJsonArrayFlag,
2022
resolveWorkspaceId,
2123
} from "./shared.ts";
@@ -117,6 +119,7 @@ export default defineCommand({
117119
"--plan-version overrides --enable-rerank and changes the price: pro reranks, lite does not.",
118120
"zh-CN": "--plan-version 覆盖 --enable-rerank 且影响计费:pro 开启重排,lite 不开启。",
119121
},
122+
MEMORY_RATE_LIMIT_NOTE,
120123
],
121124
exampleArgs: [
122125
{
@@ -135,6 +138,8 @@ export default defineCommand({
135138
],
136139
validate: (flags: SearchFlags) => {
137140
if (!flags.query && !flags.messages) return "Provide --query or --messages.";
141+
const scopeError = checkMemoryScopeLengths(flags);
142+
if (scopeError) return scopeError;
138143
if (flags.topK !== undefined && (flags.topK < 1 || flags.topK > MAX_TOP_K))
139144
return `--top-k must be between 1 and ${MAX_TOP_K}.`;
140145
if (flags.minScore !== undefined && (flags.minScore < 0 || flags.minScore > 1))

0 commit comments

Comments
 (0)