@happyvertical/smrt-playbooks 0.44.0
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/AGENTS.md +174 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +95 -0
- package/dist/index.d.ts +345 -0
- package/dist/index.js +734 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +424 -0
- package/dist/smrt-knowledge.json +367 -0
- package/dist/types.d.ts +241 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
import { PLAYBOOK_PLANES } from "./types.js";
|
|
2
|
+
import { ObjectRegistry, SmrtCollection, SmrtObject, field, smrt } from "@happyvertical/smrt-core";
|
|
3
|
+
import { getPackageConfig } from "@happyvertical/smrt-config";
|
|
4
|
+
import { getTenantId } from "@happyvertical/smrt-tenancy";
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __defProp$1 = Object.defineProperty;
|
|
7
|
+
var __exportAll = (all, no_symbols) => {
|
|
8
|
+
let target = {};
|
|
9
|
+
for (var name in all) __defProp$1(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
13
|
+
if (!no_symbols) __defProp$1(target, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/__smrt-register__.ts
|
|
18
|
+
ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-playbooks\",\"packageVersion\":\"0.44.0\",\"objects\":{\"@happyvertical/smrt-playbooks:PlaybookOverrideCollection\":{\"name\":\"playbookoverridecollection\",\"className\":\"PlaybookOverrideCollection\",\"qualifiedName\":\"@happyvertical/smrt-playbooks:PlaybookOverrideCollection\",\"collection\":\"playbookoverrides\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/playbooks/src/collections/PlaybookOverrideCollection.ts\",\"packageName\":\"@happyvertical/smrt-playbooks\",\"fields\":{},\"methods\":{\"getAppOverride\":{\"name\":\"getAppOverride\",\"async\":true,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<PlaybookOverride | null>\",\"isStatic\":false,\"isPublic\":true},\"getTenantOverride\":{\"name\":\"getTenantOverride\",\"async\":true,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false},{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<PlaybookOverride | null>\",\"isStatic\":false,\"isPublic\":true},\"getResolutionLayers\":{\"name\":\"getResolutionLayers\",\"async\":true,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false},{\"name\":\"tenantId\",\"type\":\"string | null\",\"optional\":true},{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<object>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_playbook_overrides\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"PlaybookOverride\",\"exportName\":\"PlaybookOverrideCollection\",\"collectionExportName\":\"PlaybookOverrideCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_playbook_overrides\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_playbook_overrides\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_playbook_overrides_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true},{\"name\":\"_smrt_playbook_overrides_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"410e08c4\"}},\"@happyvertical/smrt-playbooks:PlaybookOverride\":{\"name\":\"playbookoverride\",\"className\":\"PlaybookOverride\",\"qualifiedName\":\"@happyvertical/smrt-playbooks:PlaybookOverride\",\"collection\":\"playbookoverrides\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/playbooks/src/models/PlaybookOverride.ts\",\"packageName\":\"@happyvertical/smrt-playbooks\",\"fields\":{\"key\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"title\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"description\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"planes\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"onStepFailure\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"enabled\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{\"nullable\":true}},\"metadata\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{\"getMetadata\":{\"name\":\"getMetadata\",\"async\":false,\"parameters\":[],\"returnType\":\"PlaybookMetadata\",\"isStatic\":false,\"isPublic\":true},\"setMetadata\":{\"name\":\"setMetadata\",\"async\":false,\"parameters\":[{\"name\":\"metadata\",\"type\":\"PlaybookMetadata | null\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getPlanes\":{\"name\":\"getPlanes\",\"async\":false,\"parameters\":[],\"returnType\":\"null\",\"isStatic\":false,\"isPublic\":true},\"setPlanes\":{\"name\":\"setPlanes\",\"async\":false,\"parameters\":[{\"name\":\"planes\",\"type\":\"null\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"toPlaybookLayer\":{\"name\":\"toPlaybookLayer\",\"async\":false,\"parameters\":[],\"returnType\":\"PlaybookLayer\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_playbook_overrides\",\"conflictColumns\":[\"key\",\"context\"],\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"cli\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"],\"exclude\":[\"getMetadata\",\"setMetadata\",\"getPlanes\",\"setPlanes\",\"toPlaybookLayer\"]},\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"PlaybookOverride\",\"collectionExportName\":\"PlaybookOverrideCollection\",\"validationRules\":[{\"field\":\"key\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_playbook_overrides\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_playbook_overrides\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"key\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" TEXT,\\n \\\"title\\\" TEXT,\\n \\\"description\\\" TEXT,\\n \\\"planes\\\" TEXT,\\n \\\"on_step_failure\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"metadata\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"title\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"description\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"planes\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"on_step_failure\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"metadata\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_playbook_overrides_key_context_idx\",\"columns\":[\"key\",\"context\"],\"unique\":true},{\"name\":\"_smrt_playbook_overrides_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"_smrt_playbook_overrides_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"8aec8e13\"}}},\"moduleType\":\"smrt\",\"smrtDependencies\":[\"@happyvertical/smrt-core\",\"@happyvertical/smrt-tenancy\"]}"));
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/cache.ts
|
|
21
|
+
var PLAYBOOK_CACHE_TTL_MS = 3e4;
|
|
22
|
+
var playbookCache = /* @__PURE__ */ new Map();
|
|
23
|
+
var cacheGenerations = /* @__PURE__ */ new Map();
|
|
24
|
+
var dbInstanceIds = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
var nextDbId = 1;
|
|
26
|
+
function getDbNamespace(db) {
|
|
27
|
+
if (!db) return "no-db";
|
|
28
|
+
if (typeof db === "string") return `db:${db}`;
|
|
29
|
+
if (typeof db === "object") {
|
|
30
|
+
const dbObject = db;
|
|
31
|
+
if (typeof dbObject.query === "function") {
|
|
32
|
+
if (!dbInstanceIds.has(dbObject)) dbInstanceIds.set(dbObject, `db-instance:${nextDbId++}`);
|
|
33
|
+
const namespace = dbInstanceIds.get(dbObject);
|
|
34
|
+
if (namespace) return namespace;
|
|
35
|
+
return "db-instance:unknown";
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return `db-config:${JSON.stringify(dbObject)}`;
|
|
39
|
+
} catch {
|
|
40
|
+
return "db-config:opaque";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return "db:unknown";
|
|
44
|
+
}
|
|
45
|
+
function buildCacheKey(key, tenantId, db) {
|
|
46
|
+
return `${getDbNamespace(db)}::${key}::${tenantId ?? "app"}`;
|
|
47
|
+
}
|
|
48
|
+
function buildGenerationKey(key, db) {
|
|
49
|
+
return `${getDbNamespace(db)}::${key}`;
|
|
50
|
+
}
|
|
51
|
+
function bumpGeneration(key, db) {
|
|
52
|
+
const generationKey = buildGenerationKey(key, db);
|
|
53
|
+
cacheGenerations.set(generationKey, (cacheGenerations.get(generationKey) ?? 0) + 1);
|
|
54
|
+
}
|
|
55
|
+
function getPlaybookCacheTtlMs() {
|
|
56
|
+
return PLAYBOOK_CACHE_TTL_MS;
|
|
57
|
+
}
|
|
58
|
+
function getCachedPlaybookBase(key, tenantId, db) {
|
|
59
|
+
const cacheKey = buildCacheKey(key, tenantId, db);
|
|
60
|
+
const cached = playbookCache.get(cacheKey);
|
|
61
|
+
if (!cached) return null;
|
|
62
|
+
if (cached.expiresAt <= Date.now()) {
|
|
63
|
+
playbookCache.delete(cacheKey);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return cached.value;
|
|
67
|
+
}
|
|
68
|
+
function getPlaybookCacheGeneration(key, db) {
|
|
69
|
+
return cacheGenerations.get(buildGenerationKey(key, db)) ?? 0;
|
|
70
|
+
}
|
|
71
|
+
function setCachedPlaybookBase(key, tenantId, db, value, loadedAtGeneration) {
|
|
72
|
+
if (getPlaybookCacheGeneration(key, db) !== loadedAtGeneration) return;
|
|
73
|
+
playbookCache.set(buildCacheKey(key, tenantId, db), {
|
|
74
|
+
expiresAt: Date.now() + PLAYBOOK_CACHE_TTL_MS,
|
|
75
|
+
value
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function invalidatePlaybookCache(key, tenantId, db) {
|
|
79
|
+
const dbNamespace = getDbNamespace(db);
|
|
80
|
+
bumpGeneration(key, db);
|
|
81
|
+
if (tenantId !== null && tenantId !== void 0) {
|
|
82
|
+
playbookCache.delete(buildCacheKey(key, tenantId, db));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const keyPrefix = `${dbNamespace}::${key}::`;
|
|
86
|
+
for (const cacheKey of playbookCache.keys()) if (cacheKey.startsWith(keyPrefix)) playbookCache.delete(cacheKey);
|
|
87
|
+
}
|
|
88
|
+
function clearPlaybookCache() {
|
|
89
|
+
playbookCache.clear();
|
|
90
|
+
for (const generationKey of cacheGenerations.keys()) cacheGenerations.set(generationKey, (cacheGenerations.get(generationKey) ?? 0) + 1);
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/utils.ts
|
|
94
|
+
var DEFAULT_EDITABLE = {
|
|
95
|
+
title: false,
|
|
96
|
+
description: false,
|
|
97
|
+
planes: false,
|
|
98
|
+
onStepFailure: false,
|
|
99
|
+
enabled: false,
|
|
100
|
+
metadata: false
|
|
101
|
+
};
|
|
102
|
+
var FAIL_CLOSED_CLASSIFICATION = {
|
|
103
|
+
effect: "destructive",
|
|
104
|
+
idempotent: false,
|
|
105
|
+
openWorld: true
|
|
106
|
+
};
|
|
107
|
+
var FAILURE_POLICIES = /* @__PURE__ */ new Set(["abort", "continue"]);
|
|
108
|
+
var QUALIFIED_MODEL_PATTERN = /^\S+:\S+$/;
|
|
109
|
+
var BROWSER_ONLY_PLANES = Object.freeze(["browser"]);
|
|
110
|
+
function isPlainObject(value) {
|
|
111
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
112
|
+
}
|
|
113
|
+
function normalizeEditableConfig(editable) {
|
|
114
|
+
const flag = (name) => {
|
|
115
|
+
const supplied = editable?.[name];
|
|
116
|
+
if (supplied === void 0) return DEFAULT_EDITABLE[name];
|
|
117
|
+
if (typeof supplied !== "boolean") throw new Error(`Playbook editable.${name} must be a boolean, received ${typeof supplied} "${String(supplied)}"`);
|
|
118
|
+
return supplied;
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
title: flag("title"),
|
|
122
|
+
description: flag("description"),
|
|
123
|
+
planes: flag("planes"),
|
|
124
|
+
onStepFailure: flag("onStepFailure"),
|
|
125
|
+
enabled: flag("enabled"),
|
|
126
|
+
metadata: flag("metadata")
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function applyCapabilityDeclaration(declaration) {
|
|
130
|
+
if (!isPlainObject(declaration)) return { ...FAIL_CLOSED_CLASSIFICATION };
|
|
131
|
+
return {
|
|
132
|
+
effect: declaration.effect ?? FAIL_CLOSED_CLASSIFICATION.effect,
|
|
133
|
+
idempotent: declaration.idempotent ?? FAIL_CLOSED_CLASSIFICATION.idempotent,
|
|
134
|
+
openWorld: declaration.openWorld ?? FAIL_CLOSED_CLASSIFICATION.openWorld
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function isCapabilityDeclarationComplete(declaration) {
|
|
138
|
+
return isPlainObject(declaration) && declaration.effect !== void 0 && declaration.idempotent !== void 0 && declaration.openWorld !== void 0;
|
|
139
|
+
}
|
|
140
|
+
function normalizePlanes(planes, context) {
|
|
141
|
+
if (planes === void 0) return;
|
|
142
|
+
if (planes === null) return;
|
|
143
|
+
if (!Array.isArray(planes)) throw new Error(`${context} planes must be an array`);
|
|
144
|
+
const normalized = [];
|
|
145
|
+
for (const plane of planes) {
|
|
146
|
+
if (!PLAYBOOK_PLANES.includes(plane)) throw new Error(`${context} declares unknown plane "${String(plane)}"; expected one of ${PLAYBOOK_PLANES.join(", ")}`);
|
|
147
|
+
if (!normalized.includes(plane)) normalized.push(plane);
|
|
148
|
+
}
|
|
149
|
+
if (normalized.length === 0) throw new Error(`${context} must declare at least one plane`);
|
|
150
|
+
return PLAYBOOK_PLANES.filter((plane) => normalized.includes(plane));
|
|
151
|
+
}
|
|
152
|
+
function normalizeFailurePolicy(value, context) {
|
|
153
|
+
if (!FAILURE_POLICIES.has(value)) throw new Error(`${context} onStepFailure must be "abort" or "continue", received "${String(value)}"`);
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
function normalizeEnabled(value, context) {
|
|
157
|
+
if (typeof value !== "boolean") throw new Error(`${context} enabled must be a boolean, received ${typeof value} "${String(value)}"`);
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
function normalizeStepOptional(value, context) {
|
|
161
|
+
if (typeof value !== "boolean") throw new Error(`${context} optional must be a boolean, received ${typeof value} "${String(value)}"`);
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
function normalizeOptionalText(value, context, field) {
|
|
165
|
+
if (value === void 0) return;
|
|
166
|
+
if (typeof value !== "string") throw new Error(`${context} ${field} must be a string`);
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
function normalizeSteps(steps, context) {
|
|
170
|
+
if (!Array.isArray(steps) || steps.length === 0) throw new Error(`${context} requires at least one step`);
|
|
171
|
+
return Object.freeze(steps.map((rawStep, index) => {
|
|
172
|
+
const stepContext = `${context} step ${index}`;
|
|
173
|
+
if (!isPlainObject(rawStep)) throw new Error(`${stepContext} must be an object`);
|
|
174
|
+
const kind = rawStep.kind;
|
|
175
|
+
if (kind === "playbook" || "playbook" in rawStep) throw new Error(`${stepContext} references another playbook; nested playbooks are not supported`);
|
|
176
|
+
if (kind === "operation") {
|
|
177
|
+
const model = rawStep.model;
|
|
178
|
+
const action = rawStep.action;
|
|
179
|
+
if (typeof model !== "string" || model.trim() === "") throw new Error(`${stepContext} requires a qualified model name`);
|
|
180
|
+
if (!QUALIFIED_MODEL_PATTERN.test(model)) throw new Error(`${stepContext} model "${model}" must be a qualified pair such as "@happyvertical/smrt-commerce:Order"`);
|
|
181
|
+
if (typeof action !== "string" || action.trim() === "") throw new Error(`${stepContext} requires an action name`);
|
|
182
|
+
return Object.freeze({
|
|
183
|
+
kind: "operation",
|
|
184
|
+
model,
|
|
185
|
+
action,
|
|
186
|
+
...normalizeOptionalText(rawStep.label, stepContext, "label") !== void 0 ? { label: rawStep.label } : {},
|
|
187
|
+
...normalizeOptionalText(rawStep.description, stepContext, "description") !== void 0 ? { description: rawStep.description } : {},
|
|
188
|
+
...rawStep.optional === void 0 ? {} : { optional: normalizeStepOptional(rawStep.optional, stepContext) }
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (kind === "intent") {
|
|
192
|
+
const id = rawStep.id;
|
|
193
|
+
if (typeof id !== "string" || id.trim() === "") throw new Error(`${stepContext} requires an intent id`);
|
|
194
|
+
return Object.freeze({
|
|
195
|
+
kind: "intent",
|
|
196
|
+
id,
|
|
197
|
+
...normalizeOptionalText(rawStep.label, stepContext, "label") !== void 0 ? { label: rawStep.label } : {},
|
|
198
|
+
...normalizeOptionalText(rawStep.description, stepContext, "description") !== void 0 ? { description: rawStep.description } : {},
|
|
199
|
+
...rawStep.optional === void 0 ? {} : { optional: normalizeStepOptional(rawStep.optional, stepContext) }
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
throw new Error(`${stepContext} has unknown kind "${String(kind)}"; expected "operation" or "intent"`);
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
function hasIntentStep(steps) {
|
|
206
|
+
return steps.some((step) => step.kind === "intent");
|
|
207
|
+
}
|
|
208
|
+
function defaultPlanesForSteps(steps) {
|
|
209
|
+
return hasIntentStep(steps) ? BROWSER_ONLY_PLANES : PLAYBOOK_PLANES;
|
|
210
|
+
}
|
|
211
|
+
function normalizePlaybookDefinitionInput(input) {
|
|
212
|
+
if (!input || typeof input.key !== "string" || input.key.trim() === "") throw new Error("Playbook definitions require a non-empty key");
|
|
213
|
+
const context = `Playbook "${input.key}"`;
|
|
214
|
+
if (typeof input.title !== "string" || input.title.trim() === "") throw new Error(`${context} requires a title`);
|
|
215
|
+
if (typeof input.description !== "string" || input.description.trim() === "") throw new Error(`${context} requires a description`);
|
|
216
|
+
if ("steps" in (input.editable ?? {})) throw new Error(`${context} cannot mark steps editable; playbook step lists are never editable`);
|
|
217
|
+
const steps = normalizeSteps(input.steps, context);
|
|
218
|
+
const declaredPlanes = normalizePlanes(input.planes, context);
|
|
219
|
+
return Object.freeze({
|
|
220
|
+
key: input.key,
|
|
221
|
+
title: input.title,
|
|
222
|
+
description: input.description,
|
|
223
|
+
steps,
|
|
224
|
+
planes: Object.freeze(declaredPlanes ?? defaultPlanesForSteps(steps)),
|
|
225
|
+
onStepFailure: input.onStepFailure === void 0 ? "abort" : normalizeFailurePolicy(input.onStepFailure, context),
|
|
226
|
+
enabled: input.enabled === void 0 ? true : normalizeEnabled(input.enabled, context),
|
|
227
|
+
metadata: Object.freeze(input.metadata ? sanitizeMetadata(input.metadata) : {}),
|
|
228
|
+
editable: Object.freeze(normalizeEditableConfig(input.editable))
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function sanitizeMetadata(metadata) {
|
|
232
|
+
if (!isPlainObject(metadata)) return {};
|
|
233
|
+
return deepFreezeJsonObject(metadata);
|
|
234
|
+
}
|
|
235
|
+
function deepFreezeJsonValue(value) {
|
|
236
|
+
if (Array.isArray(value)) return Object.freeze(value.map(deepFreezeJsonValue));
|
|
237
|
+
if (isPlainObject(value)) return deepFreezeJsonObject(value);
|
|
238
|
+
return value;
|
|
239
|
+
}
|
|
240
|
+
function deepFreezeJsonObject(source) {
|
|
241
|
+
const copied = {};
|
|
242
|
+
for (const [key, value] of Object.entries(source)) {
|
|
243
|
+
if (value === void 0) continue;
|
|
244
|
+
copied[key] = deepFreezeJsonValue(value);
|
|
245
|
+
}
|
|
246
|
+
return Object.freeze(copied);
|
|
247
|
+
}
|
|
248
|
+
function parseMetadata(raw) {
|
|
249
|
+
if (!raw) return {};
|
|
250
|
+
try {
|
|
251
|
+
const parsed = JSON.parse(raw);
|
|
252
|
+
return isPlainObject(parsed) ? sanitizeMetadata(parsed) : {};
|
|
253
|
+
} catch {
|
|
254
|
+
return {};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function serializeMetadata(metadata) {
|
|
258
|
+
if (metadata === null || metadata === void 0) return null;
|
|
259
|
+
return JSON.stringify(sanitizeMetadata(metadata));
|
|
260
|
+
}
|
|
261
|
+
function parsePlanes(raw) {
|
|
262
|
+
if (!raw) return null;
|
|
263
|
+
try {
|
|
264
|
+
const parsed = JSON.parse(raw);
|
|
265
|
+
if (!Array.isArray(parsed)) return null;
|
|
266
|
+
return normalizePlanes(parsed, "Stored override") ?? null;
|
|
267
|
+
} catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function serializePlanes(planes) {
|
|
272
|
+
if (planes === null || planes === void 0) return null;
|
|
273
|
+
return JSON.stringify(normalizePlanes(planes, "Stored override") ?? []);
|
|
274
|
+
}
|
|
275
|
+
function normalizePlaybookLayer(input, context = "Playbook override") {
|
|
276
|
+
if (!isPlainObject(input)) return {};
|
|
277
|
+
if ("steps" in input) throw new Error(`${context} cannot set steps; playbook step lists are never editable`);
|
|
278
|
+
const layer = {};
|
|
279
|
+
if (input.title !== void 0) layer.title = input.title === null ? null : normalizeOptionalText(input.title, context, "title") ?? null;
|
|
280
|
+
if (input.description !== void 0) layer.description = input.description === null ? null : normalizeOptionalText(input.description, context, "description") ?? null;
|
|
281
|
+
if (input.planes !== void 0) layer.planes = input.planes === null ? null : normalizePlanes(input.planes, context) ?? null;
|
|
282
|
+
if (input.onStepFailure !== void 0) layer.onStepFailure = input.onStepFailure === null ? null : normalizeFailurePolicy(input.onStepFailure, context);
|
|
283
|
+
if (input.enabled !== void 0) layer.enabled = input.enabled === null ? null : normalizeEnabled(input.enabled, context);
|
|
284
|
+
if (input.metadata !== void 0) layer.metadata = input.metadata === null ? null : sanitizeMetadata(input.metadata);
|
|
285
|
+
return layer;
|
|
286
|
+
}
|
|
287
|
+
function mergePlaybookLayers(base, ...layers) {
|
|
288
|
+
let title = base.title;
|
|
289
|
+
let description = base.description;
|
|
290
|
+
let planes = base.planes;
|
|
291
|
+
let onStepFailure = base.onStepFailure;
|
|
292
|
+
let enabled = base.enabled;
|
|
293
|
+
let metadata = { ...base.metadata };
|
|
294
|
+
for (const layer of layers) {
|
|
295
|
+
if (!layer) continue;
|
|
296
|
+
if (layer.title !== void 0 && layer.title !== null) title = layer.title;
|
|
297
|
+
if (layer.description !== void 0 && layer.description !== null) description = layer.description;
|
|
298
|
+
if (layer.planes !== void 0 && layer.planes !== null) planes = layer.planes.filter((plane) => planes.includes(plane));
|
|
299
|
+
if (layer.onStepFailure !== void 0 && layer.onStepFailure !== null) onStepFailure = layer.onStepFailure;
|
|
300
|
+
if (layer.enabled !== void 0 && layer.enabled !== null) enabled = enabled && layer.enabled;
|
|
301
|
+
if (layer.metadata !== void 0 && layer.metadata !== null) metadata = {
|
|
302
|
+
...metadata,
|
|
303
|
+
...layer.metadata
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return Object.freeze({
|
|
307
|
+
title,
|
|
308
|
+
description,
|
|
309
|
+
planes: Object.freeze(planes),
|
|
310
|
+
onStepFailure,
|
|
311
|
+
enabled,
|
|
312
|
+
metadata: Object.freeze(metadata)
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/playbook-registry.ts
|
|
317
|
+
function getRegistry() {
|
|
318
|
+
if (!globalThis.__smrtPlaybookRegistry) globalThis.__smrtPlaybookRegistry = /* @__PURE__ */ new Map();
|
|
319
|
+
return globalThis.__smrtPlaybookRegistry;
|
|
320
|
+
}
|
|
321
|
+
function stableStringify(value) {
|
|
322
|
+
if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
323
|
+
if (value && typeof value === "object") return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
|
|
324
|
+
return JSON.stringify(value);
|
|
325
|
+
}
|
|
326
|
+
var PlaybookRegistry = {
|
|
327
|
+
register(input) {
|
|
328
|
+
const definition = normalizePlaybookDefinitionInput(input);
|
|
329
|
+
const registry = getRegistry();
|
|
330
|
+
const existing = registry.get(definition.key);
|
|
331
|
+
if (existing) {
|
|
332
|
+
if (stableStringify(existing) !== stableStringify(definition)) throw new Error(`Playbook "${definition.key}" is already registered with a different definition`);
|
|
333
|
+
return existing;
|
|
334
|
+
}
|
|
335
|
+
registry.set(definition.key, definition);
|
|
336
|
+
return definition;
|
|
337
|
+
},
|
|
338
|
+
get(key) {
|
|
339
|
+
return getRegistry().get(key);
|
|
340
|
+
},
|
|
341
|
+
has(key) {
|
|
342
|
+
return getRegistry().has(key);
|
|
343
|
+
},
|
|
344
|
+
getAll() {
|
|
345
|
+
return Array.from(getRegistry().values());
|
|
346
|
+
},
|
|
347
|
+
clear() {
|
|
348
|
+
getRegistry().clear();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
function definePlaybook(input) {
|
|
352
|
+
return PlaybookRegistry.register(input);
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/models/PlaybookOverride.ts
|
|
356
|
+
var __defProp = Object.defineProperty;
|
|
357
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
358
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
359
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
360
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
361
|
+
if (kind && result) __defProp(target, key, result);
|
|
362
|
+
return result;
|
|
363
|
+
};
|
|
364
|
+
function getPlaybookConfig$1() {
|
|
365
|
+
return getPackageConfig("playbooks", { playbooks: {} });
|
|
366
|
+
}
|
|
367
|
+
var PlaybookOverride = class extends SmrtObject {
|
|
368
|
+
key = "";
|
|
369
|
+
tenantId = null;
|
|
370
|
+
title = null;
|
|
371
|
+
description = null;
|
|
372
|
+
planes = null;
|
|
373
|
+
onStepFailure = null;
|
|
374
|
+
enabled = null;
|
|
375
|
+
metadata = null;
|
|
376
|
+
constructor(options = {}) {
|
|
377
|
+
super(options);
|
|
378
|
+
if (options.steps !== void 0) throw new Error("Playbook step lists are never editable; PlaybookOverride cannot carry steps");
|
|
379
|
+
if (options.key !== void 0) this.key = options.key;
|
|
380
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
381
|
+
if (options.title !== void 0) this.title = options.title;
|
|
382
|
+
if (options.description !== void 0) this.description = options.description;
|
|
383
|
+
if (options.onStepFailure !== void 0) this.onStepFailure = options.onStepFailure;
|
|
384
|
+
if (options.enabled !== void 0) this.enabled = options.enabled;
|
|
385
|
+
if (options.planes !== void 0) this.planes = typeof options.planes === "string" || options.planes === null ? options.planes : serializePlanes(options.planes);
|
|
386
|
+
if (options.metadata !== void 0) this.metadata = typeof options.metadata === "string" || options.metadata === null ? options.metadata : serializeMetadata(options.metadata);
|
|
387
|
+
}
|
|
388
|
+
getMetadata() {
|
|
389
|
+
return parseMetadata(this.metadata);
|
|
390
|
+
}
|
|
391
|
+
setMetadata(metadata) {
|
|
392
|
+
this.metadata = serializeMetadata(metadata);
|
|
393
|
+
}
|
|
394
|
+
getPlanes() {
|
|
395
|
+
return parsePlanes(this.planes);
|
|
396
|
+
}
|
|
397
|
+
setPlanes(planes) {
|
|
398
|
+
this.planes = serializePlanes(planes);
|
|
399
|
+
}
|
|
400
|
+
toPlaybookLayer() {
|
|
401
|
+
return {
|
|
402
|
+
title: this.title,
|
|
403
|
+
description: this.description,
|
|
404
|
+
planes: this.getPlanes(),
|
|
405
|
+
onStepFailure: this.onStepFailure ?? null,
|
|
406
|
+
enabled: this.enabled === null || this.enabled === void 0 ? null : Boolean(this.enabled),
|
|
407
|
+
metadata: this.metadata === null ? null : this.getMetadata()
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
async save() {
|
|
411
|
+
const previousIdentity = await this.getPersistedIdentity();
|
|
412
|
+
this.normalizeForPersistence();
|
|
413
|
+
await this.validatePlaybookOverride();
|
|
414
|
+
this.context = this.tenantId ?? "__app__";
|
|
415
|
+
const identityChanged = !!previousIdentity && (previousIdentity.key !== this.key || previousIdentity.tenantId !== this.tenantId);
|
|
416
|
+
const result = identityChanged && previousIdentity ? await this.saveAfterIdentityChange() : await super.save();
|
|
417
|
+
if (identityChanged && previousIdentity) invalidatePlaybookCache(previousIdentity.key, previousIdentity.tenantId, this.db);
|
|
418
|
+
invalidatePlaybookCache(this.key, this.tenantId, this.db);
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
async saveAfterIdentityChange() {
|
|
422
|
+
if (typeof this.db.beginTransaction === "function") return this.saveAfterIdentityChangeInTransaction();
|
|
423
|
+
return this.saveAfterIdentityChangeWithDeferredDelete();
|
|
424
|
+
}
|
|
425
|
+
async saveAfterIdentityChangeInTransaction() {
|
|
426
|
+
const originalDb = this._db;
|
|
427
|
+
const originalOptionsDb = this.options.db;
|
|
428
|
+
const tx = await this.db.beginTransaction?.();
|
|
429
|
+
if (!tx) return this.saveAfterIdentityChangeWithDeferredDelete();
|
|
430
|
+
try {
|
|
431
|
+
this._db = tx;
|
|
432
|
+
this.options.db = tx;
|
|
433
|
+
await super.delete();
|
|
434
|
+
const result = await super.save();
|
|
435
|
+
await tx.commit();
|
|
436
|
+
return result;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
try {
|
|
439
|
+
await tx.rollback();
|
|
440
|
+
} catch {}
|
|
441
|
+
throw error;
|
|
442
|
+
} finally {
|
|
443
|
+
this._db = originalDb;
|
|
444
|
+
this.options.db = originalOptionsDb;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async saveAfterIdentityChangeWithDeferredDelete() {
|
|
448
|
+
const previousId = this.id;
|
|
449
|
+
if (!previousId) return super.save();
|
|
450
|
+
const replacementId = crypto.randomUUID();
|
|
451
|
+
let replacementSaved = false;
|
|
452
|
+
this.id = replacementId;
|
|
453
|
+
try {
|
|
454
|
+
const result = await super.save();
|
|
455
|
+
replacementSaved = true;
|
|
456
|
+
await this.db.delete(this.tableName, { id: previousId });
|
|
457
|
+
return result;
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (replacementSaved) try {
|
|
460
|
+
await this.db.delete(this.tableName, { id: replacementId });
|
|
461
|
+
} catch {}
|
|
462
|
+
this.id = previousId;
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async delete() {
|
|
467
|
+
const key = this.key;
|
|
468
|
+
const tenantId = this.tenantId;
|
|
469
|
+
await super.delete();
|
|
470
|
+
invalidatePlaybookCache(key, tenantId, this.db);
|
|
471
|
+
}
|
|
472
|
+
async validatePlaybookOverride() {
|
|
473
|
+
if (!this.key || this.key.trim() === "") throw new Error("PlaybookOverride.key is required");
|
|
474
|
+
if (this.steps !== void 0) throw new Error(`Playbook "${this.key}" step lists are never editable; PlaybookOverride cannot carry steps`);
|
|
475
|
+
const definition = PlaybookRegistry.get(this.key);
|
|
476
|
+
if (!definition) throw new Error(`Unknown playbook key "${this.key}"`);
|
|
477
|
+
const editable = definition.editable;
|
|
478
|
+
if (this.title !== null && !editable.title) throw new Error(`Playbook "${this.key}" does not allow title overrides`);
|
|
479
|
+
if (this.description !== null && !editable.description) throw new Error(`Playbook "${this.key}" does not allow description overrides`);
|
|
480
|
+
if (this.planes !== null && !editable.planes) throw new Error(`Playbook "${this.key}" does not allow planes overrides`);
|
|
481
|
+
if (this.onStepFailure !== null && !editable.onStepFailure) throw new Error(`Playbook "${this.key}" does not allow onStepFailure overrides`);
|
|
482
|
+
if (this.enabled !== null && !editable.enabled) throw new Error(`Playbook "${this.key}" does not allow enablement overrides`);
|
|
483
|
+
if (this.metadata !== null && !editable.metadata) throw new Error(`Playbook "${this.key}" does not allow metadata overrides`);
|
|
484
|
+
if (this.metadata !== null) {
|
|
485
|
+
let parsed;
|
|
486
|
+
try {
|
|
487
|
+
parsed = JSON.parse(this.metadata);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
throw new Error(`Playbook "${this.key}" has invalid metadata JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
490
|
+
}
|
|
491
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Playbook "${this.key}" metadata must be a JSON object`);
|
|
492
|
+
}
|
|
493
|
+
if (this.planes !== null) {
|
|
494
|
+
const parsedPlanes = parsePlanes(this.planes);
|
|
495
|
+
if (!parsedPlanes || parsedPlanes.length === 0) throw new Error(`Playbook "${this.key}" planes must be a non-empty JSON array of plane names`);
|
|
496
|
+
}
|
|
497
|
+
if (this.onStepFailure !== null && this.onStepFailure !== "abort" && this.onStepFailure !== "continue") throw new Error(`Playbook "${this.key}" onStepFailure must be "abort" or "continue"`);
|
|
498
|
+
const lowerLayers = await this.getLowerPrecedenceLayers();
|
|
499
|
+
const currentLayer = this.toPlaybookLayer();
|
|
500
|
+
const lowerMerged = mergePlaybookLayers(definition, ...lowerLayers);
|
|
501
|
+
if (this.enabled === true && !lowerMerged.enabled) throw new Error(`Playbook "${this.key}" is disabled by a lower layer and cannot be re-enabled by an override`);
|
|
502
|
+
if (currentLayer.planes) {
|
|
503
|
+
const widened = currentLayer.planes.filter((plane) => !lowerMerged.planes.includes(plane));
|
|
504
|
+
if (widened.length > 0) throw new Error(`Playbook "${this.key}" cannot add plane(s) ${widened.join(", ")} that no lower layer declares`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async getLowerPrecedenceLayers() {
|
|
508
|
+
const layers = [normalizePlaybookLayer(getPlaybookConfig$1().playbooks?.[this.key], `Playbook "${this.key}" config override`)];
|
|
509
|
+
const { PlaybookOverrideCollection } = await Promise.resolve().then(() => PlaybookOverrideCollection_exports);
|
|
510
|
+
const collection = await PlaybookOverrideCollection.create({ db: this.options.db ?? this.options.persistence });
|
|
511
|
+
if (this.tenantId) {
|
|
512
|
+
const appOverride = await collection.getAppOverride(this.key, { excludeId: this.id ?? void 0 });
|
|
513
|
+
if (appOverride) layers.push(appOverride.toPlaybookLayer());
|
|
514
|
+
}
|
|
515
|
+
return layers;
|
|
516
|
+
}
|
|
517
|
+
normalizeForPersistence() {
|
|
518
|
+
const rawMetadata = this.metadata;
|
|
519
|
+
if (rawMetadata === void 0) this.metadata = null;
|
|
520
|
+
else if (rawMetadata !== null && typeof rawMetadata === "object" && !Array.isArray(rawMetadata)) this.metadata = serializeMetadata(rawMetadata);
|
|
521
|
+
else if (rawMetadata !== null && typeof rawMetadata !== "string") throw new Error(`Playbook "${this.key}" metadata must be a JSON object or JSON string`);
|
|
522
|
+
const rawPlanes = this.planes;
|
|
523
|
+
if (rawPlanes === void 0) this.planes = null;
|
|
524
|
+
else if (Array.isArray(rawPlanes)) this.planes = serializePlanes(rawPlanes);
|
|
525
|
+
else if (rawPlanes !== null && typeof rawPlanes !== "string") throw new Error(`Playbook "${this.key}" planes must be an array or JSON string`);
|
|
526
|
+
if (this.enabled === void 0) this.enabled = null;
|
|
527
|
+
else if (this.enabled !== null && typeof this.enabled !== "boolean") throw new Error(`Playbook "${this.key}" enabled must be a boolean or null, received ${typeof this.enabled} "${String(this.enabled)}"`);
|
|
528
|
+
}
|
|
529
|
+
async getPersistedIdentity() {
|
|
530
|
+
if (!this.id) return null;
|
|
531
|
+
const existing = await this.db.get(this.tableName, { id: this.id });
|
|
532
|
+
if (!existing) return null;
|
|
533
|
+
const row = existing;
|
|
534
|
+
return {
|
|
535
|
+
key: String(row.key ?? this.key),
|
|
536
|
+
tenantId: row.tenantId !== void 0 ? row.tenantId : row.tenant_id ?? null
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
__decorateClass([field({ required: true })], PlaybookOverride.prototype, "key", 2);
|
|
541
|
+
__decorateClass([field({
|
|
542
|
+
type: "text",
|
|
543
|
+
nullable: true
|
|
544
|
+
})], PlaybookOverride.prototype, "tenantId", 2);
|
|
545
|
+
__decorateClass([field({
|
|
546
|
+
type: "text",
|
|
547
|
+
nullable: true
|
|
548
|
+
})], PlaybookOverride.prototype, "title", 2);
|
|
549
|
+
__decorateClass([field({
|
|
550
|
+
type: "text",
|
|
551
|
+
nullable: true
|
|
552
|
+
})], PlaybookOverride.prototype, "description", 2);
|
|
553
|
+
__decorateClass([field({
|
|
554
|
+
type: "text",
|
|
555
|
+
nullable: true
|
|
556
|
+
})], PlaybookOverride.prototype, "planes", 2);
|
|
557
|
+
__decorateClass([field({
|
|
558
|
+
type: "text",
|
|
559
|
+
nullable: true
|
|
560
|
+
})], PlaybookOverride.prototype, "onStepFailure", 2);
|
|
561
|
+
__decorateClass([field({
|
|
562
|
+
type: "boolean",
|
|
563
|
+
nullable: true
|
|
564
|
+
})], PlaybookOverride.prototype, "enabled", 2);
|
|
565
|
+
__decorateClass([field({
|
|
566
|
+
type: "text",
|
|
567
|
+
nullable: true
|
|
568
|
+
})], PlaybookOverride.prototype, "metadata", 2);
|
|
569
|
+
PlaybookOverride = __decorateClass([smrt({
|
|
570
|
+
tableName: "_smrt_playbook_overrides",
|
|
571
|
+
conflictColumns: ["key", "context"],
|
|
572
|
+
api: { include: [
|
|
573
|
+
"list",
|
|
574
|
+
"get",
|
|
575
|
+
"create",
|
|
576
|
+
"update",
|
|
577
|
+
"delete"
|
|
578
|
+
] },
|
|
579
|
+
cli: {
|
|
580
|
+
include: [
|
|
581
|
+
"list",
|
|
582
|
+
"get",
|
|
583
|
+
"create",
|
|
584
|
+
"update",
|
|
585
|
+
"delete"
|
|
586
|
+
],
|
|
587
|
+
exclude: [
|
|
588
|
+
"getMetadata",
|
|
589
|
+
"setMetadata",
|
|
590
|
+
"getPlanes",
|
|
591
|
+
"setPlanes",
|
|
592
|
+
"toPlaybookLayer"
|
|
593
|
+
]
|
|
594
|
+
},
|
|
595
|
+
mcp: { include: [] }
|
|
596
|
+
})], PlaybookOverride);
|
|
597
|
+
//#endregion
|
|
598
|
+
//#region src/collections/PlaybookOverrideCollection.ts
|
|
599
|
+
var PlaybookOverrideCollection_exports = /* @__PURE__ */ __exportAll({ PlaybookOverrideCollection: () => PlaybookOverrideCollection });
|
|
600
|
+
var PlaybookOverrideCollection = class extends SmrtCollection {
|
|
601
|
+
static _itemClass = PlaybookOverride;
|
|
602
|
+
excludeOverrideId(items, excludeId) {
|
|
603
|
+
return items.filter((item) => excludeId ? item.id !== excludeId : true);
|
|
604
|
+
}
|
|
605
|
+
async getAppOverride(key, options = {}) {
|
|
606
|
+
const items = await this.list({ where: {
|
|
607
|
+
key,
|
|
608
|
+
tenantId: null
|
|
609
|
+
} });
|
|
610
|
+
return this.excludeOverrideId(items, options.excludeId)[0] ?? null;
|
|
611
|
+
}
|
|
612
|
+
async getTenantOverride(key, tenantId, options = {}) {
|
|
613
|
+
const items = await this.list({ where: {
|
|
614
|
+
key,
|
|
615
|
+
tenantId
|
|
616
|
+
} });
|
|
617
|
+
return this.excludeOverrideId(items, options.excludeId)[0] ?? null;
|
|
618
|
+
}
|
|
619
|
+
async getResolutionLayers(key, tenantId, options = {}) {
|
|
620
|
+
const [app, tenant] = await Promise.all([this.getAppOverride(key, options), tenantId != null ? this.getTenantOverride(key, tenantId, options) : Promise.resolve(null)]);
|
|
621
|
+
return {
|
|
622
|
+
app,
|
|
623
|
+
tenant
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region src/playbook-resolver.ts
|
|
629
|
+
var DEFAULT_INTENT_PLANES = Object.freeze(["browser"]);
|
|
630
|
+
function getPlaybookConfig() {
|
|
631
|
+
return getPackageConfig("playbooks", { playbooks: {} });
|
|
632
|
+
}
|
|
633
|
+
function reject(key, reason, message, stepIndex) {
|
|
634
|
+
return {
|
|
635
|
+
ok: false,
|
|
636
|
+
reason,
|
|
637
|
+
message,
|
|
638
|
+
key,
|
|
639
|
+
...stepIndex === void 0 ? {} : { stepIndex }
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
async function loadPlaybookBase(definition, options) {
|
|
643
|
+
const key = definition.key;
|
|
644
|
+
const tenantId = options.tenantId !== void 0 ? options.tenantId : getTenantId() ?? null;
|
|
645
|
+
let collection = null;
|
|
646
|
+
let cacheDb = options.db;
|
|
647
|
+
if (options.db) {
|
|
648
|
+
const initializedCollection = await PlaybookOverrideCollection.create({ db: options.db });
|
|
649
|
+
collection = initializedCollection;
|
|
650
|
+
cacheDb = initializedCollection.db;
|
|
651
|
+
}
|
|
652
|
+
const cached = getCachedPlaybookBase(key, tenantId, cacheDb);
|
|
653
|
+
if (cached) return cached;
|
|
654
|
+
const loadedAtGeneration = getPlaybookCacheGeneration(key, cacheDb);
|
|
655
|
+
const layers = [normalizePlaybookLayer(getPlaybookConfig().playbooks?.[key], `Playbook "${key}" config override`)];
|
|
656
|
+
if (collection) {
|
|
657
|
+
const stored = await collection.getResolutionLayers(key, tenantId);
|
|
658
|
+
if (stored.app) layers.push(stored.app.toPlaybookLayer());
|
|
659
|
+
if (stored.tenant) layers.push(stored.tenant.toPlaybookLayer());
|
|
660
|
+
}
|
|
661
|
+
const merged = mergePlaybookLayers(definition, ...layers);
|
|
662
|
+
const value = Object.freeze({
|
|
663
|
+
key,
|
|
664
|
+
title: merged.title,
|
|
665
|
+
description: merged.description,
|
|
666
|
+
planes: merged.planes,
|
|
667
|
+
onStepFailure: merged.onStepFailure,
|
|
668
|
+
enabled: merged.enabled,
|
|
669
|
+
metadata: merged.metadata
|
|
670
|
+
});
|
|
671
|
+
setCachedPlaybookBase(key, tenantId, cacheDb, value, loadedAtGeneration);
|
|
672
|
+
return value;
|
|
673
|
+
}
|
|
674
|
+
async function resolvePlaybook(key, options = {}) {
|
|
675
|
+
const definition = PlaybookRegistry.get(key);
|
|
676
|
+
if (!definition) return reject(key, "unknown-playbook", `Unknown playbook key "${key}"`);
|
|
677
|
+
const base = await loadPlaybookBase(definition, options);
|
|
678
|
+
const runtimeOverride = normalizePlaybookLayer(options.override, `Playbook "${key}" runtime override`);
|
|
679
|
+
const merged = mergePlaybookLayers({
|
|
680
|
+
...definition,
|
|
681
|
+
title: base.title,
|
|
682
|
+
description: base.description,
|
|
683
|
+
planes: base.planes,
|
|
684
|
+
onStepFailure: base.onStepFailure,
|
|
685
|
+
enabled: base.enabled,
|
|
686
|
+
metadata: base.metadata
|
|
687
|
+
}, runtimeOverride);
|
|
688
|
+
if (!merged.enabled) return reject(key, "disabled", `Playbook "${key}" is disabled for this scope`);
|
|
689
|
+
const plane = options.plane ?? "server";
|
|
690
|
+
if (!merged.planes.includes(plane)) return reject(key, "plane-not-declared", `Playbook "${key}" is not valid on the "${plane}" plane; it declares ${merged.planes.join(", ")}`);
|
|
691
|
+
const steps = [];
|
|
692
|
+
for (const [index, step] of definition.steps.entries()) {
|
|
693
|
+
if (step.kind === "operation") {
|
|
694
|
+
const declaration = options.classifier?.(step) ?? null;
|
|
695
|
+
steps.push({
|
|
696
|
+
index,
|
|
697
|
+
step,
|
|
698
|
+
classification: applyCapabilityDeclaration(declaration),
|
|
699
|
+
classificationDeclared: isCapabilityDeclarationComplete(declaration)
|
|
700
|
+
});
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (!options.intents) return reject(key, "intent-registry-unavailable", `Playbook "${key}" step ${index} references view intent "${step.id}", but no intent registry was supplied (see #2588)`, index);
|
|
704
|
+
const record = options.intents(step.id);
|
|
705
|
+
if (!record) return reject(key, "unknown-intent", `Playbook "${key}" step ${index} references unknown view intent "${step.id}"`, index);
|
|
706
|
+
if (!(record.planes ?? DEFAULT_INTENT_PLANES).includes(plane)) return reject(key, "intent-plane-not-declared", `Playbook "${key}" step ${index} references view intent "${step.id}", which is not valid on the "${plane}" plane`, index);
|
|
707
|
+
steps.push({
|
|
708
|
+
index,
|
|
709
|
+
step,
|
|
710
|
+
classification: applyCapabilityDeclaration(record.classification),
|
|
711
|
+
classificationDeclared: isCapabilityDeclarationComplete(record.classification)
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
ok: true,
|
|
716
|
+
plan: Object.freeze({
|
|
717
|
+
key,
|
|
718
|
+
title: merged.title,
|
|
719
|
+
description: merged.description,
|
|
720
|
+
plane,
|
|
721
|
+
planes: merged.planes,
|
|
722
|
+
onStepFailure: merged.onStepFailure,
|
|
723
|
+
metadata: merged.metadata,
|
|
724
|
+
steps: Object.freeze(steps)
|
|
725
|
+
})
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
//#endregion
|
|
729
|
+
//#region src/index.ts
|
|
730
|
+
var PACKAGE_VERSION_INITIALIZED = true;
|
|
731
|
+
//#endregion
|
|
732
|
+
export { FAIL_CLOSED_CLASSIFICATION, PACKAGE_VERSION_INITIALIZED, PLAYBOOK_PLANES, PlaybookOverride, PlaybookOverrideCollection, PlaybookRegistry, clearPlaybookCache, definePlaybook, getPlaybookCacheTtlMs, normalizeEditableConfig, resolvePlaybook };
|
|
733
|
+
|
|
734
|
+
//# sourceMappingURL=index.js.map
|