UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

136 lines (135 loc) 6.13 kB
import { r as asNullableRecord } from "./record-coerce-DItp3I4t.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { a as getNodeSqliteKysely, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js"; import { i as openOpenClawStateDatabase, s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js"; import { a as sha256Hex } from "./crypto-digest-C4hqTb_e.js"; import { a as updateConfigMachineState, r as readConfigMachineState } from "./config-machine-state-BCereLZr.js"; import { n as withOpenClawStateLease } from "./openclaw-state-lease-Ciyr2uVu.js"; import { o as databaseOptions, s as ensureSkillWorkshopSchema } from "./store-sqlite-record-9Z9Ph1pe.js"; import path from "node:path"; import { randomUUID } from "node:crypto"; //#region src/skills/workshop/collection-review-state.ts const REVIEW_CLAIM_MS = 66e4; const SKILL_COLLECTION_REVIEW_RETENTION_COUNT = 90; const SKILL_COLLECTION_REVIEW_HISTORY_LIMIT = 20; function workspaceKey(workspaceDir) { return sha256Hex(path.resolve(workspaceDir)); } async function withSkillCollectionReviewClaim(workspaceDir, run, options = {}) { return await withOpenClawStateLease({ scope: "skill-collection-review", key: workspaceKey(workspaceDir), database: { scope: "shared", options }, leaseMs: REVIEW_CLAIM_MS, waitMs: 0, leaseLabel: "skill collection review claim", operationLabel: "skill-collection.review" }, async () => await run()); } function reviewMap(state, field) { return asNullableRecord(state[field]) ?? {}; } function readReviewState(options) { return asNullableRecord(readConfigMachineState("skills.curatorState", options)?.lastResult) ?? {}; } function readSkillReviewOutcomes(options = {}) { const state = readReviewState(options); return { collectionReviews: reviewMap(state, "collectionReviews"), experienceReviews: reviewMap(state, "experienceReviews") }; } function recordSkillCollectionReviewStatus(workspaceDir, review, options = {}) { const status = review.error !== void 0 ? { attemptedAtMs: review.attemptedAtMs, error: formatErrorMessage(review.error).slice(0, 300) } : { attemptedAtMs: review.attemptedAtMs, ...review.succeededAtMs !== void 0 ? { succeededAtMs: review.succeededAtMs } : {} }; recordWorkspaceReview("collectionReviews", workspaceDir, status, { lastAttemptAtMs: status.attemptedAtMs, ...status.succeededAtMs !== void 0 ? { lastSuccessAtMs: status.succeededAtMs } : {}, lastError: status.error ?? null }, options); } function recordSkillExperienceReviewOutcome(workspaceDir, review, options = {}) { recordWorkspaceReview("experienceReviews", workspaceDir, review, {}, options); } function recordWorkspaceReview(field, workspaceDir, review, columns, options) { updateConfigMachineState("skills.curatorState", (current) => { const state = asNullableRecord(current?.lastResult) ?? {}; return { lastAttemptAtMs: 0, lastSuccessAtMs: null, lastError: null, ...current, ...columns, lastResult: { ...state, [field]: { ...asNullableRecord(state[field]), [workspaceKey(workspaceDir)]: review } } }; }, options); } function parseStoredNames(value, field) { const parsed = JSON.parse(value); if (!Array.isArray(parsed) || !parsed.every((entry) => typeof entry === "string")) throw new Error(`Invalid ${field} in stored skill collection review.`); return parsed; } function parseStoredDrops(value) { const parsed = JSON.parse(value); if (!Array.isArray(parsed)) throw new Error("Invalid dropped entries in stored skill collection review."); return parsed.map((entry) => { const record = asNullableRecord(entry); if (!record || typeof record.name !== "string" || typeof record.reason !== "string") throw new Error("Invalid dropped entry in stored skill collection review."); return { name: record.name, reason: record.reason }; }); } function listSkillCollectionReviewOutcomes(workspaceDir, options = {}) { ensureSkillWorkshopSchema(options); const database = openOpenClawStateDatabase(databaseOptions(options)); const kysely = getNodeSqliteKysely(database.db); return executeSqliteQuerySync(database.db, kysely.selectFrom("skill_workshop_collection_reviews").select([ "backup_id", "create_time", "kept_names_json", "written_names_json", "dropped_json" ]).where("workspace_dir", "=", path.resolve(workspaceDir)).orderBy("create_time", "desc").orderBy("review_id", "desc").limit(SKILL_COLLECTION_REVIEW_HISTORY_LIMIT)).rows.map((row) => ({ createTime: row.create_time, backupId: row.backup_id, kept: parseStoredNames(row.kept_names_json, "kept names"), written: parseStoredNames(row.written_names_json, "written names"), dropped: parseStoredDrops(row.dropped_json) })); } function recordSkillCollectionReviewHistory(workspaceDir, nowMs, result, options = {}) { ensureSkillWorkshopSchema(options); runOpenClawStateWriteTransaction(({ db }) => { const kysely = getNodeSqliteKysely(db); const resolvedWorkspaceDir = path.resolve(workspaceDir); executeSqliteQuerySync(db, kysely.insertInto("skill_workshop_collection_reviews").values({ review_id: randomUUID(), workspace_dir: resolvedWorkspaceDir, backup_id: result.backupId, create_time: nowMs, kept_names_json: JSON.stringify(result.kept), written_names_json: JSON.stringify(result.written), dropped_json: JSON.stringify(result.dropped) })); const retainedReviewIds = kysely.selectFrom("skill_workshop_collection_reviews").select("review_id").where("workspace_dir", "=", resolvedWorkspaceDir).orderBy("create_time", "desc").orderBy("review_id", "desc").limit(SKILL_COLLECTION_REVIEW_RETENTION_COUNT); executeSqliteQuerySync(db, kysely.deleteFrom("skill_workshop_collection_reviews").where("workspace_dir", "=", resolvedWorkspaceDir).where("review_id", "not in", retainedReviewIds)); }, databaseOptions(options)); } //#endregion export { recordSkillExperienceReviewOutcome as a, recordSkillCollectionReviewStatus as i, readSkillReviewOutcomes as n, withSkillCollectionReviewClaim as o, recordSkillCollectionReviewHistory as r, listSkillCollectionReviewOutcomes as t };