@sunnoy/wecom 2.1.0 → 2.2.1
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 +6 -2
- package/index.js +2 -0
- package/openclaw.plugin.json +3 -0
- package/package.json +5 -3
- package/skills/wecom-contact-lookup/SKILL.md +167 -0
- package/skills/wecom-doc-manager/SKILL.md +106 -0
- package/skills/wecom-doc-manager/references/api-create-doc.md +56 -0
- package/skills/wecom-doc-manager/references/api-edit-doc-content.md +68 -0
- package/skills/wecom-doc-manager/references/api-export-document.md +88 -0
- package/skills/wecom-edit-todo/SKILL.md +254 -0
- package/skills/wecom-get-todo-detail/SKILL.md +148 -0
- package/skills/wecom-get-todo-list/SKILL.md +132 -0
- package/skills/wecom-meeting-create/SKILL.md +163 -0
- package/skills/wecom-meeting-create/references/example-full.md +30 -0
- package/skills/wecom-meeting-create/references/example-reminder.md +46 -0
- package/skills/wecom-meeting-create/references/example-security.md +22 -0
- package/skills/wecom-meeting-manage/SKILL.md +141 -0
- package/skills/wecom-meeting-query/SKILL.md +335 -0
- package/skills/wecom-preflight/SKILL.md +103 -0
- package/skills/wecom-schedule/SKILL.md +164 -0
- package/skills/wecom-schedule/references/api-check-availability.md +56 -0
- package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
- package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
- package/skills/wecom-schedule/references/api-update-schedule.md +30 -0
- package/skills/wecom-schedule/references/ref-reminders.md +24 -0
- package/skills/wecom-smartsheet-data/SKILL.md +76 -0
- package/skills/wecom-smartsheet-data/references/api-get-records.md +61 -0
- package/skills/wecom-smartsheet-data/references/cell-value-formats.md +120 -0
- package/skills/wecom-smartsheet-schema/SKILL.md +96 -0
- package/skills/wecom-smartsheet-schema/references/field-types.md +43 -0
- package/wecom/accounts.js +1 -0
- package/wecom/callback-inbound.js +133 -33
- package/wecom/channel-plugin.js +107 -125
- package/wecom/constants.js +83 -3
- package/wecom/mcp-config.js +146 -0
- package/wecom/mcp-tool.js +660 -0
- package/wecom/media-uploader.js +208 -0
- package/wecom/openclaw-compat.js +302 -0
- package/wecom/reqid-store.js +146 -0
- package/wecom/target.js +3 -2
- package/wecom/workspace-template.js +107 -21
- package/wecom/ws-monitor.js +778 -328
- package/image-processor.js +0 -175
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
copyFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
renameSync,
|
|
8
|
+
statSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
2
11
|
import { homedir } from "node:os";
|
|
3
12
|
import { join } from "node:path";
|
|
4
13
|
import { logger } from "../logger.js";
|
|
@@ -19,8 +28,10 @@ function expandTilde(p) {
|
|
|
19
28
|
|
|
20
29
|
// --- mtime caches for force-reseed ---
|
|
21
30
|
const _templateMtimeCache = new Map(); // templateDir → { maxMtimeMs, checkedAt }
|
|
22
|
-
const _agentSeedMtimeCache = new Map(); // `${templateDir}::${agentId}` → maxMtimeMs
|
|
23
31
|
const TEMPLATE_MTIME_CACHE_TTL_MS = 60_000;
|
|
32
|
+
const TEMPLATE_STATE_DIRNAME = ".openclaw";
|
|
33
|
+
const TEMPLATE_STATE_FILENAME = "wecom-template-state.json";
|
|
34
|
+
const TEMPLATE_STATE_VERSION = 1;
|
|
24
35
|
|
|
25
36
|
function getTemplateMaxMtimeMs(templateDir) {
|
|
26
37
|
const now = Date.now();
|
|
@@ -43,7 +54,7 @@ function getTemplateMaxMtimeMs(templateDir) {
|
|
|
43
54
|
|
|
44
55
|
export function clearTemplateMtimeCache({ agentSeedCache = true } = {}) {
|
|
45
56
|
_templateMtimeCache.clear();
|
|
46
|
-
|
|
57
|
+
void agentSeedCache;
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
/**
|
|
@@ -67,9 +78,76 @@ export function getWorkspaceTemplateDir(config) {
|
|
|
67
78
|
return config?.channels?.wecom?.workspaceTemplate?.trim() || null;
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
function hasWorkspaceMemoryMarkers(workspaceDir) {
|
|
82
|
+
return existsSync(join(workspaceDir, "memory")) || existsSync(join(workspaceDir, "MEMORY.md"));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveTemplateStatePath(workspaceDir) {
|
|
86
|
+
return join(workspaceDir, TEMPLATE_STATE_DIRNAME, TEMPLATE_STATE_FILENAME);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readTemplateState(workspaceDir) {
|
|
90
|
+
const statePath = resolveTemplateStatePath(workspaceDir);
|
|
91
|
+
if (!existsSync(statePath)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const raw = readFileSync(statePath, "utf8");
|
|
97
|
+
const parsed = JSON.parse(raw);
|
|
98
|
+
if (!parsed || typeof parsed !== "object") {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
version: parsed.version,
|
|
104
|
+
seededAt: typeof parsed.seededAt === "string" ? parsed.seededAt : null,
|
|
105
|
+
templateDir: typeof parsed.templateDir === "string" ? parsed.templateDir : null,
|
|
106
|
+
seededFiles: Array.isArray(parsed.seededFiles)
|
|
107
|
+
? parsed.seededFiles.filter((file) => typeof file === "string")
|
|
108
|
+
: [],
|
|
109
|
+
templateMtimeMs:
|
|
110
|
+
typeof parsed.templateMtimeMs === "number" && Number.isFinite(parsed.templateMtimeMs)
|
|
111
|
+
? parsed.templateMtimeMs
|
|
112
|
+
: null,
|
|
113
|
+
migratedFromLegacy: parsed.migratedFromLegacy === true,
|
|
114
|
+
};
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function writeTemplateState(workspaceDir, state) {
|
|
121
|
+
const statePath = resolveTemplateStatePath(workspaceDir);
|
|
122
|
+
const stateDir = join(workspaceDir, TEMPLATE_STATE_DIRNAME);
|
|
123
|
+
mkdirSync(stateDir, { recursive: true });
|
|
124
|
+
|
|
125
|
+
const payload = `${JSON.stringify(
|
|
126
|
+
{
|
|
127
|
+
version: TEMPLATE_STATE_VERSION,
|
|
128
|
+
seededAt: state.seededAt,
|
|
129
|
+
templateDir: state.templateDir,
|
|
130
|
+
seededFiles: [...new Set(state.seededFiles)].sort(),
|
|
131
|
+
templateMtimeMs: state.templateMtimeMs,
|
|
132
|
+
migratedFromLegacy: state.migratedFromLegacy === true,
|
|
133
|
+
},
|
|
134
|
+
null,
|
|
135
|
+
2,
|
|
136
|
+
)}\n`;
|
|
137
|
+
const tmpPath = `${statePath}.tmp-${process.pid}-${Date.now().toString(36)}`;
|
|
138
|
+
writeFileSync(tmpPath, payload, "utf8");
|
|
139
|
+
renameSync(tmpPath, statePath);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function collectExistingSeededFiles(workspaceDir, templateFiles) {
|
|
143
|
+
return templateFiles.filter((file) => existsSync(join(workspaceDir, file)));
|
|
144
|
+
}
|
|
145
|
+
|
|
70
146
|
/**
|
|
71
|
-
* Copy template files into a
|
|
72
|
-
*
|
|
147
|
+
* Copy selected template files into a dynamic agent workspace.
|
|
148
|
+
* BOOTSTRAP.md may be synced only before user memory markers appear; once the
|
|
149
|
+
* workspace has memory/ or MEMORY.md, bootstrap is treated as completed and is
|
|
150
|
+
* never re-seeded by the plugin.
|
|
73
151
|
* Silently skips if workspaceTemplate is not configured or directory is missing.
|
|
74
152
|
*
|
|
75
153
|
* @param {string} agentId
|
|
@@ -89,33 +167,38 @@ export function seedAgentWorkspace(agentId, config, overrideTemplateDir) {
|
|
|
89
167
|
}
|
|
90
168
|
|
|
91
169
|
const workspaceDir = resolveAgentWorkspaceDirLocal(agentId);
|
|
170
|
+
const workspaceExistedBefore = existsSync(workspaceDir);
|
|
92
171
|
|
|
93
172
|
try {
|
|
94
173
|
const templateMaxMtimeMs = getTemplateMaxMtimeMs(templateDir);
|
|
95
|
-
|
|
96
|
-
const lastSyncedMtimeMs = _agentSeedMtimeCache.get(cacheKey) ?? 0;
|
|
97
|
-
const isFirstSeed = lastSyncedMtimeMs === 0;
|
|
174
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
98
175
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
176
|
+
const templateFiles = readdirSync(templateDir).filter((file) => BOOTSTRAP_FILENAMES.has(file));
|
|
177
|
+
let state = readTemplateState(workspaceDir);
|
|
178
|
+
const isLegacyWorkspace = !state && workspaceExistedBefore;
|
|
179
|
+
const isFirstSeed = !state && !workspaceExistedBefore;
|
|
102
180
|
|
|
103
|
-
|
|
181
|
+
if (!state) {
|
|
182
|
+
state = {
|
|
183
|
+
version: TEMPLATE_STATE_VERSION,
|
|
184
|
+
seededAt: new Date().toISOString(),
|
|
185
|
+
templateDir,
|
|
186
|
+
seededFiles: isLegacyWorkspace ? collectExistingSeededFiles(workspaceDir, templateFiles) : [],
|
|
187
|
+
templateMtimeMs: templateMaxMtimeMs,
|
|
188
|
+
migratedFromLegacy: isLegacyWorkspace,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
104
191
|
|
|
105
|
-
const
|
|
106
|
-
for (const file of
|
|
107
|
-
if (!
|
|
192
|
+
const bootstrapAllowed = !hasWorkspaceMemoryMarkers(workspaceDir);
|
|
193
|
+
for (const file of templateFiles) {
|
|
194
|
+
if (file === "BOOTSTRAP.md" && !bootstrapAllowed) {
|
|
108
195
|
continue;
|
|
109
196
|
}
|
|
110
197
|
const src = join(templateDir, file);
|
|
111
198
|
const dest = join(workspaceDir, file);
|
|
112
199
|
if (existsSync(dest)) {
|
|
113
200
|
if (!isFirstSeed) {
|
|
114
|
-
|
|
115
|
-
const destMtimeMs = statSync(dest).mtimeMs;
|
|
116
|
-
if (srcMtimeMs <= destMtimeMs) {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
201
|
+
continue;
|
|
119
202
|
}
|
|
120
203
|
copyFileSync(src, dest);
|
|
121
204
|
logger.info("WeCom: re-seeded workspace file", { agentId, file, isFirstSeed });
|
|
@@ -123,9 +206,12 @@ export function seedAgentWorkspace(agentId, config, overrideTemplateDir) {
|
|
|
123
206
|
copyFileSync(src, dest);
|
|
124
207
|
logger.info("WeCom: seeded workspace file", { agentId, file });
|
|
125
208
|
}
|
|
209
|
+
state.seededFiles.push(file);
|
|
126
210
|
}
|
|
127
211
|
|
|
128
|
-
|
|
212
|
+
state.templateDir = templateDir;
|
|
213
|
+
state.templateMtimeMs = templateMaxMtimeMs;
|
|
214
|
+
writeTemplateState(workspaceDir, state);
|
|
129
215
|
} catch (err) {
|
|
130
216
|
logger.warn("WeCom: failed to seed agent workspace", {
|
|
131
217
|
agentId,
|