simple-dynamsoft-mcp 7.2.0 → 7.2.2

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.
@@ -1,364 +0,0 @@
1
- #!/usr/bin/env node
2
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
5
-
6
- import {
7
- SHARED_STATE_SCHEMA_VERSION,
8
- computeRepoSignature,
9
- createSharedState,
10
- loadSharedState,
11
- normalizeRepoKey,
12
- normalizeRepoPath
13
- } from "../src/data/shared-state.js";
14
-
15
- const __dirname = dirname(fileURLToPath(import.meta.url));
16
- const projectRoot = join(__dirname, "..");
17
- const DEFAULT_EMBEDDING_MODEL = "models/gemini-embedding-001";
18
- const DEFAULT_INDEX_VERSION = "azure-shared-v1";
19
- const DEFAULT_INDEX_CONFIG = {
20
- chunkSize: 1200,
21
- chunkOverlap: 200,
22
- maxChunksPerDoc: 6,
23
- maxTextChars: 4000
24
- };
25
-
26
- function parseIntegerOption(flag, rawValue, { min = 0 } = {}) {
27
- const text = String(rawValue ?? "").trim();
28
- if (!/^-?\d+$/.test(text)) {
29
- throw new Error(`Invalid ${flag}: expected an integer, received '${rawValue}'`);
30
- }
31
-
32
- const value = Number.parseInt(text, 10);
33
- if (!Number.isSafeInteger(value) || value < min) {
34
- throw new Error(`Invalid ${flag}: expected integer >= ${min}, received '${rawValue}'`);
35
- }
36
-
37
- return value;
38
- }
39
-
40
- function toAbsolutePath(pathValue) {
41
- if (!pathValue) return "";
42
- if (pathValue.startsWith("/")) return pathValue;
43
- return join(projectRoot, pathValue);
44
- }
45
-
46
- function parseArgs(argv, env = process.env) {
47
- const currentStatePath = env.DATA_SYNC_AZURE_CURRENT_STATE_PATH || ".tmp/azure-shared-state/state/current.json";
48
- const defaultIndexConfig = {
49
- chunkSize: env.DATA_SYNC_AZURE_CHUNK_SIZE
50
- ? parseIntegerOption("--chunk-size", env.DATA_SYNC_AZURE_CHUNK_SIZE, { min: 0 })
51
- : DEFAULT_INDEX_CONFIG.chunkSize,
52
- chunkOverlap: env.DATA_SYNC_AZURE_CHUNK_OVERLAP
53
- ? parseIntegerOption("--chunk-overlap", env.DATA_SYNC_AZURE_CHUNK_OVERLAP, { min: 0 })
54
- : DEFAULT_INDEX_CONFIG.chunkOverlap,
55
- maxChunksPerDoc: env.DATA_SYNC_AZURE_MAX_CHUNKS_PER_DOC
56
- ? parseIntegerOption("--max-chunks-per-doc", env.DATA_SYNC_AZURE_MAX_CHUNKS_PER_DOC, { min: 1 })
57
- : DEFAULT_INDEX_CONFIG.maxChunksPerDoc,
58
- maxTextChars: env.DATA_SYNC_AZURE_MAX_TEXT_CHARS
59
- ? parseIntegerOption("--max-text-chars", env.DATA_SYNC_AZURE_MAX_TEXT_CHARS, { min: 0 })
60
- : DEFAULT_INDEX_CONFIG.maxTextChars
61
- };
62
- const defaults = {
63
- manifestPath: env.DATA_SYNC_AZURE_MANIFEST_PATH || "data/metadata/data-manifest.json",
64
- currentStatePath,
65
- nextStatePath: env.DATA_SYNC_AZURE_NEXT_STATE_PATH || ".tmp/azure-shared-state/state/next-state.json",
66
- planOutputPath: env.DATA_SYNC_AZURE_PLAN_OUTPUT_PATH || ".tmp/azure-shared-state/state/plan.json",
67
- embeddingModel: env.DATA_SYNC_AZURE_EMBEDDING_MODEL || DEFAULT_EMBEDDING_MODEL,
68
- indexVersion: env.DATA_SYNC_AZURE_INDEX_VERSION || DEFAULT_INDEX_VERSION,
69
- generatedAt: env.DATA_SYNC_AZURE_GENERATED_AT || new Date().toISOString(),
70
- schemaVersion: SHARED_STATE_SCHEMA_VERSION,
71
- indexConfig: defaultIndexConfig
72
- };
73
-
74
- for (let i = 0; i < argv.length; i++) {
75
- const arg = argv[i];
76
- const value = argv[i + 1];
77
-
78
- if (arg === "--manifest" && value) {
79
- defaults.manifestPath = value;
80
- i++;
81
- continue;
82
- }
83
- if (arg === "--current-state" && value) {
84
- defaults.currentStatePath = value;
85
- i++;
86
- continue;
87
- }
88
- if (arg === "--next-state" && value) {
89
- defaults.nextStatePath = value;
90
- i++;
91
- continue;
92
- }
93
- if (arg === "--plan-output" && value) {
94
- defaults.planOutputPath = value;
95
- i++;
96
- continue;
97
- }
98
- if (arg === "--embedding-model" && value) {
99
- defaults.embeddingModel = value;
100
- i++;
101
- continue;
102
- }
103
- if (arg === "--index-version" && value) {
104
- defaults.indexVersion = value;
105
- i++;
106
- continue;
107
- }
108
- if (arg === "--generated-at" && value) {
109
- defaults.generatedAt = value;
110
- i++;
111
- continue;
112
- }
113
- if (arg === "--schema-version" && value) {
114
- defaults.schemaVersion = parseIntegerOption("--schema-version", value, { min: 1 });
115
- i++;
116
- continue;
117
- }
118
- if (arg === "--chunk-size" && value) {
119
- defaults.indexConfig.chunkSize = parseIntegerOption("--chunk-size", value, { min: 0 });
120
- i++;
121
- continue;
122
- }
123
- if (arg === "--chunk-overlap" && value) {
124
- defaults.indexConfig.chunkOverlap = parseIntegerOption("--chunk-overlap", value, { min: 0 });
125
- i++;
126
- continue;
127
- }
128
- if (arg === "--max-chunks-per-doc" && value) {
129
- defaults.indexConfig.maxChunksPerDoc = parseIntegerOption("--max-chunks-per-doc", value, { min: 1 });
130
- i++;
131
- continue;
132
- }
133
- if (arg === "--max-text-chars" && value) {
134
- defaults.indexConfig.maxTextChars = parseIntegerOption("--max-text-chars", value, { min: 0 });
135
- i++;
136
- }
137
- }
138
-
139
- return {
140
- ...defaults,
141
- manifestPath: toAbsolutePath(defaults.manifestPath),
142
- currentStatePath: toAbsolutePath(defaults.currentStatePath),
143
- nextStatePath: toAbsolutePath(defaults.nextStatePath),
144
- planOutputPath: toAbsolutePath(defaults.planOutputPath)
145
- };
146
- }
147
-
148
- function readManifest(manifestPath) {
149
- if (!existsSync(manifestPath)) {
150
- throw new Error(`Manifest not found: ${manifestPath}`);
151
- }
152
-
153
- const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
154
- if (!Array.isArray(manifest?.repos)) {
155
- throw new Error("Manifest must include a repos array");
156
- }
157
-
158
- return manifest;
159
- }
160
-
161
- function buildShardPath(repoPath, signature) {
162
- return `rag/cache/gemini-${signature}.json`;
163
- }
164
-
165
- function buildDesiredRepos(manifestRepos, options) {
166
- const repos = {};
167
- const sourcePathByKey = new Map();
168
-
169
- for (const repo of manifestRepos) {
170
- const path = normalizeRepoPath(repo.path);
171
- const key = normalizeRepoKey(path);
172
- if (!key) continue;
173
-
174
- const existingPath = sourcePathByKey.get(key);
175
- if (existingPath && existingPath !== path) {
176
- throw new Error(
177
- `Repo key collision for '${key}': '${existingPath}' and '${path}' normalize to the same key`
178
- );
179
- }
180
-
181
- const signature = computeRepoSignature({
182
- repo,
183
- embeddingModel: options.embeddingModel,
184
- indexConfig: options.indexConfig,
185
- indexVersion: options.indexVersion,
186
- schemaVersion: options.schemaVersion
187
- });
188
-
189
- repos[key] = {
190
- path,
191
- commit: String(repo.commit || "").trim(),
192
- signature,
193
- shardPath: buildShardPath(path, signature)
194
- };
195
- sourcePathByKey.set(key, path);
196
- }
197
-
198
- return repos;
199
- }
200
-
201
- function loadCurrentState(statePath, { generatedAt, indexVersion } = {}) {
202
- if (!existsSync(statePath)) {
203
- return createSharedState({
204
- generatedAt,
205
- indexVersion,
206
- repos: {}
207
- });
208
- }
209
-
210
- return loadSharedState(readFileSync(statePath, "utf8"));
211
- }
212
-
213
- function sortStrings(values) {
214
- return [...values].sort((a, b) => a.localeCompare(b));
215
- }
216
-
217
- function computeRepoDiff({ currentRepos, desiredRepos }) {
218
- const changed = [];
219
- const added = [];
220
- const unchanged = [];
221
- const removed = [];
222
-
223
- const currentKeys = new Set(Object.keys(currentRepos || {}));
224
- const desiredKeys = new Set(Object.keys(desiredRepos || {}));
225
-
226
- for (const key of desiredKeys) {
227
- if (!currentKeys.has(key)) {
228
- added.push(key);
229
- continue;
230
- }
231
-
232
- const currentSignature = String(currentRepos[key]?.signature || "").trim();
233
- const desiredSignature = String(desiredRepos[key]?.signature || "").trim();
234
- if (currentSignature === desiredSignature) {
235
- unchanged.push(key);
236
- } else {
237
- changed.push(key);
238
- }
239
- }
240
-
241
- for (const key of currentKeys) {
242
- if (!desiredKeys.has(key)) {
243
- removed.push(key);
244
- }
245
- }
246
-
247
- return {
248
- changed: sortStrings(changed),
249
- added: sortStrings(added),
250
- unchanged: sortStrings(unchanged),
251
- removed: sortStrings(removed),
252
- hasChanges: changed.length + added.length + removed.length > 0
253
- };
254
- }
255
-
256
- function ensureParentDir(filePath) {
257
- mkdirSync(dirname(filePath), { recursive: true });
258
- }
259
-
260
- function writeJsonFile(filePath, payload) {
261
- ensureParentDir(filePath);
262
- writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`);
263
- }
264
-
265
- function simulateAtomicPromotion({ currentStatePath, nextState }) {
266
- const promotionPath = `${currentStatePath}.next.json`;
267
- writeJsonFile(promotionPath, nextState);
268
- renameSync(promotionPath, currentStatePath);
269
- return {
270
- promotionPath,
271
- promotedPath: currentStatePath
272
- };
273
- }
274
-
275
- function buildPlan({ options, currentState, nextState, diff }) {
276
- return {
277
- kind: "azure_shared_state_sync_plan",
278
- generatedAt: options.generatedAt,
279
- manifestPath: options.manifestPath,
280
- currentStatePath: options.currentStatePath,
281
- nextStatePath: options.nextStatePath,
282
- embeddingModel: options.embeddingModel,
283
- indexVersion: options.indexVersion,
284
- schemaVersion: options.schemaVersion,
285
- summary: {
286
- currentRepos: Object.keys(currentState.repos).length,
287
- desiredRepos: Object.keys(nextState.repos).length,
288
- changed: diff.changed.length,
289
- added: diff.added.length,
290
- unchanged: diff.unchanged.length,
291
- removed: diff.removed.length,
292
- hasChanges: diff.hasChanges
293
- },
294
- repos: diff
295
- };
296
- }
297
-
298
- function logSummary(plan) {
299
- const summary = plan.summary;
300
- console.log(`[data-sync-azure] manifest=${plan.manifestPath}`);
301
- console.log(`[data-sync-azure] current_state=${plan.currentStatePath}`);
302
- console.log(`[data-sync-azure] next_state=${plan.nextStatePath}`);
303
- console.log(
304
- `[data-sync-azure] repos current=${summary.currentRepos} desired=${summary.desiredRepos} ` +
305
- `changed=${summary.changed} added=${summary.added} unchanged=${summary.unchanged} removed=${summary.removed}`
306
- );
307
- console.log(`[data-sync-azure] has_changes=${summary.hasChanges}`);
308
- }
309
-
310
- function runDataSyncAzure(argv = process.argv.slice(2), env = process.env) {
311
- const options = parseArgs(argv, env);
312
- const manifest = readManifest(options.manifestPath);
313
- const currentState = loadCurrentState(options.currentStatePath, {
314
- generatedAt: options.generatedAt,
315
- indexVersion: options.indexVersion
316
- });
317
- const desiredRepos = buildDesiredRepos(manifest.repos, options);
318
- const nextState = createSharedState({
319
- generatedAt: options.generatedAt,
320
- indexVersion: options.indexVersion,
321
- schemaVersion: options.schemaVersion,
322
- repos: desiredRepos
323
- });
324
-
325
- const diff = computeRepoDiff({
326
- currentRepos: currentState.repos,
327
- desiredRepos: nextState.repos
328
- });
329
-
330
- const plan = buildPlan({
331
- options,
332
- currentState,
333
- nextState,
334
- diff
335
- });
336
-
337
- writeJsonFile(options.planOutputPath, plan);
338
- writeJsonFile(options.nextStatePath, nextState);
339
- const promotion = simulateAtomicPromotion({
340
- currentStatePath: options.currentStatePath,
341
- nextState
342
- });
343
- logSummary(plan);
344
- console.log(`[data-sync-azure] plan_json=${options.planOutputPath}`);
345
- console.log(`[data-sync-azure] promoted_state=${promotion.promotedPath}`);
346
-
347
- return {
348
- options,
349
- plan,
350
- nextState,
351
- promotion
352
- };
353
- }
354
-
355
- if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
356
- runDataSyncAzure();
357
- }
358
-
359
- export {
360
- buildDesiredRepos,
361
- computeRepoDiff,
362
- parseArgs,
363
- runDataSyncAzure
364
- };
@@ -1,214 +0,0 @@
1
- import { createHash } from "node:crypto";
2
-
3
- const SHARED_STATE_SCHEMA_VERSION = 1;
4
-
5
- function normalizeRepoPath(input) {
6
- if (input === undefined || input === null) return "";
7
- const normalized = String(input)
8
- .trim()
9
- .replace(/\\/g, "/")
10
- .replace(/^\.\//, "")
11
- .replace(/^\/+/, "")
12
- .replace(/\/+/g, "/")
13
- .replace(/\/+$/, "");
14
- return normalized;
15
- }
16
-
17
- function normalizeRepoKey(input) {
18
- const path = normalizeRepoPath(input).toLowerCase();
19
- if (!path) return "";
20
- return path
21
- .replace(/[^a-z0-9]+/g, "_")
22
- .replace(/^_+/, "")
23
- .replace(/_+$/, "");
24
- }
25
-
26
- function stableValue(value) {
27
- if (Array.isArray(value)) {
28
- return value.map((item) => stableValue(item));
29
- }
30
- if (value && typeof value === "object") {
31
- const out = {};
32
- const keys = Object.keys(value).sort((a, b) => a.localeCompare(b));
33
- for (const key of keys) {
34
- out[key] = stableValue(value[key]);
35
- }
36
- return out;
37
- }
38
- return value;
39
- }
40
-
41
- function stableStringify(value) {
42
- return JSON.stringify(stableValue(value));
43
- }
44
-
45
- function normalizeRepoEntry(key, entry) {
46
- const source = entry && typeof entry === "object" ? entry : {};
47
- const normalizedPath = normalizeRepoPath(source.path || key);
48
-
49
- return {
50
- path: normalizedPath,
51
- commit: String(source.commit || "").trim(),
52
- signature: String(source.signature || "").trim(),
53
- shardPath: normalizeShardPath(source.shardPath),
54
- updatedAt: source.updatedAt ? String(source.updatedAt).trim() : undefined
55
- };
56
- }
57
-
58
- function normalizeShardPath(input) {
59
- if (input === undefined || input === null) return "";
60
- const raw = String(input).trim();
61
- if (!raw) return "";
62
-
63
- const slashNormalized = raw
64
- .replace(/\\/g, "/")
65
- .replace(/\/+/g, "/")
66
- .replace(/\/+$/, "");
67
-
68
- if (!slashNormalized) return "";
69
- if (slashNormalized.startsWith("/") || /^[A-Za-z]:\//.test(slashNormalized)) {
70
- return slashNormalized;
71
- }
72
- return normalizeRepoPath(slashNormalized);
73
- }
74
-
75
- function normalizeReposMap(repos) {
76
- const input = repos && typeof repos === "object" ? repos : {};
77
- const normalized = {};
78
- const sourceByKey = new Map();
79
-
80
- for (const [repoKey, entry] of Object.entries(input)) {
81
- const normalizedEntry = normalizeRepoEntry(repoKey, entry);
82
- const key = normalizeRepoKey(normalizedEntry.path || repoKey);
83
- if (!key) continue;
84
-
85
- const existingEntry = normalized[key];
86
- if (existingEntry && existingEntry.path !== normalizedEntry.path) {
87
- const existingSource = sourceByKey.get(key) || existingEntry.path;
88
- throw new Error(
89
- `Repo key collision for '${key}': '${existingSource}' and '${repoKey}' normalize to different paths ('${existingEntry.path}' vs '${normalizedEntry.path}')`
90
- );
91
- }
92
-
93
- sourceByKey.set(key, repoKey);
94
- normalized[key] = normalizedEntry;
95
- }
96
-
97
- return Object.fromEntries(Object.entries(normalized).sort(([a], [b]) => a.localeCompare(b)));
98
- }
99
-
100
- function computeRepoSignature({
101
- repo,
102
- embeddingModel,
103
- indexConfig,
104
- indexVersion,
105
- schemaVersion = SHARED_STATE_SCHEMA_VERSION
106
- }) {
107
- const normalizedRepoPath = normalizeRepoPath(repo?.path);
108
- const commit = String(repo?.commit || "").trim();
109
- const model = String(embeddingModel || "").trim();
110
- const marker = String(indexVersion || "").trim();
111
-
112
- if (!normalizedRepoPath) throw new Error("repo.path is required to compute repo signature");
113
- if (!commit) throw new Error("repo.commit is required to compute repo signature");
114
- if (!model) throw new Error("embeddingModel is required to compute repo signature");
115
- if (!marker) throw new Error("indexVersion is required to compute repo signature");
116
-
117
- const payload = {
118
- schemaVersion,
119
- indexVersion: marker,
120
- embeddingModel: model,
121
- repo: {
122
- path: normalizedRepoPath,
123
- commit
124
- },
125
- indexConfig: stableValue(indexConfig && typeof indexConfig === "object" ? indexConfig : {})
126
- };
127
-
128
- return createHash("sha256").update(stableStringify(payload)).digest("hex");
129
- }
130
-
131
- function createSharedState({
132
- generatedAt = new Date().toISOString(),
133
- indexVersion,
134
- repos = {},
135
- schemaVersion = SHARED_STATE_SCHEMA_VERSION
136
- } = {}) {
137
- return {
138
- schemaVersion,
139
- generatedAt: String(generatedAt || "").trim(),
140
- indexVersion: String(indexVersion || "").trim(),
141
- repos: normalizeReposMap(repos)
142
- };
143
- }
144
-
145
- function validateSharedState(state) {
146
- const errors = [];
147
-
148
- if (!state || typeof state !== "object" || Array.isArray(state)) {
149
- return { ok: false, errors: ["state must be an object"] };
150
- }
151
-
152
- if (state.schemaVersion !== SHARED_STATE_SCHEMA_VERSION) {
153
- errors.push(`schemaVersion must be ${SHARED_STATE_SCHEMA_VERSION}`);
154
- }
155
- if (!String(state.generatedAt || "").trim()) {
156
- errors.push("generatedAt is required");
157
- }
158
- if (!String(state.indexVersion || "").trim()) {
159
- errors.push("indexVersion is required");
160
- }
161
- if (!state.repos || typeof state.repos !== "object" || Array.isArray(state.repos)) {
162
- errors.push("repos must be an object map");
163
- }
164
-
165
- if (state.repos && typeof state.repos === "object" && !Array.isArray(state.repos)) {
166
- for (const [key, repo] of Object.entries(state.repos)) {
167
- if (!String(key || "").trim()) {
168
- errors.push("repo key must be a non-empty string");
169
- }
170
- if (!repo || typeof repo !== "object" || Array.isArray(repo)) {
171
- errors.push(`repo entry '${key}' must be an object`);
172
- continue;
173
- }
174
-
175
- const path = normalizeRepoPath(repo.path);
176
- if (!path) errors.push(`repo entry '${key}' is missing path`);
177
- if (!String(repo.commit || "").trim()) errors.push(`repo entry '${key}' is missing commit`);
178
- if (!String(repo.signature || "").trim()) errors.push(`repo entry '${key}' is missing signature`);
179
- if (!normalizeRepoPath(repo.shardPath)) errors.push(`repo entry '${key}' is missing shardPath`);
180
- }
181
- }
182
-
183
- return {
184
- ok: errors.length === 0,
185
- errors
186
- };
187
- }
188
-
189
- function loadSharedState(input) {
190
- const parsed = typeof input === "string" ? JSON.parse(input) : input;
191
- const state = {
192
- schemaVersion: parsed?.schemaVersion,
193
- generatedAt: parsed?.generatedAt,
194
- indexVersion: parsed?.indexVersion,
195
- repos: normalizeReposMap(parsed?.repos)
196
- };
197
-
198
- const validation = validateSharedState(state);
199
- if (!validation.ok) {
200
- throw new Error(`Invalid shared state: ${validation.errors.join("; ")}`);
201
- }
202
-
203
- return state;
204
- }
205
-
206
- export {
207
- SHARED_STATE_SCHEMA_VERSION,
208
- normalizeRepoPath,
209
- normalizeRepoKey,
210
- computeRepoSignature,
211
- createSharedState,
212
- validateSharedState,
213
- loadSharedState
214
- };