@nowcrew/daemon 0.6.97-beta.2 → 0.6.97-beta.3
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/dist/cloud-report-upload.js +10 -1
- package/dist/execution-journal.js +1 -3
- package/dist/execution-protocol.js +1 -1
- package/dist/execution-runner.js +1 -4
- package/dist/local-execution-prompt.js +3 -8
- package/dist/local-executor.js +39 -63
- package/dist/local-memory/index-budget.js +3 -9
- package/dist/local-memory/inventory-cli.js +8 -49
- package/dist/local-memory/inventory-source.js +8 -15
- package/dist/local-memory/pending-query.js +2 -5
- package/dist/local-memory/pending-schema.js +1 -1
- package/dist/local-memory/pending.js +2 -16
- package/dist/local-memory/prompt.js +5 -21
- package/dist/local-memory/read-cli.js +76 -98
- package/dist/local-memory/rollout.js +11 -79
- package/dist/local-memory/runtime.js +13 -181
- package/dist/local-memory/write.js +21 -189
- package/dist/local-memory-diagnostics.js +13 -94
- package/dist/local-memory-telemetry.js +2 -19
- package/dist/machine-info.js +0 -3
- package/dist/marketplace-protocol.js +1 -1
- package/dist/marketplace-remote.js +1 -1
- package/dist/marketplace-runner.js +1 -1
- package/dist/memory-prune-diagnostics.js +4 -60
- package/dist/message-file-preview.js +1 -11
- package/dist/prompt.js +2 -6
- package/dist/runner.js +1 -1
- package/dist/runtimes/claude-thread-runner.js +4 -25
- package/dist/serve.js +3 -38
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +3 -16
- package/dist/workspace.js +2 -13
- package/package.json +1 -1
- package/dist/local-memory/commit-telemetry.js +0 -30
- package/dist/local-memory/execution-workspace.js +0 -62
- package/dist/local-memory/full-index-search.js +0 -47
- package/dist/local-memory/initialization-identity.js +0 -36
- package/dist/local-memory/inventory-v2.js +0 -361
- package/dist/local-memory/legacy-adapter.js +0 -233
- package/dist/local-memory/migration-access.js +0 -242
- package/dist/local-memory/migration-cli.js +0 -54
- package/dist/local-memory/migration-io.js +0 -199
- package/dist/local-memory/migration-pending.js +0 -9
- package/dist/local-memory/migration-plan.js +0 -317
- package/dist/local-memory/migration-schema.js +0 -55
- package/dist/local-memory/migration-state-invariants.js +0 -65
- package/dist/local-memory/migration-store.js +0 -414
- package/dist/local-memory/migration-tool-entry.js +0 -21
- package/dist/local-memory/storage-format.js +0 -152
- package/dist/local-memory/writer-mode.js +0 -13
- package/dist/memory-migration-controller.js +0 -86
- package/dist/memory-migration-inspect.js +0 -108
- package/dist/memory-migration-journal.js +0 -14
- package/dist/memory-migration-protocol.js +0 -14
- package/dist/memory-migration-runtime.js +0 -305
|
@@ -6,8 +6,6 @@ import { dslog } from "../slog.js";
|
|
|
6
6
|
import { LocalMemoryChangeSchema, LocalMemorySingleLineText, validateLocalMemoryChange, } from "./candidate.js";
|
|
7
7
|
import { LOCAL_MEMORY_INDEX_CODE_POINT_CAP, LOCAL_MEMORY_INDEX_ENTRY_CAP, LOCAL_MEMORY_TOTAL_CODE_POINT_CAP, renderGeneratedIndex, selectLocalMemoryIndexEntries, } from "./index-budget.js";
|
|
8
8
|
import { persistPendingMemoryCandidate, readPendingMemoryCandidates, removePendingMemoryCandidate, } from "./pending.js";
|
|
9
|
-
import { assertLocalMemoryStorageSize, detectLocalMemoryStorageVersion, LOCAL_MEMORY_GENERATED_INDEX_PATTERN, LOCAL_MEMORY_NOTE_BYTE_CAP, LOCAL_MEMORY_READ_BYTE_CAP, parseFullMemoryIndex, replaceFullMemoryIndex, selectLocalMemoryPreview, } from "./storage-format.js";
|
|
10
|
-
import { assertMemoryInventoryHealth, assertMemoryWriterAllowed, withMemoryAccess } from "./migration-access.js";
|
|
11
9
|
const StatusChangeSchema = z.object({
|
|
12
10
|
memoryId: LocalMemorySingleLineText,
|
|
13
11
|
expectedRevision: z.number().int().positive(),
|
|
@@ -92,14 +90,12 @@ function parseCards(section) {
|
|
|
92
90
|
});
|
|
93
91
|
}
|
|
94
92
|
export function parseLocalMemoryIndex(memory) {
|
|
95
|
-
if (detectLocalMemoryStorageVersion(memory) === 2)
|
|
96
|
-
return parseFullMemoryIndex(memory);
|
|
97
93
|
const section = indexSection(memory);
|
|
98
94
|
const generated = GENERATED_INDEX_PATTERN.exec(section)?.[0];
|
|
99
95
|
return parseCards(generated ?? section);
|
|
100
96
|
}
|
|
101
97
|
function parseUnmarkedMemoryIndex(memory) {
|
|
102
|
-
const section = indexSection(memory).replace(
|
|
98
|
+
const section = indexSection(memory).replace(GENERATED_INDEX_PATTERN, "");
|
|
103
99
|
return parseCards(section);
|
|
104
100
|
}
|
|
105
101
|
function replaceGeneratedIndex(memory, entries) {
|
|
@@ -126,36 +122,11 @@ function noteSection(id, revision, title, body) {
|
|
|
126
122
|
}
|
|
127
123
|
function notePattern(id) {
|
|
128
124
|
const escaped = id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
129
|
-
return new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}
|
|
130
|
-
}
|
|
131
|
-
function uniqueNoteSection(content, id) {
|
|
132
|
-
const escaped = id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
133
|
-
const begins = [...content.matchAll(new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}(?=\\s|-->)`, "gu"))];
|
|
134
|
-
const ends = [...content.matchAll(new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->`, "gu"))];
|
|
135
|
-
if (begins.length === 0 && ends.length === 0)
|
|
136
|
-
return null;
|
|
137
|
-
const section = notePattern(id).exec(content)?.[0];
|
|
138
|
-
if (begins.length !== 1 || ends.length !== 1 || section === undefined
|
|
139
|
-
|| (section.match(/NOWWORK:MEMORY_ENTRY:(?:BEGIN|END)/gu) ?? []).length !== 2) {
|
|
140
|
-
throw new Error("local_memory_note_anchor_invalid");
|
|
141
|
-
}
|
|
142
|
-
return section;
|
|
125
|
+
return new RegExp(`<!--\\s*NOWWORK:MEMORY_ENTRY:BEGIN\\s+id=${escaped}[^>]*-->[\\s\\S]*?<!--\\s*NOWWORK:MEMORY_ENTRY:END\\s+id=${escaped}\\s*-->\\s*`, "u");
|
|
143
126
|
}
|
|
144
127
|
function replaceNote(content, id, replacement) {
|
|
145
128
|
const pattern = notePattern(id);
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
function updateNoteRevision(content, id, revision) {
|
|
149
|
-
const section = uniqueNoteSection(content, id);
|
|
150
|
-
const beginning = `<!-- NOWWORK:MEMORY_ENTRY:BEGIN id=${id} `;
|
|
151
|
-
const ending = `<!-- NOWWORK:MEMORY_ENTRY:END id=${id} -->`;
|
|
152
|
-
if (section === null || content.split(beginning).length !== 2 || content.split(ending).length !== 2) {
|
|
153
|
-
throw new Error("local_memory_note_anchor_missing");
|
|
154
|
-
}
|
|
155
|
-
const updated = section.replace(/^(<!--\s*NOWWORK:MEMORY_ENTRY:BEGIN\s+id=[A-Za-z0-9_-]+\s+revision=)[1-9][0-9]*(\s*-->)/u, (_match, prefix, suffix) => `${prefix}${revision}${suffix}`);
|
|
156
|
-
if (!updated.startsWith(`${beginning}revision=${revision} -->`))
|
|
157
|
-
throw new Error("local_memory_note_anchor_missing");
|
|
158
|
-
return content.replace(notePattern(id), () => updated);
|
|
129
|
+
return pattern.test(content) ? content.replace(pattern, replacement) : null;
|
|
159
130
|
}
|
|
160
131
|
function validateCandidate(result) {
|
|
161
132
|
if (result.outcome === "no_change" && (result.changes.length > 0 || result.statusChanges.length > 0)) {
|
|
@@ -209,25 +180,6 @@ function stableId(executionId, index, change) {
|
|
|
209
180
|
const input = [executionId, String(index), change.operation, change.title, change.summary, change.detailBody].join("\0");
|
|
210
181
|
return `mem_${createHash("sha256").update(input, "utf8").digest("hex").slice(0, 12)}`;
|
|
211
182
|
}
|
|
212
|
-
function migrationPendingId(change) {
|
|
213
|
-
// Keep the digest independent of object insertion order for callers that
|
|
214
|
-
// reach applyLocalMemoryResult directly instead of the JSON/Zod boundary.
|
|
215
|
-
const canonicalChange = {
|
|
216
|
-
operation: change.operation,
|
|
217
|
-
memoryId: change.memoryId,
|
|
218
|
-
baseRevision: change.baseRevision,
|
|
219
|
-
supersedesMemoryId: change.supersedesMemoryId,
|
|
220
|
-
title: change.title,
|
|
221
|
-
kind: change.kind,
|
|
222
|
-
summary: change.summary,
|
|
223
|
-
triggers: change.triggers,
|
|
224
|
-
priority: change.priority,
|
|
225
|
-
detailCategory: change.detailCategory,
|
|
226
|
-
detailBody: change.detailBody,
|
|
227
|
-
reviewAfter: change.reviewAfter,
|
|
228
|
-
};
|
|
229
|
-
return `pending-migration-${createHash("sha256").update(JSON.stringify(canonicalChange), "utf8").digest("hex").slice(0, 12)}`;
|
|
230
|
-
}
|
|
231
183
|
function normalize(value) {
|
|
232
184
|
return value.normalize("NFKC").toLocaleLowerCase().replace(/\s+/gu, " ").trim();
|
|
233
185
|
}
|
|
@@ -247,14 +199,14 @@ function detailCategoryFromFile(file) {
|
|
|
247
199
|
return DETAIL_CATEGORIES.has(category) ? category : null;
|
|
248
200
|
}
|
|
249
201
|
function readNoteBody(content, id) {
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
202
|
+
const begin = content.indexOf(`<!-- NOWWORK:MEMORY_ENTRY:BEGIN id=${id}`);
|
|
203
|
+
if (begin < 0)
|
|
252
204
|
return null;
|
|
253
|
-
const bodyStart =
|
|
254
|
-
const bodyEnd =
|
|
205
|
+
const bodyStart = content.indexOf("\n\n", begin);
|
|
206
|
+
const bodyEnd = content.indexOf(`\n<!-- NOWWORK:MEMORY_ENTRY:END id=${id}`, bodyStart + 2);
|
|
255
207
|
if (bodyStart < 0 || bodyEnd < 0)
|
|
256
208
|
return null;
|
|
257
|
-
return
|
|
209
|
+
return content.slice(bodyStart + 2, bodyEnd).trim();
|
|
258
210
|
}
|
|
259
211
|
function archivedChange(entry, detailBody) {
|
|
260
212
|
const detailCategory = detailCategoryFromFile(entry.file);
|
|
@@ -278,34 +230,6 @@ function archivedChange(entry, detailBody) {
|
|
|
278
230
|
function emptyResult() {
|
|
279
231
|
return { createdIds: [], updatedIds: [], statusChangedIds: [], deferredIds: [], pendingIds: [] };
|
|
280
232
|
}
|
|
281
|
-
async function stageMigrationDeferredPending(input) {
|
|
282
|
-
if (input.result.outcome !== "proposed")
|
|
283
|
-
return emptyResult();
|
|
284
|
-
if (input.result.statusChanges.length > 0 || input.result.changes.some((change) => change.operation !== "create")) {
|
|
285
|
-
throw new Error("local_memory_migration_pending_create_only");
|
|
286
|
-
}
|
|
287
|
-
const executionId = input.executionId ?? "unknown";
|
|
288
|
-
const pending = input.result.changes.map((change) => ({
|
|
289
|
-
schemaVersion: 1,
|
|
290
|
-
pendingId: migrationPendingId(change),
|
|
291
|
-
createdAt: new Date().toISOString(),
|
|
292
|
-
reason: "legacy_requires_migration",
|
|
293
|
-
source: { executionId, pruneTraceId: input.pruneTraceId ?? null },
|
|
294
|
-
candidate: change,
|
|
295
|
-
}));
|
|
296
|
-
await assertMemoryAccessDirectory(input.agentDir);
|
|
297
|
-
await withMemoryAccess(input.agentDir, async () => {
|
|
298
|
-
const memory = await readFile(join(input.agentDir, "MEMORY.md"), "utf8").catch(() => null);
|
|
299
|
-
if (memory === null || detectLocalMemoryStorageVersion(memory) !== 1) {
|
|
300
|
-
throw new Error("local_memory_migration_pending_storage_invalid");
|
|
301
|
-
}
|
|
302
|
-
for (const candidate of pending) {
|
|
303
|
-
await persistPendingMemoryCandidate({ agentDir: input.agentDir, candidate });
|
|
304
|
-
}
|
|
305
|
-
}, { pendingOnly: true });
|
|
306
|
-
const pendingIds = [...new Set(pending.map((candidate) => candidate.pendingId))];
|
|
307
|
-
return { createdIds: [], updatedIds: [], statusChangedIds: [], deferredIds: pendingIds, pendingIds };
|
|
308
|
-
}
|
|
309
233
|
function isWithin(root, candidate) {
|
|
310
234
|
const path = relative(root, candidate);
|
|
311
235
|
return path === "" || (path !== ".." && !path.startsWith(`..${sep}`));
|
|
@@ -320,29 +244,12 @@ async function assertWritablePaths(agentDir, memoryPath) {
|
|
|
320
244
|
if (!memoryStat.isFile() || memoryStat.isSymbolicLink())
|
|
321
245
|
throw new Error("local_memory_memory_path_invalid");
|
|
322
246
|
}
|
|
323
|
-
async function assertMemoryAccessDirectory(agentDir) {
|
|
324
|
-
try {
|
|
325
|
-
const stat = await lstat(join(agentDir, ".crew/memory"));
|
|
326
|
-
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
327
|
-
throw new Error("local_memory_pending_path_invalid");
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
if (error.code === "ENOENT")
|
|
331
|
-
return;
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
247
|
async function readNote(path) {
|
|
336
248
|
try {
|
|
337
249
|
const stat = await lstat(path);
|
|
338
250
|
if (!stat.isFile() || stat.isSymbolicLink())
|
|
339
251
|
throw new Error("local_memory_note_path_invalid");
|
|
340
|
-
|
|
341
|
-
throw new Error("local_memory_note_byte_limit");
|
|
342
|
-
const content = await readFile(path, "utf8");
|
|
343
|
-
if (Buffer.byteLength(content, "utf8") > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
344
|
-
throw new Error("local_memory_note_byte_limit");
|
|
345
|
-
return content;
|
|
252
|
+
return await readFile(path, "utf8");
|
|
346
253
|
}
|
|
347
254
|
catch (error) {
|
|
348
255
|
if (error.code === "ENOENT")
|
|
@@ -362,11 +269,6 @@ async function atomicWrite(path, content) {
|
|
|
362
269
|
}
|
|
363
270
|
}
|
|
364
271
|
function projectIndex(memory, entries, protectedIds) {
|
|
365
|
-
if (detectLocalMemoryStorageVersion(memory) === 2) {
|
|
366
|
-
const updated = replaceFullMemoryIndex(memory, entries);
|
|
367
|
-
selectLocalMemoryPreview(updated);
|
|
368
|
-
return { memory: updated, omittedIds: [], reason: null };
|
|
369
|
-
}
|
|
370
272
|
const empty = replaceGeneratedIndex(memory, []);
|
|
371
273
|
if (codePointLength(empty) > LOCAL_MEMORY_TOTAL_CODE_POINT_CAP)
|
|
372
274
|
throw new Error("local_memory_memory_over_budget");
|
|
@@ -404,29 +306,12 @@ async function withApplyLease(agentDir, operation) {
|
|
|
404
306
|
}
|
|
405
307
|
export async function applyLocalMemoryResult(input) {
|
|
406
308
|
validateCandidate(input.result);
|
|
407
|
-
if (input.
|
|
408
|
-
return stageMigrationDeferredPending(input);
|
|
409
|
-
if (input.result.outcome !== "proposed" && input.requiredStorageVersion !== 2)
|
|
309
|
+
if (input.result.outcome !== "proposed")
|
|
410
310
|
return emptyResult();
|
|
411
311
|
const memoryPath = join(input.agentDir, "MEMORY.md");
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
await assertMemoryAccessDirectory(input.agentDir);
|
|
415
|
-
return withApplyLease(input.agentDir, async () => withMemoryAccess(input.agentDir, async () => {
|
|
416
|
-
await assertMemoryWriterAllowed(input.agentDir, input.writerCapabilities ?? []);
|
|
417
|
-
const memoryStat = await lstat(memoryPath);
|
|
418
|
-
if (memoryStat.size > 16 * 1024 * 1024)
|
|
419
|
-
throw new Error("local_memory_storage_byte_limit");
|
|
312
|
+
await assertWritablePaths(input.agentDir, memoryPath);
|
|
313
|
+
return withApplyLease(input.agentDir, async () => {
|
|
420
314
|
const memory = await readFile(memoryPath, "utf8");
|
|
421
|
-
assertLocalMemoryStorageSize(memory);
|
|
422
|
-
const fullStorage = detectLocalMemoryStorageVersion(memory) === 2;
|
|
423
|
-
if (input.requiredStorageVersion === 2 && !fullStorage) {
|
|
424
|
-
throw new Error("local_memory_v2_state_invalid_before_commit");
|
|
425
|
-
}
|
|
426
|
-
// A V2 no-op is still a commit decision. Return only after the access
|
|
427
|
-
// lock and committed-state writer gate have validated the same snapshot.
|
|
428
|
-
if (input.result.outcome !== "proposed")
|
|
429
|
-
return emptyResult();
|
|
430
315
|
if (parseUnmarkedMemoryIndex(memory).length > 0)
|
|
431
316
|
throw new Error("local_memory_legacy_index_requires_migration");
|
|
432
317
|
let entries = [...parseLocalMemoryIndex(memory)];
|
|
@@ -457,26 +342,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
457
342
|
const pendingCandidates = [];
|
|
458
343
|
const resolvedPendingIds = new Set();
|
|
459
344
|
const noteUpdates = new Map();
|
|
460
|
-
const noteCache = new Map();
|
|
461
|
-
let totalReadBytes = Buffer.byteLength(memory, "utf8");
|
|
462
|
-
const readStoredNote = async (path) => {
|
|
463
|
-
const cached = noteUpdates.get(path) ?? noteCache.get(path);
|
|
464
|
-
if (cached !== undefined)
|
|
465
|
-
return cached;
|
|
466
|
-
const stat = await lstat(path).catch((error) => {
|
|
467
|
-
if (error.code === "ENOENT")
|
|
468
|
-
return null;
|
|
469
|
-
throw error;
|
|
470
|
-
});
|
|
471
|
-
if (stat !== null && totalReadBytes + stat.size > LOCAL_MEMORY_READ_BYTE_CAP)
|
|
472
|
-
throw new Error("local_memory_read_byte_limit");
|
|
473
|
-
const content = await readNote(path);
|
|
474
|
-
totalReadBytes += Buffer.byteLength(content, "utf8");
|
|
475
|
-
if (totalReadBytes > LOCAL_MEMORY_READ_BYTE_CAP)
|
|
476
|
-
throw new Error("local_memory_read_byte_limit");
|
|
477
|
-
noteCache.set(path, content);
|
|
478
|
-
return content;
|
|
479
|
-
};
|
|
480
345
|
let allowActiveEviction = false;
|
|
481
346
|
const randomId = input.randomId ?? randomUUID;
|
|
482
347
|
const executionId = input.executionId ?? "unknown";
|
|
@@ -518,21 +383,10 @@ export async function applyLocalMemoryResult(input) {
|
|
|
518
383
|
if (existingCreate !== undefined && !matchesCreateCandidate(existingCreate, change)) {
|
|
519
384
|
throw new Error("local_memory_duplicate_candidate");
|
|
520
385
|
}
|
|
521
|
-
if (existingCreate === undefined && entries.some((entry) => (
|
|
522
|
-
|
|
386
|
+
if (existingCreate === undefined && entries.some((entry) => normalize(entry.title) === normalize(change.title)
|
|
387
|
+
|| normalize(entry.summary) === normalize(change.summary))) {
|
|
523
388
|
throw new Error("local_memory_duplicate_candidate");
|
|
524
389
|
}
|
|
525
|
-
if (existingCreate !== undefined) {
|
|
526
|
-
if (existingCreate.revision !== 1 || existingCreate.status !== "active") {
|
|
527
|
-
throw new Error("local_memory_create_replay_conflict");
|
|
528
|
-
}
|
|
529
|
-
const stored = await readStoredNote(resolve(input.agentDir, existingCreate.file));
|
|
530
|
-
if (uniqueNoteSection(stored, id)?.trimEnd() !== noteSection(id, 1, change.title, change.detailBody).trimEnd()) {
|
|
531
|
-
throw new Error("local_memory_create_replay_conflict");
|
|
532
|
-
}
|
|
533
|
-
// A proven duplicate is a no-op; it must never rewrite a newer body or status.
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
390
|
}
|
|
537
391
|
const revision = existingCreate?.revision ?? (current === undefined ? 1 : current.revision + 1);
|
|
538
392
|
const entry = existingCreate ?? entryFromChange(change, id, revision, current?.status ?? "active");
|
|
@@ -546,7 +400,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
546
400
|
: item),
|
|
547
401
|
entry,
|
|
548
402
|
];
|
|
549
|
-
const allowEvictionForChange =
|
|
403
|
+
const allowEvictionForChange = change.operation === "create" && change.priority === "high";
|
|
550
404
|
const protectedIds = new Set(allowEvictionForChange ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
551
405
|
if (allowEvictionForChange)
|
|
552
406
|
allowActiveEviction = true;
|
|
@@ -563,7 +417,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
563
417
|
const notePath = resolve(input.agentDir, entry.file);
|
|
564
418
|
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
565
419
|
throw new Error("local_memory_note_path_invalid");
|
|
566
|
-
const previous = await
|
|
420
|
+
const previous = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
567
421
|
const replacement = noteSection(id, revision, change.title, change.detailBody);
|
|
568
422
|
const existingSection = replaceNote(previous, id, replacement);
|
|
569
423
|
const next = existingSection ?? (current === undefined
|
|
@@ -581,7 +435,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
581
435
|
// Pending create candidates are durable overflow, not dead ends. Promote only
|
|
582
436
|
// when the current catalog has room; supersede/update candidates stay pending
|
|
583
437
|
// until a fresh revision-aware decision can safely replay them.
|
|
584
|
-
const pendingCandidatesOnDisk =
|
|
438
|
+
const pendingCandidatesOnDisk = await readPendingMemoryCandidates({ agentDir: input.agentDir }).catch((error) => {
|
|
585
439
|
if (!(error instanceof Error) || error.message !== "memory_pending_directory_over_budget")
|
|
586
440
|
throw error;
|
|
587
441
|
// 自动提升是可选工作,目录查询预算不应阻断本轮已验证的更新或腾位操作。
|
|
@@ -592,10 +446,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
592
446
|
return [];
|
|
593
447
|
});
|
|
594
448
|
for (const pending of pendingCandidatesOnDisk) {
|
|
595
|
-
// Migration-deferred candidates require reviewed import; only capacity
|
|
596
|
-
// overflow is eligible for the existing automatic promotion path.
|
|
597
|
-
if (pending.reason !== "index_over_budget")
|
|
598
|
-
continue;
|
|
599
449
|
const change = pending.candidate;
|
|
600
450
|
if (change.operation !== "create")
|
|
601
451
|
continue;
|
|
@@ -610,7 +460,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
610
460
|
|| normalize(entry.summary) === normalize(change.summary)));
|
|
611
461
|
if (duplicate !== undefined) {
|
|
612
462
|
const duplicatePath = resolve(input.agentDir, duplicate.file);
|
|
613
|
-
const duplicateBody = readNoteBody(await
|
|
463
|
+
const duplicateBody = readNoteBody(noteUpdates.get(duplicatePath) ?? await readNote(duplicatePath), duplicate.id);
|
|
614
464
|
if (duplicateBody === change.detailBody
|
|
615
465
|
|| duplicateBody === unwrappedNoteBody(change.title, change.detailBody).trim()) {
|
|
616
466
|
resolvedPendingIds.add(pending.pendingId);
|
|
@@ -628,7 +478,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
628
478
|
const notePath = resolve(input.agentDir, entry.file);
|
|
629
479
|
if (!isWithin(resolve(input.agentDir, "notes"), notePath))
|
|
630
480
|
throw new Error("local_memory_note_path_invalid");
|
|
631
|
-
const previous = await
|
|
481
|
+
const previous = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
632
482
|
const replacement = noteSection(id, 1, change.title, change.detailBody);
|
|
633
483
|
const existingSection = replaceNote(previous, id, replacement);
|
|
634
484
|
const next = existingSection ?? `${previous.trimEnd()}${previous.trimEnd() ? "\n\n" : ""}${replacement}`;
|
|
@@ -637,17 +487,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
637
487
|
createdIds.push(id);
|
|
638
488
|
resolvedPendingIds.add(pending.pendingId);
|
|
639
489
|
}
|
|
640
|
-
if (fullStorage) {
|
|
641
|
-
const changedMetadataIds = new Set([
|
|
642
|
-
...statusChangedIds,
|
|
643
|
-
...input.result.changes.flatMap(change => change.supersedesMemoryId === null ? [] : [change.supersedesMemoryId]),
|
|
644
|
-
]);
|
|
645
|
-
for (const id of changedMetadataIds) {
|
|
646
|
-
const entry = entries.find(item => item.id === id);
|
|
647
|
-
const notePath = resolve(input.agentDir, entry.file);
|
|
648
|
-
noteUpdates.set(notePath, updateNoteRevision(await readStoredNote(notePath), id, entry.revision));
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
490
|
let finalProjection = projectIndex(memory, entries, allowActiveEviction ? pinnedIds(entries) : activeOrPinnedIds(entries));
|
|
652
491
|
if (finalProjection.reason !== null
|
|
653
492
|
&& (!allowActiveEviction || finalProjection.reason === "index_over_budget_pinned")) {
|
|
@@ -659,7 +498,7 @@ export async function applyLocalMemoryResult(input) {
|
|
|
659
498
|
.filter((entry) => entry?.status === "active");
|
|
660
499
|
for (const entry of omittedActiveEntries) {
|
|
661
500
|
const notePath = resolve(input.agentDir, entry.file);
|
|
662
|
-
const content = await
|
|
501
|
+
const content = noteUpdates.get(notePath) ?? await readNote(notePath);
|
|
663
502
|
const detailBody = readNoteBody(content, entry.id);
|
|
664
503
|
const change = detailBody === null ? null : archivedChange(entry, detailBody);
|
|
665
504
|
if (change === null)
|
|
@@ -680,13 +519,6 @@ export async function applyLocalMemoryResult(input) {
|
|
|
680
519
|
if (finalProjection.reason !== null)
|
|
681
520
|
throw new Error(finalProjection.reason);
|
|
682
521
|
}
|
|
683
|
-
// Complete every resource check before the first persistent write.
|
|
684
|
-
assertLocalMemoryStorageSize(finalProjection.memory);
|
|
685
|
-
for (const content of noteUpdates.values()) {
|
|
686
|
-
if (Buffer.byteLength(content, "utf8") > LOCAL_MEMORY_NOTE_BYTE_CAP)
|
|
687
|
-
throw new Error("local_memory_note_byte_limit");
|
|
688
|
-
}
|
|
689
|
-
await assertMemoryInventoryHealth(input.agentDir);
|
|
690
522
|
for (const candidate of pendingCandidates)
|
|
691
523
|
await persistPendingMemoryCandidate({ agentDir: input.agentDir, candidate });
|
|
692
524
|
for (const [path, content] of noteUpdates)
|
|
@@ -703,5 +535,5 @@ export async function applyLocalMemoryResult(input) {
|
|
|
703
535
|
deferredIds,
|
|
704
536
|
pendingIds: deferredIds,
|
|
705
537
|
};
|
|
706
|
-
})
|
|
538
|
+
});
|
|
707
539
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
import { isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { capMemoryForInject, MEMORY_INJECT_CAP } from "./prompt.js";
|
|
@@ -8,7 +8,6 @@ const INJECTION_MARKERS = [
|
|
|
8
8
|
"## [注入] 你的 MEMORY.md(索引,只读参考)\n",
|
|
9
9
|
];
|
|
10
10
|
const LOCAL_MEMORY_HASH_CAP_BYTES = 10 * 1024 * 1024;
|
|
11
|
-
export const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 2_000;
|
|
12
11
|
function errorCode(error) {
|
|
13
12
|
const code = error?.code;
|
|
14
13
|
return typeof code === "string" ? code : error instanceof Error ? error.name : "unknown";
|
|
@@ -40,75 +39,30 @@ async function inspectFile(path, signal) {
|
|
|
40
39
|
return { ...base, error_code: errorCode(error) };
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
|
-
async function
|
|
44
|
-
let metadata;
|
|
45
|
-
try {
|
|
46
|
-
metadata = await lstat(path);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
const code = errorCode(error);
|
|
50
|
-
return code === "ENOENT" ? { exists: false, entry_count: 0 } : { exists: false, error_code: code };
|
|
51
|
-
}
|
|
52
|
-
const base = { exists: true, mtime_ms: metadata.mtimeMs };
|
|
53
|
-
if (metadata.isSymbolicLink() || !metadata.isDirectory())
|
|
54
|
-
return { ...base, hash_skipped_reason: "not_regular_file" };
|
|
55
|
-
try {
|
|
56
|
-
const names = (await readdir(path, { withFileTypes: true }))
|
|
57
|
-
.filter(entry => entry.name.endsWith(".json"))
|
|
58
|
-
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
59
|
-
if (names.length > 128)
|
|
60
|
-
return { ...base, entry_count: names.length, error_code: "entry_limit" };
|
|
61
|
-
const hash = createHash("sha256");
|
|
62
|
-
let size = 0;
|
|
63
|
-
for (const entry of names) {
|
|
64
|
-
if (signal.aborted)
|
|
65
|
-
throw signal.reason;
|
|
66
|
-
if (!entry.isFile() || entry.isSymbolicLink())
|
|
67
|
-
return { ...base, entry_count: names.length, error_code: "invalid_entry" };
|
|
68
|
-
const fact = await inspectFile(join(path, entry.name), signal);
|
|
69
|
-
if (!fact.exists || fact.sha256 === undefined || fact.error_code !== undefined
|
|
70
|
-
|| fact.hash_skipped_reason !== undefined)
|
|
71
|
-
return { ...base, entry_count: names.length, error_code: "incomplete_entry" };
|
|
72
|
-
size += fact.size_bytes ?? 0;
|
|
73
|
-
hash.update(entry.name).update("\0").update(fact.sha256).update("\0");
|
|
74
|
-
}
|
|
75
|
-
return { ...base, size_bytes: size, entry_count: names.length, sha256: hash.digest("hex") };
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
return { ...base, error_code: errorCode(error) };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
export async function withinLocalMemoryDiagnosticsDeadline(operation, timeoutMs) {
|
|
42
|
+
export async function inspectLocalMemoryFilesWithinDeadline(homeDir, timeoutMs) {
|
|
82
43
|
const controller = new AbortController();
|
|
83
44
|
let timer;
|
|
45
|
+
const inspection = Promise.all([
|
|
46
|
+
inspectFile(join(homeDir, "MEMORY.md"), controller.signal),
|
|
47
|
+
inspectFile(join(homeDir, "notes", "lessons.md"), controller.signal),
|
|
48
|
+
]).then(([memory, lessons]) => ({ memory, lessons }));
|
|
84
49
|
const timeout = new Promise((_resolve, reject) => {
|
|
85
50
|
timer = setTimeout(() => {
|
|
86
|
-
|
|
51
|
+
controller.abort();
|
|
52
|
+
reject(Object.assign(new Error("local memory diagnostics timed out"), {
|
|
87
53
|
code: "diagnostics_timeout",
|
|
88
|
-
});
|
|
89
|
-
controller.abort(error);
|
|
90
|
-
reject(error);
|
|
54
|
+
}));
|
|
91
55
|
}, Math.max(1, timeoutMs));
|
|
92
56
|
timer.unref?.();
|
|
93
57
|
});
|
|
94
58
|
try {
|
|
95
|
-
return await Promise.race([
|
|
59
|
+
return await Promise.race([inspection, timeout]);
|
|
96
60
|
}
|
|
97
61
|
finally {
|
|
98
62
|
if (timer !== undefined)
|
|
99
63
|
clearTimeout(timer);
|
|
100
64
|
}
|
|
101
65
|
}
|
|
102
|
-
export async function inspectLocalMemoryFilesWithinDeadline(homeDir, timeoutMs) {
|
|
103
|
-
return withinLocalMemoryDiagnosticsDeadline(async (signal) => {
|
|
104
|
-
const [memory, lessons, pending] = await Promise.all([
|
|
105
|
-
inspectFile(join(homeDir, "MEMORY.md"), signal),
|
|
106
|
-
inspectFile(join(homeDir, "notes", "lessons.md"), signal),
|
|
107
|
-
inspectPendingDirectory(join(homeDir, ".crew", "memory", "pending"), signal),
|
|
108
|
-
]);
|
|
109
|
-
return { memory, lessons, pending };
|
|
110
|
-
}, timeoutMs);
|
|
111
|
-
}
|
|
112
66
|
export function localMemoryContentFacts(content, maxScanBytes = LOCAL_MEMORY_HASH_CAP_BYTES) {
|
|
113
67
|
const sizeBytes = Buffer.byteLength(content, "utf8");
|
|
114
68
|
if (sizeBytes > Math.max(0, maxScanBytes)) {
|
|
@@ -263,50 +217,15 @@ function crewMemoryReadAccess(command, paths) {
|
|
|
263
217
|
return null;
|
|
264
218
|
}
|
|
265
219
|
}
|
|
266
|
-
/**
|
|
267
|
-
* Decode only the shell wrapper shape emitted by supported runtimes. The body must be one complete
|
|
268
|
-
* quoted argument. Double-quoted bodies may escape only a quote or backslash; shell expansion and
|
|
269
|
-
* concatenated quoting stay unknown so telemetry never guesses what the shell actually executed.
|
|
270
|
-
*/
|
|
271
|
-
function unwrapLiteralShellCommand(command) {
|
|
272
|
-
const trimmed = command.trim();
|
|
273
|
-
const wrapper = /^\/bin\/(?:(?:zsh|bash)\s+-lc|sh\s+-c)\s+([\s\S]+)$/u.exec(trimmed);
|
|
274
|
-
if (wrapper === null)
|
|
275
|
-
return trimmed;
|
|
276
|
-
const quoted = wrapper[1];
|
|
277
|
-
const quote = quoted[0];
|
|
278
|
-
if ((quote !== "'" && quote !== '"') || quoted.at(-1) !== quote || quoted.length < 2)
|
|
279
|
-
return null;
|
|
280
|
-
const body = quoted.slice(1, -1);
|
|
281
|
-
if (quote === "'")
|
|
282
|
-
return body.includes("'") ? null : body;
|
|
283
|
-
let decoded = "";
|
|
284
|
-
for (let index = 0; index < body.length; index += 1) {
|
|
285
|
-
const character = body[index];
|
|
286
|
-
if (character === '"' || character === "$" || character === "`")
|
|
287
|
-
return null;
|
|
288
|
-
if (character !== "\\") {
|
|
289
|
-
decoded += character;
|
|
290
|
-
continue;
|
|
291
|
-
}
|
|
292
|
-
const escaped = body[index + 1];
|
|
293
|
-
if (escaped !== '"' && escaped !== "\\")
|
|
294
|
-
return null;
|
|
295
|
-
decoded += escaped;
|
|
296
|
-
index += 1;
|
|
297
|
-
}
|
|
298
|
-
return decoded;
|
|
299
|
-
}
|
|
300
220
|
function commandAccesses(command, paths) {
|
|
301
|
-
|
|
302
|
-
if (unwrapped === null || /[;|&\n]/.test(unwrapped))
|
|
221
|
+
if (/[;|&\n]/.test(command))
|
|
303
222
|
return [];
|
|
304
223
|
const accesses = new Map();
|
|
305
|
-
const memoryRead = crewMemoryReadAccess(
|
|
224
|
+
const memoryRead = crewMemoryReadAccess(command, paths);
|
|
306
225
|
if (memoryRead !== null) {
|
|
307
226
|
accesses.set(`${memoryRead.target}:${memoryRead.relative_path ?? ""}:read`, memoryRead);
|
|
308
227
|
}
|
|
309
|
-
const expanded =
|
|
228
|
+
const expanded = command
|
|
310
229
|
.replaceAll("${CREW_HOME}", paths.homeDir)
|
|
311
230
|
.replaceAll("$CREW_HOME", paths.homeDir)
|
|
312
231
|
.replace(/["']/g, "");
|
|
@@ -38,7 +38,6 @@ export function logLocalMemoryDiagnosticsFailure(identity, phase, error) {
|
|
|
38
38
|
function snapshotFields(snapshot) {
|
|
39
39
|
if (snapshot === null)
|
|
40
40
|
return {};
|
|
41
|
-
const pending = snapshot.pending ?? { exists: false, entry_count: 0 };
|
|
42
41
|
return {
|
|
43
42
|
memory_exists: snapshot.memory.exists,
|
|
44
43
|
memory_size_bytes: snapshot.memory.size_bytes,
|
|
@@ -55,12 +54,6 @@ function snapshotFields(snapshot) {
|
|
|
55
54
|
lessons_hash_skipped_reason: snapshot.lessons.hash_skipped_reason,
|
|
56
55
|
lessons_error_code: snapshot.lessons.error_code,
|
|
57
56
|
lessons_markdown_heading_count: snapshot.lessons.markdown_heading_count,
|
|
58
|
-
pending_exists: pending.exists,
|
|
59
|
-
pending_size_bytes: pending.size_bytes,
|
|
60
|
-
pending_mtime_ms: pending.mtime_ms,
|
|
61
|
-
pending_sha256: pending.sha256,
|
|
62
|
-
pending_error_code: pending.error_code,
|
|
63
|
-
pending_entry_count: pending.entry_count,
|
|
64
57
|
};
|
|
65
58
|
}
|
|
66
59
|
function promptDelivery(runtime, stableProtocolRuntime) {
|
|
@@ -190,13 +183,10 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
190
183
|
const lessonsChanged = beforeSnapshot !== null && afterSnapshot !== null
|
|
191
184
|
? localMemoryFileChanged(beforeSnapshot.lessons, afterSnapshot.lessons)
|
|
192
185
|
: undefined;
|
|
193
|
-
const pendingChanged = beforeSnapshot !== null && afterSnapshot !== null
|
|
194
|
-
? localMemoryFileChanged(beforeSnapshot.pending ?? { exists: false }, afterSnapshot.pending ?? { exists: false })
|
|
195
|
-
: undefined;
|
|
196
186
|
const accessSummary = accessObserver.summary();
|
|
197
|
-
const sharedFilesChanged = memoryChanged === undefined || lessonsChanged === undefined
|
|
187
|
+
const sharedFilesChanged = memoryChanged === undefined || lessonsChanged === undefined
|
|
198
188
|
? undefined
|
|
199
|
-
: memoryChanged || lessonsChanged
|
|
189
|
+
: memoryChanged || lessonsChanged;
|
|
200
190
|
safeLog("local_memory.execution_observed", "本地记忆执行观察已完成", {
|
|
201
191
|
execution_id: options.executionId,
|
|
202
192
|
agent_handle: options.agentHandle,
|
|
@@ -210,7 +200,6 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
210
200
|
executor_outcome: executorCompleted ? "succeeded" : "failed",
|
|
211
201
|
memory_changed: memoryChanged,
|
|
212
202
|
lessons_changed: lessonsChanged,
|
|
213
|
-
pending_changed: pendingChanged,
|
|
214
203
|
shared_files_changed_during_execution: sharedFilesChanged,
|
|
215
204
|
unexpected_shared_write: options.isMemoryPrune
|
|
216
205
|
? undefined
|
|
@@ -235,12 +224,6 @@ export function createLocalMemoryTelemetry(options) {
|
|
|
235
224
|
lessons_after_hash_skipped_reason: afterSnapshot.lessons.hash_skipped_reason,
|
|
236
225
|
lessons_after_error_code: afterSnapshot.lessons.error_code,
|
|
237
226
|
lessons_after_markdown_heading_count: afterSnapshot.lessons.markdown_heading_count,
|
|
238
|
-
pending_after_exists: afterSnapshot.pending?.exists ?? false,
|
|
239
|
-
pending_after_size_bytes: afterSnapshot.pending?.size_bytes,
|
|
240
|
-
pending_after_mtime_ms: afterSnapshot.pending?.mtime_ms,
|
|
241
|
-
pending_after_sha256: afterSnapshot.pending?.sha256,
|
|
242
|
-
pending_after_error_code: afterSnapshot.pending?.error_code,
|
|
243
|
-
pending_after_entry_count: afterSnapshot.pending?.entry_count ?? 0,
|
|
244
227
|
}),
|
|
245
228
|
});
|
|
246
229
|
},
|
package/dist/machine-info.js
CHANGED
|
@@ -25,7 +25,6 @@ import { RUNTIME_HEALTH_PROBE_CAPABILITY } from "./runtime-health.js";
|
|
|
25
25
|
import { probeDeepSeekHarnessAcp, deepSeekHarnessLauncher } from "./runtimes/deepseek-harness.js";
|
|
26
26
|
import { daemonHome } from "./computer-profile.js";
|
|
27
27
|
import { daemonReleaseProvenance } from "./release-provenance.js";
|
|
28
|
-
import { MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY } from "./memory-migration-protocol.js";
|
|
29
28
|
export { executableRuntimes } from "./runtime-capabilities.js";
|
|
30
29
|
const execFileP = promisify(execFile);
|
|
31
30
|
export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
|
|
@@ -41,7 +40,6 @@ export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
|
|
|
41
40
|
});
|
|
42
41
|
export const DAEMON_CAPABILITIES = Object.freeze([
|
|
43
42
|
MARKETPLACE_CAPABILITY,
|
|
44
|
-
MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY,
|
|
45
43
|
"scheduled_job_v1",
|
|
46
44
|
"reply_origin_v1",
|
|
47
45
|
"origin_decision_v1",
|
|
@@ -77,7 +75,6 @@ export const daemonCapabilities = (runtimePlatform = process.platform, readiness
|
|
|
77
75
|
return Object.freeze(DAEMON_CAPABILITIES.filter((capability) => ((capability !== MARKETPLACE_CAPABILITY || nativeWorkspacePlatform)
|
|
78
76
|
&& (capability !== PROJECT_WORKSPACES_CAPABILITY || nativeWorkspaceReady)
|
|
79
77
|
&& (capability !== "message_file_preview_v1" || nativeWorkspacePlatform)
|
|
80
|
-
&& (nativeWorkspacePlatform || ![MEMORY_MIGRATION_CAPABILITY, MEMORY_STORAGE_V2_CAPABILITY, MEMORY_STATE_GUARD_CAPABILITY].includes(capability))
|
|
81
78
|
&& (nativeWorkspacePlatform
|
|
82
79
|
|| (capability !== "project_skills_v1"
|
|
83
80
|
&& capability !== "project_skill_projection_v2"
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
// 与另一个进程中的 marketplace-protocol.ts 保持一致;跨进程契约测试校验摘要。
|
|
3
3
|
export const MARKETPLACE_CAPABILITY = "marketplace_update_v1";
|
|
4
4
|
export const MARKETPLACE = "nowwork-pm";
|
|
5
|
-
export const PLUGIN = "
|
|
5
|
+
export const PLUGIN = "AIinterview-plugin";
|
|
6
6
|
export const MarketplaceModeSchema = z.enum(["inspect", "refresh", "update"]);
|
|
7
7
|
export const MarketplaceStatusSchema = z.enum(["queued", "running", "completed", "partial", "failed", "unknown"]);
|
|
8
8
|
export const MarketplaceTargetSchema = z.object({
|
|
@@ -10,7 +10,7 @@ const Target = z.object({
|
|
|
10
10
|
version: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._+-]{0,79}$/),
|
|
11
11
|
source: z.object({ source: z.literal("git-subdir"),
|
|
12
12
|
url: z.literal("git@codeup.aliyun.com:6128d2e757e7cd986dfae5ab/pm/AIinterview-agent-skill.git"),
|
|
13
|
-
path: z.literal("plugins/
|
|
13
|
+
path: z.literal("plugins/AIinterview-plugin"), sha: z.string().regex(/^[a-f0-9]{40}$/) }),
|
|
14
14
|
});
|
|
15
15
|
export async function readRemoteMarketplace(runtime, registered, env, command) {
|
|
16
16
|
const source = validateMarketplaceSource(registered.url, registered.ref);
|
|
@@ -12,7 +12,7 @@ import { readMarketplaceSource } from "./marketplace-source.js";
|
|
|
12
12
|
export { MarketplaceFailure, runMarketplaceCommand } from "./marketplace-command.js";
|
|
13
13
|
const SELECTOR = `${PLUGIN}@${MARKETPLACE}`;
|
|
14
14
|
const SOURCE = "git@codeup.aliyun.com:6128d2e757e7cd986dfae5ab/pm/AIinterview-agent-skill.git";
|
|
15
|
-
const SOURCE_PATH = "plugins/
|
|
15
|
+
const SOURCE_PATH = "plugins/AIinterview-plugin";
|
|
16
16
|
const obj = (x) => x !== null && typeof x === "object" && !Array.isArray(x) ? x : {};
|
|
17
17
|
const parse = (text) => { try {
|
|
18
18
|
return JSON.parse(text);
|