libp2p-mesh 2026.6.14 → 2026.6.16
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/README.md +12 -0
- package/dist/src/user-md-attributes.d.ts +1 -0
- package/dist/src/user-md-attributes.js +48 -1
- package/package.json +1 -1
- package/src/user-md-attributes.ts +55 -1
package/README.md
CHANGED
|
@@ -476,6 +476,18 @@ There are two sources:
|
|
|
476
476
|
- `USER.md` tags are extracted read-only at gateway startup. The plugin never edits `USER.md`.
|
|
477
477
|
- `user-profile.json` stores manually managed structured attributes such as group, project, role, skill, or a custom key.
|
|
478
478
|
|
|
479
|
+
By default, `USER.md` is read from:
|
|
480
|
+
|
|
481
|
+
```text
|
|
482
|
+
~/.openclaw/workspace/USER.md
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
When `OPENCLAW_STATE_DIR` is set, the plugin reads:
|
|
486
|
+
|
|
487
|
+
```text
|
|
488
|
+
$OPENCLAW_STATE_DIR/workspace/USER.md
|
|
489
|
+
```
|
|
490
|
+
|
|
479
491
|
Run the profile wizard to manage structured attributes:
|
|
480
492
|
|
|
481
493
|
```bash
|
|
@@ -6,6 +6,7 @@ export type UserMdAttributeSource = {
|
|
|
6
6
|
warn?: (message: string) => void;
|
|
7
7
|
};
|
|
8
8
|
};
|
|
9
|
+
export declare function resolveUserMdPath(customPath?: string): string;
|
|
9
10
|
export declare function extractUserMdTags(markdown: string): UserPublicAttribute[];
|
|
10
11
|
export declare function createUserMdAttributeSource(options?: UserMdAttributeSource): {
|
|
11
12
|
loadTags(): Promise<UserPublicAttribute[]>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { normalizeAttributeValue } from "./user-attributes.js";
|
|
4
5
|
const MAX_TAGS = 10;
|
|
@@ -8,12 +9,23 @@ const FIELD_LINE_PATTERN = /^(?:[-*]\s*)?(?:#{1,6}\s*)?(name|what to call them|n
|
|
|
8
9
|
const TEMPLATE_PATTERN = /\b(?:todo|tbd|n\/a|none|unknown|your name|add notes here|template placeholder|placeholder)\b/i;
|
|
9
10
|
const EXPLICIT_LIST_SEPARATOR_PATTERN = /[,,、;;|/]/;
|
|
10
11
|
const ENGLISH_TAG_PATTERN = /^(?:[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z0-9]+)?|libp2p|node\.js)$/i;
|
|
12
|
+
const TECH_TOKEN_PATTERN = /\b(?:[A-Z][A-Z0-9]{1,}|[A-Za-z]*[0-9][A-Za-z0-9]*|libp2p|node\.js)\b/g;
|
|
11
13
|
const CHINESE_TAG_PATTERN = /[\p{Script=Han}]{2,8}/gu;
|
|
12
14
|
const CHINESE_CONTEXT_PATTERNS = [
|
|
13
15
|
/(?:在|来自|加入|参与|负责)([\p{Script=Han}]{2,8})(?:做|写|用|项目|团队|实验室|方向)?/gu,
|
|
14
16
|
/(?:做|写|维护|负责|参与)([\p{Script=Han}]{2,8})(?:项目|插件|工具|方向)?/gu,
|
|
15
17
|
];
|
|
16
18
|
const CHINESE_SENTENCE_WORD_PATTERN = /(?:今天|明天|昨天|今晚|晚上|早上|上午|下午|八点|同步|一下|进展|开会|讨论|安排|提醒|需要|已经|可以|应该|我们|你们|他们)/u;
|
|
19
|
+
const CHINESE_STOP_WORDS = new Set([
|
|
20
|
+
"关于",
|
|
21
|
+
"正在",
|
|
22
|
+
"当前",
|
|
23
|
+
"相关",
|
|
24
|
+
"工作",
|
|
25
|
+
"项目",
|
|
26
|
+
"网络",
|
|
27
|
+
"专注",
|
|
28
|
+
]);
|
|
17
29
|
const COMMON_WORDS = new Set([
|
|
18
30
|
"and",
|
|
19
31
|
"also",
|
|
@@ -42,6 +54,27 @@ const ENGLISH_TAG_FIELDS = new Set([
|
|
|
42
54
|
"interest",
|
|
43
55
|
"interests",
|
|
44
56
|
]);
|
|
57
|
+
const TECH_TOKEN_FIELDS = new Set([
|
|
58
|
+
"notes",
|
|
59
|
+
"note",
|
|
60
|
+
"context",
|
|
61
|
+
"project",
|
|
62
|
+
"projects",
|
|
63
|
+
"skill",
|
|
64
|
+
"skills",
|
|
65
|
+
"interest",
|
|
66
|
+
"interests",
|
|
67
|
+
]);
|
|
68
|
+
export function resolveUserMdPath(customPath) {
|
|
69
|
+
if (customPath) {
|
|
70
|
+
return customPath;
|
|
71
|
+
}
|
|
72
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR;
|
|
73
|
+
if (stateDir) {
|
|
74
|
+
return path.join(stateDir, "workspace", "USER.md");
|
|
75
|
+
}
|
|
76
|
+
return path.join(homedir(), ".openclaw", "workspace", "USER.md");
|
|
77
|
+
}
|
|
45
78
|
function isTemplateText(value) {
|
|
46
79
|
const trimmed = value.trim();
|
|
47
80
|
return trimmed.length === 0 || TEMPLATE_PATTERN.test(trimmed) || /^\[[^\]]+\]$/.test(trimmed);
|
|
@@ -68,8 +101,19 @@ function trimChineseCandidate(value) {
|
|
|
68
101
|
}
|
|
69
102
|
function isStableChineseCandidate(value) {
|
|
70
103
|
return (/^[\p{Script=Han}]{2,8}$/u.test(value) &&
|
|
104
|
+
!CHINESE_STOP_WORDS.has(value) &&
|
|
71
105
|
!CHINESE_SENTENCE_WORD_PATTERN.test(value));
|
|
72
106
|
}
|
|
107
|
+
function collectTechnicalTokens(value) {
|
|
108
|
+
const tokens = [];
|
|
109
|
+
for (const match of value.matchAll(TECH_TOKEN_PATTERN)) {
|
|
110
|
+
const token = trimCandidate(match[0] ?? "");
|
|
111
|
+
if (isStableEnglishCandidate(token)) {
|
|
112
|
+
tokens.push(token);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return tokens;
|
|
116
|
+
}
|
|
73
117
|
function collectChineseCandidates(value) {
|
|
74
118
|
const candidates = [];
|
|
75
119
|
for (const match of value.matchAll(CHINESE_TAG_PATTERN)) {
|
|
@@ -136,6 +180,9 @@ function collectCandidates(line) {
|
|
|
136
180
|
}
|
|
137
181
|
const explicitField = fieldValue(line);
|
|
138
182
|
const explicitFieldValue = explicitField?.value;
|
|
183
|
+
if (explicitField && TECH_TOKEN_FIELDS.has(explicitField.field)) {
|
|
184
|
+
candidates.push(...collectTechnicalTokens(explicitField.value));
|
|
185
|
+
}
|
|
139
186
|
if (/^[\p{Script=Han}]{2,8}$/u.test(text) && isStableChineseCandidate(text)) {
|
|
140
187
|
candidates.push(text);
|
|
141
188
|
}
|
|
@@ -183,7 +230,7 @@ export function extractUserMdTags(markdown) {
|
|
|
183
230
|
return tags;
|
|
184
231
|
}
|
|
185
232
|
export function createUserMdAttributeSource(options) {
|
|
186
|
-
const filePath = options?.path
|
|
233
|
+
const filePath = resolveUserMdPath(options?.path);
|
|
187
234
|
const logger = options?.logger;
|
|
188
235
|
return {
|
|
189
236
|
async loadTags() {
|
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
import type { UserPublicAttribute } from "./types.js";
|
|
@@ -15,6 +16,7 @@ const TEMPLATE_PATTERN =
|
|
|
15
16
|
/\b(?:todo|tbd|n\/a|none|unknown|your name|add notes here|template placeholder|placeholder)\b/i;
|
|
16
17
|
const EXPLICIT_LIST_SEPARATOR_PATTERN = /[,,、;;|/]/;
|
|
17
18
|
const ENGLISH_TAG_PATTERN = /^(?:[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z0-9]+)?|libp2p|node\.js)$/i;
|
|
19
|
+
const TECH_TOKEN_PATTERN = /\b(?:[A-Z][A-Z0-9]{1,}|[A-Za-z]*[0-9][A-Za-z0-9]*|libp2p|node\.js)\b/g;
|
|
18
20
|
const CHINESE_TAG_PATTERN = /[\p{Script=Han}]{2,8}/gu;
|
|
19
21
|
const CHINESE_CONTEXT_PATTERNS = [
|
|
20
22
|
/(?:在|来自|加入|参与|负责)([\p{Script=Han}]{2,8})(?:做|写|用|项目|团队|实验室|方向)?/gu,
|
|
@@ -22,6 +24,16 @@ const CHINESE_CONTEXT_PATTERNS = [
|
|
|
22
24
|
];
|
|
23
25
|
const CHINESE_SENTENCE_WORD_PATTERN =
|
|
24
26
|
/(?:今天|明天|昨天|今晚|晚上|早上|上午|下午|八点|同步|一下|进展|开会|讨论|安排|提醒|需要|已经|可以|应该|我们|你们|他们)/u;
|
|
27
|
+
const CHINESE_STOP_WORDS = new Set([
|
|
28
|
+
"关于",
|
|
29
|
+
"正在",
|
|
30
|
+
"当前",
|
|
31
|
+
"相关",
|
|
32
|
+
"工作",
|
|
33
|
+
"项目",
|
|
34
|
+
"网络",
|
|
35
|
+
"专注",
|
|
36
|
+
]);
|
|
25
37
|
const COMMON_WORDS = new Set([
|
|
26
38
|
"and",
|
|
27
39
|
"also",
|
|
@@ -50,6 +62,17 @@ const ENGLISH_TAG_FIELDS = new Set([
|
|
|
50
62
|
"interest",
|
|
51
63
|
"interests",
|
|
52
64
|
]);
|
|
65
|
+
const TECH_TOKEN_FIELDS = new Set([
|
|
66
|
+
"notes",
|
|
67
|
+
"note",
|
|
68
|
+
"context",
|
|
69
|
+
"project",
|
|
70
|
+
"projects",
|
|
71
|
+
"skill",
|
|
72
|
+
"skills",
|
|
73
|
+
"interest",
|
|
74
|
+
"interests",
|
|
75
|
+
]);
|
|
53
76
|
|
|
54
77
|
export type UserMdAttributeSource = {
|
|
55
78
|
path?: string;
|
|
@@ -59,6 +82,19 @@ export type UserMdAttributeSource = {
|
|
|
59
82
|
};
|
|
60
83
|
};
|
|
61
84
|
|
|
85
|
+
export function resolveUserMdPath(customPath?: string): string {
|
|
86
|
+
if (customPath) {
|
|
87
|
+
return customPath;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR;
|
|
91
|
+
if (stateDir) {
|
|
92
|
+
return path.join(stateDir, "workspace", "USER.md");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return path.join(homedir(), ".openclaw", "workspace", "USER.md");
|
|
96
|
+
}
|
|
97
|
+
|
|
62
98
|
function isTemplateText(value: string): boolean {
|
|
63
99
|
const trimmed = value.trim();
|
|
64
100
|
return trimmed.length === 0 || TEMPLATE_PATTERN.test(trimmed) || /^\[[^\]]+\]$/.test(trimmed);
|
|
@@ -90,10 +126,24 @@ function trimChineseCandidate(value: string): string {
|
|
|
90
126
|
function isStableChineseCandidate(value: string): boolean {
|
|
91
127
|
return (
|
|
92
128
|
/^[\p{Script=Han}]{2,8}$/u.test(value) &&
|
|
129
|
+
!CHINESE_STOP_WORDS.has(value) &&
|
|
93
130
|
!CHINESE_SENTENCE_WORD_PATTERN.test(value)
|
|
94
131
|
);
|
|
95
132
|
}
|
|
96
133
|
|
|
134
|
+
function collectTechnicalTokens(value: string): string[] {
|
|
135
|
+
const tokens: string[] = [];
|
|
136
|
+
|
|
137
|
+
for (const match of value.matchAll(TECH_TOKEN_PATTERN)) {
|
|
138
|
+
const token = trimCandidate(match[0] ?? "");
|
|
139
|
+
if (isStableEnglishCandidate(token)) {
|
|
140
|
+
tokens.push(token);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return tokens;
|
|
145
|
+
}
|
|
146
|
+
|
|
97
147
|
function collectChineseCandidates(value: string): string[] {
|
|
98
148
|
const candidates: string[] = [];
|
|
99
149
|
|
|
@@ -177,6 +227,10 @@ function collectCandidates(line: string): string[] {
|
|
|
177
227
|
const explicitField = fieldValue(line);
|
|
178
228
|
const explicitFieldValue = explicitField?.value;
|
|
179
229
|
|
|
230
|
+
if (explicitField && TECH_TOKEN_FIELDS.has(explicitField.field)) {
|
|
231
|
+
candidates.push(...collectTechnicalTokens(explicitField.value));
|
|
232
|
+
}
|
|
233
|
+
|
|
180
234
|
if (/^[\p{Script=Han}]{2,8}$/u.test(text) && isStableChineseCandidate(text)) {
|
|
181
235
|
candidates.push(text);
|
|
182
236
|
}
|
|
@@ -236,7 +290,7 @@ export function extractUserMdTags(markdown: string): UserPublicAttribute[] {
|
|
|
236
290
|
export function createUserMdAttributeSource(options?: UserMdAttributeSource): {
|
|
237
291
|
loadTags(): Promise<UserPublicAttribute[]>;
|
|
238
292
|
} {
|
|
239
|
-
const filePath = options?.path
|
|
293
|
+
const filePath = resolveUserMdPath(options?.path);
|
|
240
294
|
const logger = options?.logger;
|
|
241
295
|
|
|
242
296
|
return {
|