@workflow-code/cli 0.2.4-20261011005.codex-cli-web → 0.2.4-20261011007.codex-cli-web

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.
@@ -0,0 +1,908 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ WORKFLOW_RELATED_PROJECT_ALIAS_PATTERN,
4
+ WORKFLOW_RELATED_PROJECT_KEY_LIMIT
5
+ } from "./chunk-P6K3NB5S.js";
6
+
7
+ // .shared/kanban-project/revision-stream.ts
8
+ async function consumeKanbanRelatedProjectRevisionStream(stream, callbacks, signal) {
9
+ const reader = stream.getReader();
10
+ const decoder = new TextDecoder();
11
+ let buffer = "";
12
+ let aborted = signal?.aborted === true;
13
+ const cancelReader = () => {
14
+ aborted = true;
15
+ void reader.cancel(signal?.reason).catch(() => void 0);
16
+ };
17
+ if (signal?.aborted) cancelReader();
18
+ else signal?.addEventListener("abort", cancelReader, { once: true });
19
+ try {
20
+ while (!aborted) {
21
+ const result = await reader.read();
22
+ buffer += decoder.decode(result.value, { stream: !result.done });
23
+ let boundary = findSseBoundary(buffer);
24
+ while (boundary !== void 0) {
25
+ const block = buffer.slice(0, boundary.index);
26
+ buffer = buffer.slice(boundary.index + boundary.length);
27
+ dispatchKanbanRevisionEvent(block, callbacks);
28
+ boundary = findSseBoundary(buffer);
29
+ }
30
+ if (result.done) break;
31
+ }
32
+ if (!aborted && buffer.trim() !== "") {
33
+ dispatchKanbanRevisionEvent(buffer, callbacks);
34
+ }
35
+ } finally {
36
+ signal?.removeEventListener("abort", cancelReader);
37
+ if (aborted) await reader.cancel(signal?.reason).catch(() => void 0);
38
+ reader.releaseLock();
39
+ }
40
+ }
41
+ function findSseBoundary(value) {
42
+ const lf = value.indexOf("\n\n");
43
+ const crlf = value.indexOf("\r\n\r\n");
44
+ if (lf < 0 && crlf < 0) return void 0;
45
+ if (crlf >= 0 && (lf < 0 || crlf < lf)) return { index: crlf, length: 4 };
46
+ return { index: lf, length: 2 };
47
+ }
48
+ function dispatchKanbanRevisionEvent(block, callbacks) {
49
+ let event = "message";
50
+ const data = [];
51
+ for (const line of block.split(/\r?\n/)) {
52
+ if (line.startsWith("event:")) event = line.slice("event:".length).trim();
53
+ if (line.startsWith("data:")) data.push(line.slice("data:".length).trimStart());
54
+ }
55
+ if (data.length === 0 || event === "heartbeat") return;
56
+ if (event === "unavailable") {
57
+ callbacks.onUnavailable?.();
58
+ return;
59
+ }
60
+ const value = JSON.parse(data.join("\n"));
61
+ if (event === "revision") {
62
+ const revision = readKanbanRevision(value);
63
+ callbacks.onRevision?.(revision);
64
+ return;
65
+ }
66
+ if (event === "snapshot") {
67
+ if (!isRecord(value) || typeof value["resolvedTarget"] !== "string" || !Array.isArray(value["revisions"])) {
68
+ throw new Error("Kanban related-project revision snapshot is invalid.");
69
+ }
70
+ const snapshot = {
71
+ resolvedTarget: value["resolvedTarget"],
72
+ revisions: value["revisions"].map(readKanbanRevision)
73
+ };
74
+ callbacks.onSnapshot?.(snapshot);
75
+ }
76
+ }
77
+ function readKanbanRevision(value) {
78
+ if (!isRecord(value) || typeof value["alias"] !== "string" || typeof value["revision"] !== "string" || value["alias"].trim() === "" || value["revision"].trim() === "") throw new Error("Kanban related-project revision event is invalid.");
79
+ return { alias: value["alias"], revision: value["revision"] };
80
+ }
81
+ function isRecord(value) {
82
+ return value !== null && typeof value === "object" && !Array.isArray(value);
83
+ }
84
+
85
+ // .shared/kanban-project/methods.ts
86
+ var KANBAN_METHOD_CATALOG_MESSAGE = "workflow-code:kanban-method-catalog";
87
+ var KANBAN_METHOD_INVOKE_MESSAGE = "workflow-code:kanban-method-invoke";
88
+ var KANBAN_METHOD_RESULT_MESSAGE = "workflow-code:kanban-method-result";
89
+ var KANBAN_METHOD_CANCEL_MESSAGE = "workflow-code:kanban-method-cancel";
90
+ var KANBAN_METHOD_MAX_JSON_CHARS = 65536;
91
+ var KANBAN_METHOD_TIMEOUT_MS = 15e3;
92
+ var MAX_METHODS = 32;
93
+ function parseKanbanMethodInvocation(value) {
94
+ if (!isRecord2(value) || Object.keys(value).some((key) => !["instanceId", "configurationId", "method", "args"].includes(key))) {
95
+ throw methodError("KANBAN_METHOD_INVALID_REQUEST", "Invalid Kanban method invocation.");
96
+ }
97
+ if (typeof value.instanceId !== "string" || !value.instanceId || value.instanceId.length > 200 || typeof value.configurationId !== "string" || value.configurationId.length > 200 || typeof value.method !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.-]{0,79}$/.test(value.method) || !isRecord2(value.args)) {
98
+ throw methodError("KANBAN_METHOD_INVALID_REQUEST", "An instanceId, configurationId, method and JSON args object are required.");
99
+ }
100
+ assertKanbanMethodJson(value.args);
101
+ return value;
102
+ }
103
+ function assertKanbanMethodJson(value) {
104
+ const seen = /* @__PURE__ */ new Set();
105
+ const visit = (node, depth) => {
106
+ if (depth > 32) return false;
107
+ if (node === null || typeof node === "string" || typeof node === "boolean") return true;
108
+ if (typeof node === "number") return Number.isFinite(node);
109
+ if (typeof node !== "object" || seen.has(node)) return false;
110
+ seen.add(node);
111
+ const valid = Array.isArray(node) ? node.every((item) => visit(item, depth + 1)) : Object.getPrototypeOf(node) === Object.prototype && Object.values(node).every((item) => visit(item, depth + 1));
112
+ seen.delete(node);
113
+ return valid;
114
+ };
115
+ if (!visit(value, 0) || JSON.stringify(value).length > KANBAN_METHOD_MAX_JSON_CHARS) {
116
+ throw methodError("KANBAN_METHOD_INVALID_JSON", "Kanban method data must be JSON, at most 32 levels deep and 64 Ki characters.");
117
+ }
118
+ }
119
+ function parseKanbanMethodDefinitions(value) {
120
+ assertKanbanMethodJson(value);
121
+ if (!Array.isArray(value) || value.length > MAX_METHODS) throw methodError("KANBAN_METHOD_INVALID_CATALOG", "Invalid Kanban method catalog.");
122
+ const names = /* @__PURE__ */ new Set();
123
+ for (const item of value) {
124
+ if (!isRecord2(item) || Object.keys(item).some((key) => !["name", "description", "inputSchema", "readOnly"].includes(key)) || typeof item.name !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.-]{0,79}$/.test(item.name) || names.has(item.name) || typeof item.description !== "string" || !item.description.trim() || item.description.length > 2e3 || !isRecord2(item.inputSchema) || item.inputSchema.type !== "object" || JSON.stringify(item.inputSchema).length > 8192 || item.readOnly !== void 0 && typeof item.readOnly !== "boolean") {
125
+ throw methodError("KANBAN_METHOD_INVALID_CATALOG", "Invalid Kanban method definition.");
126
+ }
127
+ names.add(item.name);
128
+ }
129
+ return JSON.parse(JSON.stringify(value));
130
+ }
131
+ function methodError(code, message) {
132
+ return Object.assign(new Error(message), { code });
133
+ }
134
+ function isRecord2(value) {
135
+ return typeof value === "object" && value !== null && !Array.isArray(value);
136
+ }
137
+ function createKanbanMethodsBridgeScript(nonce) {
138
+ const safeNonce = JSON.stringify(nonce);
139
+ return `
140
+ const registeredMethods=new Map();
141
+ const methodDocumentId=Date.now().toString(36)+"-"+Math.random().toString(36).slice(2);
142
+ const activeMethodCalls=new Map();
143
+ const seenMethodCalls=new Set();
144
+ let methodRevision=0;
145
+ const methodPost=(message)=>window.parent.postMessage({...message,nonce:${safeNonce}},"*");
146
+ const methodDepth=(value,depth=0)=>depth<=32&&(value===null||typeof value!=="object"||Object.values(value).every((item)=>methodDepth(item,depth+1)));
147
+ const methodJson=(value)=>{try{if(!isJsonValue(value)||!methodDepth(value)||JSON.stringify(value).length>${KANBAN_METHOD_MAX_JSON_CHARS})throw invalidRequest();return JSON.parse(JSON.stringify(value));}catch{throw createError("Kanban method data must be bounded JSON.","KANBAN_METHOD_INVALID_JSON");}};
148
+ const publishKanbanMethods=()=>methodPost({type:${JSON.stringify(KANBAN_METHOD_CATALOG_MESSAGE)},documentId:methodDocumentId,configurationId:current.id,revision:++methodRevision,methods:[...registeredMethods.values()].map((entry)=>entry.definition)});
149
+ const finishMethodCall=(requestId,response)=>{
150
+ const entry=activeMethodCalls.get(requestId);if(!entry)return;
151
+ activeMethodCalls.delete(requestId);window.clearTimeout(entry.timer);
152
+ methodPost({type:${JSON.stringify(KANBAN_METHOD_RESULT_MESSAGE)},requestId,...response});
153
+ };
154
+ const abortMethodCall=(requestId,code,message)=>{const entry=activeMethodCalls.get(requestId);if(!entry)return;entry.controller.abort();finishMethodCall(requestId,{ok:false,error:{code,message}});};
155
+ const resetKanbanMethods=()=>{registeredMethods.clear();for(const id of activeMethodCalls.keys())abortMethodCall(id,"KANBAN_METHOD_STALE","The Kanban configuration changed.");publishKanbanMethods();};
156
+ const methods=Object.freeze({register:(definition,handler)=>{
157
+ const clean=methodJson(definition);
158
+ if(!clean||typeof clean!=="object"||Array.isArray(clean)||Object.keys(clean).some((key)=>!["name","description","inputSchema","readOnly"].includes(key))||typeof clean.name!=="string"||!/^[a-zA-Z][a-zA-Z0-9_.-]{0,79}$/.test(clean.name)||typeof clean.description!=="string"||!clean.description.trim()||clean.description.length>2000||!clean.inputSchema||clean.inputSchema.type!=="object"||JSON.stringify(clean.inputSchema).length>8192||(clean.readOnly!==undefined&&typeof clean.readOnly!=="boolean")||typeof handler!=="function"||registeredMethods.has(clean.name)||registeredMethods.size>=${MAX_METHODS})throw invalidRequest();
159
+ if(JSON.stringify([...registeredMethods.values()].map((entry)=>entry.definition).concat(clean)).length>${KANBAN_METHOD_MAX_JSON_CHARS})throw invalidRequest();
160
+ const entry={definition:clean,handler};registeredMethods.set(clean.name,entry);publishKanbanMethods();
161
+ return ()=>{if(registeredMethods.get(clean.name)!==entry)return;registeredMethods.delete(clean.name);for(const [id,call] of activeMethodCalls)if(call.method===clean.name)abortMethodCall(id,"KANBAN_METHOD_UNAVAILABLE","The Kanban method was unregistered.");publishKanbanMethods();};
162
+ }});
163
+ const handleKanbanMethodMessage=(data)=>{
164
+ if(data.type===${JSON.stringify(KANBAN_METHOD_CANCEL_MESSAGE)}){if(typeof data.requestId==="string")abortMethodCall(data.requestId,"KANBAN_METHOD_CANCELLED","Kanban method call was cancelled.");return true;}
165
+ if(data.type!==${JSON.stringify(KANBAN_METHOD_INVOKE_MESSAGE)})return false;
166
+ const requestId=data.requestId;if(typeof requestId!=="string"||!requestId||requestId.length>200||seenMethodCalls.has(requestId))return true;
167
+ seenMethodCalls.add(requestId);if(seenMethodCalls.size>128)seenMethodCalls.delete(seenMethodCalls.values().next().value);
168
+ const fail=(code,message)=>methodPost({type:${JSON.stringify(KANBAN_METHOD_RESULT_MESSAGE)},requestId,ok:false,error:{code,message}});
169
+ if(data.documentId!==methodDocumentId||data.configurationId!==current.id||data.revision!==methodRevision){fail("KANBAN_METHOD_STALE","The Kanban document, configuration or methods changed. List instances again.");return true;}
170
+ const method=registeredMethods.get(data.method);if(!method){fail("KANBAN_METHOD_UNAVAILABLE","The Kanban method is unavailable.");return true;}
171
+ if(activeMethodCalls.size>=4){fail("KANBAN_METHOD_BUSY","Too many Kanban method calls are pending.");return true;}
172
+ let input;try{input=methodJson(data.input);if(!input||typeof input!=="object"||Array.isArray(input))throw invalidRequest();}catch{fail("KANBAN_METHOD_INVALID_REQUEST","Kanban method input must be a JSON object.");return true;}
173
+ const controller=new AbortController();const timer=window.setTimeout(()=>abortMethodCall(requestId,"KANBAN_METHOD_TIMEOUT","Kanban method call timed out; inspect state before retrying."),${KANBAN_METHOD_TIMEOUT_MS});
174
+ activeMethodCalls.set(requestId,{controller,timer,method:data.method});
175
+ const context={signal:controller.signal,configuration:clone(),presentation:{...presentation}};
176
+ Promise.resolve().then(()=>{if(controller.signal.aborted)throw createError("Cancelled","KANBAN_METHOD_CANCELLED");return method.handler(input,context);})
177
+ .then((value)=>finishMethodCall(requestId,{ok:true,value:methodJson(value)}))
178
+ .catch((error)=>finishMethodCall(requestId,{ok:false,error:{code:"KANBAN_METHOD_FAILED",message:typeof error?.message==="string"?error.message.slice(0,2000):"Kanban method failed."}}));
179
+ return true;
180
+ };
181
+ window.addEventListener("pagehide",()=>{for(const id of activeMethodCalls.keys())abortMethodCall(id,"KANBAN_METHOD_INSTANCE_UNAVAILABLE","The Kanban page closed.");registeredMethods.clear();publishKanbanMethods();});
182
+ `;
183
+ }
184
+
185
+ // .shared/kanban-project/index.ts
186
+ var KANBAN_MANIFEST_FILE = "kanban.json";
187
+ var KANBAN_ENTRY_FILE = "index.html";
188
+ var KANBAN_ARTIFACT_DIR = ".";
189
+ var KANBAN_MANIFEST_VERSION = 1;
190
+ var KANBAN_CONFIGURATION_CHANGE_EVENT = "workflow-code:kanban-configuration-change";
191
+ var KANBAN_PRESENTATION_CHANGE_EVENT = "workflow-code:kanban-presentation-change";
192
+ var KANBAN_PRESENTATION_MESSAGE = "workflow-code:kanban-presentation";
193
+ var KANBAN_PREVIEW_READY_MESSAGE = "workflow-code:kanban-ready";
194
+ var KANBAN_BRIDGE_REQUEST_MESSAGE = "workflow-code:kanban-bridge-request";
195
+ var KANBAN_BRIDGE_RESPONSE_MESSAGE = "workflow-code:kanban-bridge-response";
196
+ var KANBAN_RELATED_PROJECT_REVISION_MESSAGE = "workflow-code:kanban-related-project-revision";
197
+ var KANBAN_BRIDGE_REQUEST_TIMEOUT_MS = 15e3;
198
+ var KANBAN_FILE_BRIDGE_REQUEST_TIMEOUT_MS = 12e4;
199
+ var KANBAN_FILE_MAX_BYTES = 50 * 1024 * 1024;
200
+ var KANBAN_BRIDGE_MAX_PENDING_REQUESTS = 16;
201
+ var KANBAN_BRIDGE_SAFE_ERRORS = {
202
+ KANBAN_BRIDGE_INVALID_REQUEST: "The Kanban bridge request is invalid.",
203
+ KANBAN_BRIDGE_BUSY: "Too many Kanban bridge requests are pending.",
204
+ KANBAN_BRIDGE_TIMEOUT: "Kanban bridge request timed out.",
205
+ KANBAN_BRIDGE_UNAVAILABLE: "Kanban project data is unavailable in this host.",
206
+ KANBAN_BRIDGE_SESSION_INVALID: "The Kanban preview session is invalid or expired.",
207
+ KANBAN_BRIDGE_SERVER_ERROR: "Kanban project data operation failed.",
208
+ KANBAN_DATA_OPERATION_FAILED: "Kanban project data operation failed.",
209
+ KANBAN_RELATED_PROJECT_UNAVAILABLE: "Kanban related project is unavailable.",
210
+ KANBAN_FILE_UNAVAILABLE: "Kanban file is unavailable in this project.",
211
+ KANBAN_FILE_TOO_LARGE: "Kanban files cannot exceed 50 MiB."
212
+ };
213
+ var KANBAN_BRIDGE_CALL_FIELDS = /* @__PURE__ */ new Set(["requestId", "operation", "input"]);
214
+ var MAX_KNOWLEDGE_DOCUMENT_PAGE_SIZE = 100;
215
+ var MAX_KNOWLEDGE_SEARCH_PAGE_SIZE = 1e3;
216
+ var MAX_KNOWLEDGE_CONTEXT_LINES = 50;
217
+ var MAX_KNOWLEDGE_LINE_RANGE = 1e3;
218
+ var KANBAN_BRIDGE_INPUT_FIELDS = {
219
+ "runtime.getLocale": [],
220
+ "files.createFile": ["name", "mimeType", "base64"],
221
+ "files.readFile": ["fileId"],
222
+ "files.deleteFile": ["fileId"],
223
+ "kv.getValue": ["key"],
224
+ "kv.setValue": ["key", "value"],
225
+ "relatedProject.kv.getValue": ["alias", "key"],
226
+ "relatedProject.kv.setValue": ["alias", "key", "value"],
227
+ "knowledge.listDocuments": ["pageSize", "cursor"],
228
+ "knowledge.getDocument": ["documentId"],
229
+ "knowledge.createDocument": ["id", "title", "markdown"],
230
+ "knowledge.editDocument": ["documentId", "title", "markdown"],
231
+ "knowledge.deleteDocument": ["documentId"],
232
+ "knowledge.searchDocuments": [
233
+ "query",
234
+ "documentId",
235
+ "beforeLines",
236
+ "afterLines",
237
+ "maxMatches",
238
+ "pageSize",
239
+ "cursor"
240
+ ],
241
+ "knowledge.readDocumentLines": ["documentId", "startLine", "endLine"]
242
+ };
243
+ function readPackageProjectType(value) {
244
+ if (!isRecord3(value)) return void 0;
245
+ const workflowCode = value["workflowCode"];
246
+ if (!isRecord3(workflowCode)) return void 0;
247
+ return workflowCode["projectType"] === "kanban" ? "kanban" : void 0;
248
+ }
249
+ function readKanbanArtifactConfig(value) {
250
+ if (!isRecord3(value)) {
251
+ throw new Error("package.json root must be an object.");
252
+ }
253
+ const workflowCode = value["workflowCode"];
254
+ if (!isRecord3(workflowCode)) {
255
+ throw new Error("package.json workflowCode must be an object for a Kanban project.");
256
+ }
257
+ const rawConfig = workflowCode["kanban"];
258
+ if (rawConfig !== void 0 && !isRecord3(rawConfig)) {
259
+ throw new Error("package.json workflowCode.kanban must be an object.");
260
+ }
261
+ if (rawConfig?.["dataSources"] !== void 0) {
262
+ throw new Error(
263
+ "package.json workflowCode.kanban.dataSources was removed; declare workflowCode.projectInfo.relatedProjects instead."
264
+ );
265
+ }
266
+ return {
267
+ artifactDir: readKanbanRelativePath(
268
+ rawConfig?.["artifactDir"],
269
+ "workflowCode.kanban.artifactDir",
270
+ KANBAN_ARTIFACT_DIR,
271
+ true
272
+ ),
273
+ entry: readKanbanRelativePath(
274
+ rawConfig?.["entry"],
275
+ "workflowCode.kanban.entry",
276
+ KANBAN_ENTRY_FILE,
277
+ false
278
+ )
279
+ };
280
+ }
281
+ function createKanbanArtifactEntryPath(config) {
282
+ return config.artifactDir === KANBAN_ARTIFACT_DIR ? config.entry : `${config.artifactDir}/${config.entry}`;
283
+ }
284
+ function encodeKanbanAssetUrlPath(filePath) {
285
+ return filePath.split("/").map((part) => encodeURIComponent(part)).join("/");
286
+ }
287
+ function readKanbanAssetMimeType(filePath) {
288
+ const normalized = filePath.split(/[?#]/, 1)[0]?.toLowerCase() ?? "";
289
+ const extension = normalized.includes(".") ? `.${normalized.split(".").at(-1)}` : "";
290
+ switch (extension) {
291
+ case ".html":
292
+ case ".htm":
293
+ return "text/html; charset=utf-8";
294
+ case ".css":
295
+ return "text/css; charset=utf-8";
296
+ case ".js":
297
+ case ".mjs":
298
+ return "text/javascript; charset=utf-8";
299
+ case ".json":
300
+ case ".map":
301
+ return "application/json; charset=utf-8";
302
+ case ".webmanifest":
303
+ return "application/manifest+json; charset=utf-8";
304
+ case ".svg":
305
+ return "image/svg+xml";
306
+ case ".png":
307
+ return "image/png";
308
+ case ".jpg":
309
+ case ".jpeg":
310
+ return "image/jpeg";
311
+ case ".gif":
312
+ return "image/gif";
313
+ case ".webp":
314
+ return "image/webp";
315
+ case ".avif":
316
+ return "image/avif";
317
+ case ".ico":
318
+ return "image/x-icon";
319
+ case ".woff":
320
+ return "font/woff";
321
+ case ".woff2":
322
+ return "font/woff2";
323
+ case ".ttf":
324
+ return "font/ttf";
325
+ case ".otf":
326
+ return "font/otf";
327
+ case ".eot":
328
+ return "application/vnd.ms-fontobject";
329
+ case ".wasm":
330
+ return "application/wasm";
331
+ case ".mp3":
332
+ return "audio/mpeg";
333
+ case ".ogg":
334
+ return "audio/ogg";
335
+ case ".wav":
336
+ return "audio/wav";
337
+ case ".mp4":
338
+ return "video/mp4";
339
+ case ".webm":
340
+ return "video/webm";
341
+ case ".txt":
342
+ return "text/plain; charset=utf-8";
343
+ case ".xml":
344
+ return "application/xml; charset=utf-8";
345
+ case ".pdf":
346
+ return "application/pdf";
347
+ default:
348
+ return "application/octet-stream";
349
+ }
350
+ }
351
+ function parseKanbanManifest(value) {
352
+ if (!isRecord3(value)) {
353
+ throw new Error("kanban.json root must be an object.");
354
+ }
355
+ if (value["version"] !== KANBAN_MANIFEST_VERSION) {
356
+ throw new Error(`kanban.json version must be ${KANBAN_MANIFEST_VERSION}.`);
357
+ }
358
+ const rawParams = value["params"];
359
+ if (!Array.isArray(rawParams)) {
360
+ throw new Error("kanban.json params must be an array.");
361
+ }
362
+ const names = /* @__PURE__ */ new Set();
363
+ const params = rawParams.map((param, index) => parseParam(param, index, names));
364
+ return { version: KANBAN_MANIFEST_VERSION, params };
365
+ }
366
+ function parseKanbanManifestText(content) {
367
+ try {
368
+ return parseKanbanManifest(JSON.parse(content));
369
+ } catch (error) {
370
+ if (error instanceof SyntaxError) {
371
+ throw new Error("kanban.json must contain valid JSON.");
372
+ }
373
+ throw error;
374
+ }
375
+ }
376
+ function createDefaultKanbanValues(params) {
377
+ return Object.fromEntries(params.map((param) => [param.name, param.defaultValue ?? ""]));
378
+ }
379
+ function sanitizeKanbanValues(params, values) {
380
+ const next = {};
381
+ for (const param of params) {
382
+ const value = values[param.name];
383
+ next[param.name] = typeof value === "string" ? value : param.defaultValue ?? "";
384
+ }
385
+ return next;
386
+ }
387
+ function createInjectedKanbanParams(params, values) {
388
+ return Object.fromEntries(params.map((param) => [
389
+ param.name,
390
+ parseInjectedValue(param, values[param.name] ?? param.defaultValue ?? "")
391
+ ]));
392
+ }
393
+ function createKanbanBridgeScript(configuration, nonce) {
394
+ const initial = JSON.stringify(configuration).replaceAll("<", "\\u003c");
395
+ const safeNonce = JSON.stringify(nonce);
396
+ const eventName = JSON.stringify(KANBAN_CONFIGURATION_CHANGE_EVENT);
397
+ const readyMessage = JSON.stringify(KANBAN_PREVIEW_READY_MESSAGE);
398
+ const requestMessage = JSON.stringify(KANBAN_BRIDGE_REQUEST_MESSAGE);
399
+ const responseMessage = JSON.stringify(KANBAN_BRIDGE_RESPONSE_MESSAGE);
400
+ const revisionMessage = JSON.stringify(KANBAN_RELATED_PROJECT_REVISION_MESSAGE);
401
+ const presentationMessage = JSON.stringify(KANBAN_PRESENTATION_MESSAGE);
402
+ const presentationEvent = JSON.stringify(KANBAN_PRESENTATION_CHANGE_EVENT);
403
+ const safeErrorMessages = JSON.stringify(KANBAN_BRIDGE_SAFE_ERRORS);
404
+ return `<script nonce=${safeNonce}>(function(){
405
+ let current=${initial};
406
+ let presentation={mode:"fullscreen"};
407
+ let sequence=0;
408
+ const safeErrorMessages=${safeErrorMessages};
409
+ const pending=new Map();
410
+ const relatedProjectRevisions=new Map();
411
+ const relatedProjectRevisionListeners=new Map();
412
+ const clone=()=>JSON.parse(JSON.stringify(current));
413
+ const createError=(message,code)=>{const error=new Error(message);if(code)Object.defineProperty(error,"code",{value:code,enumerable:true});return error;};
414
+ const invalidRequest=()=>createError("The Kanban bridge request is invalid.","KANBAN_BRIDGE_INVALID_REQUEST");
415
+ const isSafeErrorCode=(code)=>typeof code==="string"&&Object.prototype.hasOwnProperty.call(safeErrorMessages,code);
416
+ const isJsonValue=(value,seen=new Set())=>{
417
+ if(value===null||typeof value==="string"||typeof value==="boolean")return true;
418
+ if(typeof value==="number")return Number.isFinite(value);
419
+ if(typeof value!=="object"||seen.has(value))return false;
420
+ seen.add(value);
421
+ let valid=false;
422
+ try{valid=Array.isArray(value)?value.every((item)=>isJsonValue(item,seen)):Object.getPrototypeOf(value)===Object.prototype&&Object.values(value).every((item)=>isJsonValue(item,seen));}
423
+ catch{return false;}
424
+ finally{seen.delete(value);}
425
+ return valid;
426
+ };
427
+ const ready=()=>{window.parent.postMessage({type:${readyMessage},nonce:${safeNonce},documentId:methodDocumentId},"*");publishKanbanMethods();};
428
+ const request=(operation,input)=>{
429
+ if(pending.size>=${KANBAN_BRIDGE_MAX_PENDING_REQUESTS})return Promise.reject(createError("Too many Kanban bridge requests are pending.","KANBAN_BRIDGE_BUSY"));
430
+ const requestId=Date.now().toString(36)+"-"+(++sequence).toString(36);
431
+ return new Promise((resolve,reject)=>{
432
+ const timeout=window.setTimeout(()=>{pending.delete(requestId);reject(createError("Kanban bridge request timed out.","KANBAN_BRIDGE_TIMEOUT"));},operation.startsWith("files.")?${KANBAN_FILE_BRIDGE_REQUEST_TIMEOUT_MS}:${KANBAN_BRIDGE_REQUEST_TIMEOUT_MS});
433
+ pending.set(requestId,{resolve,reject,timeout});
434
+ try{window.parent.postMessage({type:${requestMessage},nonce:${safeNonce},requestId,operation,input},"*");}
435
+ catch{window.clearTimeout(timeout);pending.delete(requestId);reject(invalidRequest());}
436
+ });
437
+ };
438
+ const kv=Object.freeze({
439
+ getValue:(key)=>request("kv.getValue",{key}),
440
+ setValue:(key,value)=>isJsonValue(value)?request("kv.setValue",{key,value}).then(()=>undefined):Promise.reject(invalidRequest())
441
+ });
442
+ const files=Object.freeze({
443
+ createFile:async(input)=>{
444
+ if(!input||typeof input.name!=="string")throw invalidRequest();
445
+ const source=input.buffer;
446
+ if(!(source instanceof Blob)&&!(source instanceof ArrayBuffer)&&!(source instanceof Uint8Array))throw invalidRequest();
447
+ if((source instanceof Blob?source.size:source.byteLength)>${KANBAN_FILE_MAX_BYTES})throw createError(safeErrorMessages.KANBAN_FILE_TOO_LARGE,"KANBAN_FILE_TOO_LARGE");
448
+ const bytes=source instanceof Blob?new Uint8Array(await source.arrayBuffer()):source instanceof ArrayBuffer?new Uint8Array(source):source;
449
+ let binary="";
450
+ for(let offset=0;offset<bytes.length;offset+=32768)binary+=String.fromCharCode(...bytes.subarray(offset,offset+32768));
451
+ return request("files.createFile",{name:input.name,mimeType:input.mimeType||(source instanceof Blob?source.type:undefined)||undefined,base64:btoa(binary)});
452
+ },
453
+ readFile:(fileId)=>request("files.readFile",{fileId}).then(({file,base64})=>({file,buffer:Uint8Array.from(atob(base64),(char)=>char.charCodeAt(0)).buffer})),
454
+ deleteFile:(fileId)=>request("files.deleteFile",{fileId})
455
+ });
456
+ const subscribeToRelatedProject=(alias,listener)=>{
457
+ if(typeof listener!=="function")throw invalidRequest();
458
+ let listeners=relatedProjectRevisionListeners.get(alias);
459
+ if(!listeners){listeners=new Set();relatedProjectRevisionListeners.set(alias,listeners);}
460
+ listeners.add(listener);
461
+ if(relatedProjectRevisions.has(alias)){const revision=relatedProjectRevisions.get(alias);Promise.resolve().then(()=>{if(listeners.has(listener))listener({alias,revision});}).catch(()=>{});}
462
+ return ()=>{listeners.delete(listener);if(listeners.size===0)relatedProjectRevisionListeners.delete(alias);};
463
+ };
464
+ const relatedProjectContexts=new Map();
465
+ const relatedProjects=Object.freeze({
466
+ get:(alias)=>{
467
+ if(typeof alias!=="string"||!${WORKFLOW_RELATED_PROJECT_ALIAS_PATTERN.toString()}.test(alias))throw invalidRequest();
468
+ if(relatedProjectContexts.has(alias))return relatedProjectContexts.get(alias);
469
+ const context=Object.freeze({kv:Object.freeze({
470
+ getValue:(key)=>request("relatedProject.kv.getValue",{alias,key}),
471
+ setValue:(key,value)=>isJsonValue(value)?request("relatedProject.kv.setValue",{alias,key,value}).then(()=>undefined):Promise.reject(invalidRequest()),
472
+ subscribe:(listener)=>subscribeToRelatedProject(alias,listener)
473
+ })});
474
+ relatedProjectContexts.set(alias,context);
475
+ return context;
476
+ }
477
+ });
478
+ const knowledge=Object.freeze({
479
+ listDocuments:(options={})=>request("knowledge.listDocuments",options),
480
+ getDocument:(documentId)=>request("knowledge.getDocument",{documentId}),
481
+ createDocument:(input)=>request("knowledge.createDocument",input),
482
+ editDocument:(documentId,input)=>request("knowledge.editDocument",{...input,documentId}),
483
+ deleteDocument:(documentId)=>request("knowledge.deleteDocument",{documentId}),
484
+ searchDocuments:(options)=>request("knowledge.searchDocuments",options),
485
+ readDocumentLines:(documentId,options)=>request("knowledge.readDocumentLines",{...options,documentId})
486
+ });
487
+ ${createKanbanMethodsBridgeScript(nonce)}
488
+ Object.defineProperty(window,"workflowCodeKanban",{configurable:false,enumerable:true,value:Object.freeze({getConfiguration:clone,getPresentation:()=>({...presentation}),getRuntimeLocale:()=>request("runtime.getLocale",{}),kv,files,relatedProjects,knowledge,methods})});
489
+ window.addEventListener("message",function(event){
490
+ if(event.source!==window.parent||!event.data||event.data.nonce!==${safeNonce})return;
491
+ if(handleKanbanMethodMessage(event.data))return;
492
+ if(event.data.type===${presentationMessage}){
493
+ const mode=event.data.presentation?.mode;
494
+ if(!["fullscreen","phone","tablet","card"].includes(mode)||presentation.mode===mode)return;
495
+ presentation={mode};
496
+ window.dispatchEvent(new CustomEvent(${presentationEvent},{detail:{...presentation}}));
497
+ return;
498
+ }
499
+ if(event.data.type==="workflow-code:kanban-configuration"){
500
+ if(event.data.configuration?.id!==current.id)resetKanbanMethods();
501
+ current=event.data.configuration;
502
+ window.dispatchEvent(new CustomEvent(${eventName},{detail:clone()}));
503
+ ready();
504
+ return;
505
+ }
506
+ if(event.data.type===${revisionMessage}){
507
+ const alias=event.data.alias;
508
+ const revision=event.data.revision;
509
+ if(typeof alias!=="string"||!${WORKFLOW_RELATED_PROJECT_ALIAS_PATTERN.toString()}.test(alias)||typeof revision!=="string"||revision.length===0||revision.length>512)return;
510
+ if(relatedProjectRevisions.get(alias)===revision)return;
511
+ relatedProjectRevisions.set(alias,revision);
512
+ const listeners=relatedProjectRevisionListeners.get(alias);
513
+ if(listeners)for(const listener of [...listeners])Promise.resolve().then(()=>listener({alias,revision})).catch(()=>{});
514
+ return;
515
+ }
516
+ if(event.data.type!==${responseMessage}||typeof event.data.requestId!=="string")return;
517
+ const entry=pending.get(event.data.requestId);
518
+ if(!entry)return;
519
+ pending.delete(event.data.requestId);
520
+ window.clearTimeout(entry.timeout);
521
+ if(event.data.ok===true)entry.resolve(event.data.value);
522
+ else{const code=event.data.error?.code;const safe=isSafeErrorCode(code);entry.reject(createError(safe?safeErrorMessages[code]:"Kanban bridge request failed.",safe?code:"KANBAN_BRIDGE_ERROR"));}
523
+ });
524
+ if(document.readyState==="loading")window.addEventListener("DOMContentLoaded",ready,{once:true});else ready();
525
+ })();</script>`;
526
+ }
527
+ function parseKanbanBridgeCall(value) {
528
+ if (!isRecord3(value)) throw new Error("Kanban bridge request must be an object.");
529
+ if (Object.keys(value).some((key) => !KANBAN_BRIDGE_CALL_FIELDS.has(key))) {
530
+ throw new Error("Kanban bridge request contains unsupported fields.");
531
+ }
532
+ const requestId = readNonEmptyString(value["requestId"], "bridge.requestId");
533
+ const operation = value["operation"];
534
+ if (!isKanbanBridgeOperation(operation)) {
535
+ throw new Error(`Unsupported Kanban bridge operation "${String(operation)}".`);
536
+ }
537
+ const input = value["input"];
538
+ if (!isRecord3(input)) throw new Error("Kanban bridge input must be an object.");
539
+ validateKanbanBridgeInput(operation, input);
540
+ return { requestId, operation, input };
541
+ }
542
+ function createKanbanBridgeError(reason) {
543
+ if (isRecord3(reason)) {
544
+ const code = reason["code"];
545
+ if (typeof code === "string" && Object.prototype.hasOwnProperty.call(KANBAN_BRIDGE_SAFE_ERRORS, code)) {
546
+ return {
547
+ code,
548
+ message: KANBAN_BRIDGE_SAFE_ERRORS[code]
549
+ };
550
+ }
551
+ }
552
+ return {
553
+ code: "KANBAN_BRIDGE_ERROR",
554
+ message: "Kanban bridge request failed."
555
+ };
556
+ }
557
+ function isKanbanJsonValue(value, seen = /* @__PURE__ */ new Set()) {
558
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
559
+ if (typeof value === "number") return Number.isFinite(value);
560
+ if (typeof value !== "object") return false;
561
+ if (seen.has(value)) return false;
562
+ seen.add(value);
563
+ const valid = Array.isArray(value) ? value.every((item) => isKanbanJsonValue(item, seen)) : Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((item) => isKanbanJsonValue(item, seen));
564
+ seen.delete(value);
565
+ return valid;
566
+ }
567
+ function injectKanbanBridge(html, configuration, nonce) {
568
+ const bridge = createKanbanBridgeScript(configuration, nonce);
569
+ const htmlWithNoncedScripts = html.replace(/<script\b([^>]*)>/gi, (tag, attributes) => {
570
+ if (/\bsrc\s*=/.test(attributes)) return tag;
571
+ const withoutNonce = attributes.replace(/\s+nonce\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
572
+ return `<script nonce=${JSON.stringify(nonce)}${withoutNonce}>`;
573
+ });
574
+ const headMatch = /<head(?:\s[^>]*)?>/i.exec(htmlWithNoncedScripts);
575
+ if (headMatch?.index !== void 0) {
576
+ const index = headMatch.index + headMatch[0].length;
577
+ return `${htmlWithNoncedScripts.slice(0, index)}${bridge}${htmlWithNoncedScripts.slice(index)}`;
578
+ }
579
+ return `${bridge}${htmlWithNoncedScripts}`;
580
+ }
581
+ function createKanbanContentSecurityPolicy(nonce) {
582
+ return [
583
+ "default-src 'self' data: blob:",
584
+ `script-src 'self' 'nonce-${nonce}' 'wasm-unsafe-eval'`,
585
+ "style-src 'self' 'unsafe-inline'",
586
+ "img-src 'self' data: blob:",
587
+ "font-src 'self' data:",
588
+ "media-src 'self' data: blob:",
589
+ "connect-src 'self' data: blob:",
590
+ "object-src 'none'",
591
+ "base-uri 'self'"
592
+ ].join("; ");
593
+ }
594
+ function isSafeKanbanAssetPath(filePath) {
595
+ const normalized = filePath.replaceAll("\\", "/");
596
+ if (normalized === "" || normalized.startsWith("/") || normalized.includes("\0")) return false;
597
+ const parts = normalized.split("/");
598
+ if (parts.some((part) => part === "" || part === "." || part === "..")) return false;
599
+ const lowerParts = parts.map((part) => part.toLowerCase());
600
+ if (lowerParts.some((part) => part.startsWith(".") || part === "node_modules" || part === "package.json" || part === "kanban.json" || part === "package-lock.json" || part === "pnpm-lock.yaml" || part === "yarn.lock" || part === "readme.md")) return false;
601
+ return true;
602
+ }
603
+ function isSafeKanbanPublicAssetPath(filePath, entry = KANBAN_ENTRY_FILE) {
604
+ const normalized = filePath.replaceAll("\\", "/");
605
+ return normalized === entry || isSafeKanbanAssetPath(normalized);
606
+ }
607
+ function readKanbanRelativePath(value, field, fallback, allowDot) {
608
+ const raw = value === void 0 ? fallback : value;
609
+ if (typeof raw !== "string" || raw.trim() === "") {
610
+ throw new Error(`package.json ${field} must be a non-empty string.`);
611
+ }
612
+ const normalized = raw.trim().replaceAll("\\", "/").replace(/\/+$/, "");
613
+ if (allowDot && normalized === ".") return normalized;
614
+ if (normalized === "" || normalized === "." || normalized.startsWith("/") || normalized.startsWith("//") || /^[a-zA-Z]:/.test(normalized) || normalized.includes("\0") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
615
+ throw new Error(`package.json ${field} must be a safe project-relative path.`);
616
+ }
617
+ const reservedDirectory = normalized.split("/").find((part) => [".git", ".hg", ".svn", "node_modules"].includes(part.toLowerCase()));
618
+ if (reservedDirectory !== void 0) {
619
+ throw new Error(
620
+ `package.json ${field} must not use reserved directory ${JSON.stringify(reservedDirectory)}.`
621
+ );
622
+ }
623
+ if (!allowDot && !/\.html?$/i.test(normalized)) {
624
+ throw new Error(`package.json ${field} must point to an HTML file.`);
625
+ }
626
+ return normalized;
627
+ }
628
+ function parseParam(value, index, names) {
629
+ if (!isRecord3(value)) throw new Error(`kanban.json params[${index}] must be an object.`);
630
+ const name = readNonEmptyString(value["name"], `params[${index}].name`);
631
+ if (names.has(name)) throw new Error(`kanban.json contains duplicate param name "${name}".`);
632
+ names.add(name);
633
+ const type = value["type"];
634
+ if (!isParamType(type)) throw new Error(`kanban.json params[${index}].type is unsupported.`);
635
+ const control = value["control"];
636
+ if (control !== void 0 && !isParamControl(control)) {
637
+ throw new Error(`kanban.json params[${index}].control is unsupported.`);
638
+ }
639
+ const panel = value["panel"];
640
+ if (panel !== void 0 && panel !== "main" && panel !== "auxiliary" && panel !== "quick") {
641
+ throw new Error(`kanban.json params[${index}].panel is unsupported.`);
642
+ }
643
+ const options = value["options"];
644
+ if (options !== void 0 && !Array.isArray(options)) {
645
+ throw new Error(`kanban.json params[${index}].options must be an array.`);
646
+ }
647
+ return {
648
+ name,
649
+ type,
650
+ ...readOptionalString(value["flag"]) === void 0 ? {} : { flag: readOptionalString(value["flag"]) },
651
+ ...readOptionalString(value["shortFlag"]) === void 0 ? {} : { shortFlag: readOptionalString(value["shortFlag"]) },
652
+ ...typeof value["required"] === "boolean" ? { required: value["required"] } : {},
653
+ ...readOptionalString(value["label"]) === void 0 ? {} : { label: readOptionalString(value["label"]) },
654
+ ...readOptionalString(value["description"]) === void 0 ? {} : { description: readOptionalString(value["description"]) },
655
+ ...typeof value["defaultValue"] === "string" ? { defaultValue: value["defaultValue"] } : {},
656
+ ...typeof value["multiple"] === "boolean" ? { multiple: value["multiple"] } : {},
657
+ ...control === void 0 ? {} : { control },
658
+ ...panel === void 0 ? {} : { panel },
659
+ ...typeof value["resolveOnInput"] === "boolean" ? { resolveOnInput: value["resolveOnInput"] } : {},
660
+ ...options === void 0 ? {} : { options: options.map((option, optionIndex) => parseOption(option, index, optionIndex)) },
661
+ ...Array.isArray(value["accept"]) && value["accept"].every((item) => typeof item === "string") ? { accept: value["accept"] } : {},
662
+ ...value["format"] === "json" ? { format: "json" } : {}
663
+ };
664
+ }
665
+ function validateKanbanBridgeInput(operation, input) {
666
+ const allowedFields = new Set(KANBAN_BRIDGE_INPUT_FIELDS[operation]);
667
+ if (Object.keys(input).some((key) => !allowedFields.has(key))) {
668
+ throw new Error(`Kanban bridge ${operation} input contains unsupported fields.`);
669
+ }
670
+ switch (operation) {
671
+ case "runtime.getLocale":
672
+ return;
673
+ case "files.createFile": {
674
+ const name = readNonEmptyString(input["name"], "bridge.input.name");
675
+ if (name.length > 255 || /[\\/\\\\\0]/.test(name)) {
676
+ throw new Error("Kanban file name must be a plain file name.");
677
+ }
678
+ const mimeType = readOptionalNonEmptyString(input["mimeType"], "bridge.input.mimeType");
679
+ if (mimeType !== void 0 && !/^[a-zA-Z0-9!#$&^_.+-]+\/[a-zA-Z0-9!#$&^_.+-]+$/.test(mimeType)) {
680
+ throw new Error("Kanban file MIME type is invalid.");
681
+ }
682
+ const base64 = readRequiredString(input["base64"], "bridge.input.base64");
683
+ if (base64.length > Math.ceil(KANBAN_FILE_MAX_BYTES / 3) * 4) {
684
+ throw Object.assign(new Error(KANBAN_BRIDGE_SAFE_ERRORS.KANBAN_FILE_TOO_LARGE), { code: "KANBAN_FILE_TOO_LARGE" });
685
+ }
686
+ if (base64.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(base64)) {
687
+ throw new Error("Kanban file content must be base64 encoded.");
688
+ }
689
+ const size = base64.length / 4 * 3 - (base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0);
690
+ if (size > KANBAN_FILE_MAX_BYTES) {
691
+ throw Object.assign(new Error(KANBAN_BRIDGE_SAFE_ERRORS.KANBAN_FILE_TOO_LARGE), { code: "KANBAN_FILE_TOO_LARGE" });
692
+ }
693
+ return;
694
+ }
695
+ case "files.readFile":
696
+ case "files.deleteFile":
697
+ if (!/^wf_[a-f0-9]+$/.test(readNonEmptyString(input["fileId"], "bridge.input.fileId"))) {
698
+ throw new Error("Kanban file id is invalid.");
699
+ }
700
+ return;
701
+ case "kv.getValue":
702
+ readNonEmptyString(input["key"], "bridge.input.key");
703
+ return;
704
+ case "kv.setValue":
705
+ readNonEmptyString(input["key"], "bridge.input.key");
706
+ if (input["value"] === void 0 || !isKanbanJsonValue(input["value"])) {
707
+ throw new Error("Kanban bridge KV value must be JSON-serializable and cannot be undefined.");
708
+ }
709
+ return;
710
+ case "relatedProject.kv.getValue":
711
+ case "relatedProject.kv.setValue": {
712
+ const alias = readNonEmptyString(input["alias"], "bridge.input.alias");
713
+ if (!WORKFLOW_RELATED_PROJECT_ALIAS_PATTERN.test(alias)) {
714
+ throw new Error("Kanban bridge related-project alias is invalid.");
715
+ }
716
+ const key = readNonEmptyString(input["key"], "bridge.input.key");
717
+ if (key.length > WORKFLOW_RELATED_PROJECT_KEY_LIMIT) {
718
+ throw new Error("Kanban bridge related-project key is too long.");
719
+ }
720
+ if (operation === "relatedProject.kv.setValue" && (input["value"] === void 0 || !isKanbanJsonValue(input["value"]))) {
721
+ throw new Error(
722
+ "Kanban bridge related-project KV value must be JSON-serializable and cannot be undefined."
723
+ );
724
+ }
725
+ return;
726
+ }
727
+ case "knowledge.listDocuments":
728
+ readOptionalIntegerInRange(
729
+ input["pageSize"],
730
+ "bridge.input.pageSize",
731
+ 1,
732
+ MAX_KNOWLEDGE_DOCUMENT_PAGE_SIZE
733
+ );
734
+ readOptionalNonEmptyString(input["cursor"], "bridge.input.cursor");
735
+ return;
736
+ case "knowledge.getDocument":
737
+ case "knowledge.deleteDocument":
738
+ readNonEmptyString(input["documentId"], "bridge.input.documentId");
739
+ return;
740
+ case "knowledge.createDocument":
741
+ readOptionalNonEmptyString(input["id"], "bridge.input.id");
742
+ readNonEmptyString(input["title"], "bridge.input.title");
743
+ readRequiredString(input["markdown"], "bridge.input.markdown");
744
+ return;
745
+ case "knowledge.editDocument":
746
+ readNonEmptyString(input["documentId"], "bridge.input.documentId");
747
+ if (input["title"] === void 0 && input["markdown"] === void 0) {
748
+ throw new Error("Kanban bridge knowledge edit requires title or markdown.");
749
+ }
750
+ readOptionalStringValue(input["title"], "bridge.input.title");
751
+ readOptionalStringValue(input["markdown"], "bridge.input.markdown");
752
+ return;
753
+ case "knowledge.searchDocuments":
754
+ readNonEmptyString(input["query"], "bridge.input.query");
755
+ readOptionalNonEmptyString(input["documentId"], "bridge.input.documentId");
756
+ readOptionalIntegerInRange(
757
+ input["beforeLines"],
758
+ "bridge.input.beforeLines",
759
+ 0,
760
+ MAX_KNOWLEDGE_CONTEXT_LINES
761
+ );
762
+ readOptionalIntegerInRange(
763
+ input["afterLines"],
764
+ "bridge.input.afterLines",
765
+ 0,
766
+ MAX_KNOWLEDGE_CONTEXT_LINES
767
+ );
768
+ readOptionalIntegerInRange(
769
+ input["maxMatches"],
770
+ "bridge.input.maxMatches",
771
+ 1,
772
+ MAX_KNOWLEDGE_SEARCH_PAGE_SIZE
773
+ );
774
+ readOptionalIntegerInRange(
775
+ input["pageSize"],
776
+ "bridge.input.pageSize",
777
+ 1,
778
+ MAX_KNOWLEDGE_SEARCH_PAGE_SIZE
779
+ );
780
+ readOptionalNonEmptyString(input["cursor"], "bridge.input.cursor");
781
+ return;
782
+ case "knowledge.readDocumentLines":
783
+ readNonEmptyString(input["documentId"], "bridge.input.documentId");
784
+ const startLine = readPositiveInteger(input["startLine"], "bridge.input.startLine");
785
+ const endLine = readPositiveInteger(input["endLine"], "bridge.input.endLine");
786
+ if (endLine < startLine || endLine - startLine + 1 > MAX_KNOWLEDGE_LINE_RANGE) {
787
+ throw new Error("Kanban bridge knowledge line range is invalid or too large.");
788
+ }
789
+ }
790
+ }
791
+ function isKanbanBridgeOperation(value) {
792
+ return value === "runtime.getLocale" || value === "files.createFile" || value === "files.readFile" || value === "files.deleteFile" || value === "kv.getValue" || value === "kv.setValue" || value === "relatedProject.kv.getValue" || value === "relatedProject.kv.setValue" || value === "knowledge.listDocuments" || value === "knowledge.getDocument" || value === "knowledge.createDocument" || value === "knowledge.editDocument" || value === "knowledge.deleteDocument" || value === "knowledge.searchDocuments" || value === "knowledge.readDocumentLines";
793
+ }
794
+ function readRequiredString(value, field) {
795
+ if (typeof value !== "string") throw new Error(`Kanban ${field} must be a string.`);
796
+ return value;
797
+ }
798
+ function readOptionalStringValue(value, field) {
799
+ if (value === void 0) return void 0;
800
+ return readRequiredString(value, field);
801
+ }
802
+ function readOptionalNonEmptyString(value, field) {
803
+ if (value === void 0) return void 0;
804
+ return readNonEmptyString(value, field);
805
+ }
806
+ function readOptionalIntegerInRange(value, field, minimum, maximum) {
807
+ if (value === void 0) return void 0;
808
+ if (!Number.isInteger(value) || value < minimum || value > maximum) {
809
+ throw new Error(`Kanban ${field} must be an integer from ${minimum} to ${maximum}.`);
810
+ }
811
+ return value;
812
+ }
813
+ function readPositiveInteger(value, field) {
814
+ if (!Number.isInteger(value) || value <= 0) {
815
+ throw new Error(`Kanban ${field} must be a positive integer.`);
816
+ }
817
+ return value;
818
+ }
819
+ function parseOption(value, paramIndex, optionIndex) {
820
+ if (!isRecord3(value)) {
821
+ throw new Error(`kanban.json params[${paramIndex}].options[${optionIndex}] must be an object.`);
822
+ }
823
+ return {
824
+ value: readNonEmptyString(value["value"], `params[${paramIndex}].options[${optionIndex}].value`),
825
+ ...readOptionalString(value["label"]) === void 0 ? {} : { label: readOptionalString(value["label"]) },
826
+ ...readOptionalString(value["description"]) === void 0 ? {} : { description: readOptionalString(value["description"]) }
827
+ };
828
+ }
829
+ function parseInjectedValue(param, value) {
830
+ if (param.type === "file") {
831
+ try {
832
+ const parsed = JSON.parse(value);
833
+ const values = Array.isArray(parsed) ? parsed : [parsed];
834
+ return values.map(sanitizeInjectedFileReference).filter((file) => file !== null);
835
+ } catch {
836
+ return [];
837
+ }
838
+ }
839
+ if (param.control === "checkboxes" || param.multiple) {
840
+ try {
841
+ const parsed = JSON.parse(value);
842
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
843
+ } catch {
844
+ return [];
845
+ }
846
+ }
847
+ if (param.type === "number") {
848
+ const parsed = Number(value);
849
+ return value.trim() !== "" && Number.isFinite(parsed) ? parsed : null;
850
+ }
851
+ if (param.type === "boolean") return value === "true";
852
+ return value;
853
+ }
854
+ function sanitizeInjectedFileReference(value) {
855
+ if (!isRecord3(value) || typeof value["id"] !== "string" || value["id"].trim() === "" || value["storage"] !== "local" && value["storage"] !== "server") {
856
+ return null;
857
+ }
858
+ const mimeType = typeof value["mimeType"] === "string" ? value["mimeType"] : "application/octet-stream";
859
+ return {
860
+ id: value["id"],
861
+ name: typeof value["name"] === "string" ? value["name"] : value["id"],
862
+ mimeType,
863
+ size: typeof value["size"] === "number" && Number.isFinite(value["size"]) ? Math.max(0, value["size"]) : 0,
864
+ kind: value["kind"] === "image" || mimeType.startsWith("image/") ? "image" : "file",
865
+ storage: value["storage"],
866
+ ...typeof value["workflowId"] === "string" ? { workflowId: value["workflowId"] } : {},
867
+ ...typeof value["conversationId"] === "string" ? { conversationId: value["conversationId"] } : {},
868
+ ...typeof value["createdAt"] === "string" ? { createdAt: value["createdAt"] } : {}
869
+ };
870
+ }
871
+ function readNonEmptyString(value, field) {
872
+ if (typeof value !== "string" || value.trim() === "") throw new Error(`kanban.json ${field} must be a non-empty string.`);
873
+ return value.trim();
874
+ }
875
+ function readOptionalString(value) {
876
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
877
+ }
878
+ function isParamType(value) {
879
+ return value === "string" || value === "number" || value === "boolean" || value === "positional" || value === "file";
880
+ }
881
+ function isParamControl(value) {
882
+ return value === "text" || value === "textarea" || value === "radio" || value === "select" || value === "checkboxes";
883
+ }
884
+ function isRecord3(value) {
885
+ return typeof value === "object" && value !== null && !Array.isArray(value);
886
+ }
887
+
888
+ export {
889
+ parseKanbanMethodInvocation,
890
+ assertKanbanMethodJson,
891
+ parseKanbanMethodDefinitions,
892
+ consumeKanbanRelatedProjectRevisionStream,
893
+ KANBAN_MANIFEST_FILE,
894
+ readPackageProjectType,
895
+ readKanbanArtifactConfig,
896
+ createKanbanArtifactEntryPath,
897
+ encodeKanbanAssetUrlPath,
898
+ readKanbanAssetMimeType,
899
+ parseKanbanManifestText,
900
+ createDefaultKanbanValues,
901
+ sanitizeKanbanValues,
902
+ createInjectedKanbanParams,
903
+ parseKanbanBridgeCall,
904
+ createKanbanBridgeError,
905
+ injectKanbanBridge,
906
+ createKanbanContentSecurityPolicy,
907
+ isSafeKanbanPublicAssetPath
908
+ };