@wenathlan/saddle 2.0.153 → 2.0.155

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.
Files changed (307) hide show
  1. package/Dockerfile +139 -55
  2. package/README.md +58 -59
  3. package/browser.ts +482 -0
  4. package/cli.ts +274 -0
  5. package/distribution.ts +731 -0
  6. package/docker.config +29 -8
  7. package/docs/artifactavailability.md +2 -2
  8. package/docs/browser.md +18 -1
  9. package/docs/{containerplatforms.2.1.0.md → containerplatforms210.md} +1 -1
  10. package/docs/{documentmap.1.8.12.md → documentmap1812.md} +1 -1
  11. package/docs/extension.md +13 -2
  12. package/docs/libraryapi.md +1 -1
  13. package/docs/{ninety.modalities.md → ninetymodalities.md} +87 -4
  14. package/docs/{planning.ninety.md → planningninety.md} +6 -6
  15. package/docs/{releasenotes.1.8.13.md → releasenotes1813.md} +2 -2
  16. package/docs/{releasenotes.1.8.14.md → releasenotes1814.md} +1 -1
  17. package/docs/{repo.synthesis.1.8.16.md → reposynthesis1816.md} +1 -1
  18. package/docs/todo.md +2 -2
  19. package/docs/{todo.1.8.15.md → todo1815.md} +1 -1
  20. package/docs/{todo.1.8.16.md → todo1816.md} +5 -5
  21. package/execution.ts +677 -0
  22. package/foundation.ts +314 -0
  23. package/index.ts +35 -27
  24. package/isolation.ts +84 -0
  25. package/modes.ts +170 -0
  26. package/mttg.config +1 -1
  27. package/orchestrator.ts +49 -15
  28. package/package.json +90 -75
  29. package/qemu.config +2 -2
  30. package/sandbox.ts +2094 -0
  31. package/scheduler.ts +1 -1
  32. package/server.ts +52 -0
  33. package/tsconfig.json +11 -12
  34. package/virtual.ts +1105 -0
  35. package/virtualgpu.ts +1 -1
  36. package/virtualization.ts +1 -1
  37. package/workloadlaw.ts +293 -0
  38. package/dist/App.d.ts +0 -5
  39. package/dist/App.js +0 -52
  40. package/dist/Sol/Sol.d.ts +0 -3
  41. package/dist/Sol/Sol.js +0 -46
  42. package/dist/Sol/agentbrowser/agentbrowser.d.ts +0 -9
  43. package/dist/Sol/agentbrowser/agentbrowser.js +0 -121
  44. package/dist/Sol/architecture/architecture.d.ts +0 -9
  45. package/dist/Sol/architecture/architecture.js +0 -93
  46. package/dist/Sol/compute/compute.d.ts +0 -9
  47. package/dist/Sol/compute/compute.js +0 -22
  48. package/dist/Sol/console/console.d.ts +0 -15
  49. package/dist/Sol/console/console.js +0 -292
  50. package/dist/Sol/dashboard/dashboard.d.ts +0 -10
  51. package/dist/Sol/dashboard/dashboard.js +0 -181
  52. package/dist/Sol/docs/docs.d.ts +0 -9
  53. package/dist/Sol/docs/docs.js +0 -28
  54. package/dist/Sol/home/home.d.ts +0 -35
  55. package/dist/Sol/home/home.js +0 -239
  56. package/dist/Sol/integrations/integrations.d.ts +0 -9
  57. package/dist/Sol/integrations/integrations.js +0 -83
  58. package/dist/Sol/intro/intro.d.ts +0 -7
  59. package/dist/Sol/intro/intro.js +0 -102
  60. package/dist/Sol/login/login.d.ts +0 -16
  61. package/dist/Sol/login/login.js +0 -49
  62. package/dist/Sol/notfound/notfound.d.ts +0 -9
  63. package/dist/Sol/notfound/notfound.js +0 -15
  64. package/dist/Sol/onboarding/onboarding.d.ts +0 -25
  65. package/dist/Sol/onboarding/onboarding.js +0 -60
  66. package/dist/Sol/playground/playground.d.ts +0 -9
  67. package/dist/Sol/playground/playground.js +0 -127
  68. package/dist/Sol/register/register.d.ts +0 -16
  69. package/dist/Sol/register/register.js +0 -193
  70. package/dist/Sol/shell/Shell.d.ts +0 -124
  71. package/dist/Sol/shell/Shell.js +0 -335
  72. package/dist/Sol/wingets/ComputeBootladder.d.ts +0 -6
  73. package/dist/Sol/wingets/ComputeBootladder.js +0 -71
  74. package/dist/Sol/wingets/ComputeModalitycatalog.d.ts +0 -7
  75. package/dist/Sol/wingets/ComputeModalitycatalog.js +0 -71
  76. package/dist/Sol/wingets/ConsoleApibadge.d.ts +0 -7
  77. package/dist/Sol/wingets/ConsoleApibadge.js +0 -5
  78. package/dist/Sol/wingets/ConsoleEdgelaw.d.ts +0 -6
  79. package/dist/Sol/wingets/ConsoleEdgelaw.js +0 -36
  80. package/dist/Sol/wingets/ConsoleEventstimeline.d.ts +0 -19
  81. package/dist/Sol/wingets/ConsoleEventstimeline.js +0 -12
  82. package/dist/Sol/wingets/ConsoleSnapshotcontrols.d.ts +0 -8
  83. package/dist/Sol/wingets/ConsoleSnapshotcontrols.js +0 -8
  84. package/dist/Sol/wingets/ConsoleSpecpanel.d.ts +0 -21
  85. package/dist/Sol/wingets/ConsoleSpecpanel.js +0 -75
  86. package/dist/Sol/wingets/ConsoleTerminal.d.ts +0 -22
  87. package/dist/Sol/wingets/ConsoleTerminal.js +0 -77
  88. package/dist/Sol/wingets/DashboardAccountpanel.d.ts +0 -18
  89. package/dist/Sol/wingets/DashboardAccountpanel.js +0 -65
  90. package/dist/Sol/wingets/DashboardAdminpanel.d.ts +0 -6
  91. package/dist/Sol/wingets/DashboardAdminpanel.js +0 -90
  92. package/dist/Sol/wingets/DashboardAuditlog.d.ts +0 -8
  93. package/dist/Sol/wingets/DashboardAuditlog.js +0 -50
  94. package/dist/Sol/wingets/DashboardCreatesandboxform.d.ts +0 -10
  95. package/dist/Sol/wingets/DashboardCreatesandboxform.js +0 -71
  96. package/dist/Sol/wingets/DashboardEventstimeline.d.ts +0 -8
  97. package/dist/Sol/wingets/DashboardEventstimeline.js +0 -83
  98. package/dist/Sol/wingets/DashboardGlobalsandboxestable.d.ts +0 -8
  99. package/dist/Sol/wingets/DashboardGlobalsandboxestable.js +0 -57
  100. package/dist/Sol/wingets/DashboardMeshnodestable.d.ts +0 -8
  101. package/dist/Sol/wingets/DashboardMeshnodestable.js +0 -103
  102. package/dist/Sol/wingets/DashboardSandboxlist.d.ts +0 -10
  103. package/dist/Sol/wingets/DashboardSandboxlist.js +0 -163
  104. package/dist/Sol/wingets/DashboardUserstable.d.ts +0 -8
  105. package/dist/Sol/wingets/DashboardUserstable.js +0 -78
  106. package/dist/Sol/wingets/HomeEntrygate.d.ts +0 -23
  107. package/dist/Sol/wingets/HomeEntrygate.js +0 -73
  108. package/dist/Sol/wingets/HomeMetricstrip.d.ts +0 -9
  109. package/dist/Sol/wingets/HomeMetricstrip.js +0 -4
  110. package/dist/Sol/wingets/HomeRuntimediagram.d.ts +0 -1
  111. package/dist/Sol/wingets/HomeRuntimediagram.js +0 -22
  112. package/dist/Sol/wingets/LoginForm.d.ts +0 -19
  113. package/dist/Sol/wingets/LoginForm.js +0 -141
  114. package/dist/Sol/wingets/RegisterPasswordconfirm.d.ts +0 -13
  115. package/dist/Sol/wingets/RegisterPasswordconfirm.js +0 -16
  116. package/dist/Sol/wingets/RegisterPasswordmeter.d.ts +0 -19
  117. package/dist/Sol/wingets/RegisterPasswordmeter.js +0 -65
  118. package/dist/Sol/wingets/RegisterUsernamerules.d.ts +0 -11
  119. package/dist/Sol/wingets/RegisterUsernamerules.js +0 -16
  120. package/dist/Sol/wingets/index.d.ts +0 -39
  121. package/dist/Sol/wingets/index.js +0 -39
  122. package/dist/alternatives.d.ts +0 -508
  123. package/dist/alternatives.js +0 -950
  124. package/dist/api.d.ts +0 -154
  125. package/dist/api.js +0 -174
  126. package/dist/auth.d.ts +0 -268
  127. package/dist/auth.js +0 -476
  128. package/dist/automation.d.ts +0 -425
  129. package/dist/automation.js +0 -566
  130. package/dist/browser.d.ts +0 -561
  131. package/dist/browser.js +0 -1058
  132. package/dist/capacitor.config.d.ts +0 -45
  133. package/dist/capacitor.config.js +0 -48
  134. package/dist/catalog.d.ts +0 -43
  135. package/dist/catalog.js +0 -44
  136. package/dist/cli.d.ts +0 -16
  137. package/dist/cli.js +0 -168
  138. package/dist/communication.d.ts +0 -181
  139. package/dist/communication.js +0 -376
  140. package/dist/compute.d.ts +0 -1160
  141. package/dist/compute.js +0 -2272
  142. package/dist/dashboardlib.d.ts +0 -58
  143. package/dist/dashboardlib.js +0 -161
  144. package/dist/db.d.ts +0 -569
  145. package/dist/db.js +0 -1080
  146. package/dist/distribution.d.ts +0 -528
  147. package/dist/distribution.js +0 -810
  148. package/dist/drizzle.config.d.ts +0 -20
  149. package/dist/drizzle.config.js +0 -18
  150. package/dist/edgelandes.d.ts +0 -70
  151. package/dist/edgelandes.host.d.ts +0 -26
  152. package/dist/edgelandes.host.js +0 -1089
  153. package/dist/edgelandes.inproc.d.ts +0 -27
  154. package/dist/edgelandes.inproc.js +0 -952
  155. package/dist/edgelandes.js +0 -1133
  156. package/dist/edgelandes.ninety.d.ts +0 -31
  157. package/dist/edgelandes.ninety.js +0 -174
  158. package/dist/edgelandes.virtual.d.ts +0 -25
  159. package/dist/edgelandes.virtual.js +0 -629
  160. package/dist/edgeplane.d.ts +0 -114
  161. package/dist/edgeplane.js +0 -305
  162. package/dist/examplelocaljob.d.ts +0 -1
  163. package/dist/examplelocaljob.js +0 -14
  164. package/dist/execution.d.ts +0 -529
  165. package/dist/execution.js +0 -760
  166. package/dist/family.d.ts +0 -16
  167. package/dist/family.js +0 -42
  168. package/dist/familyurl.d.ts +0 -21
  169. package/dist/familyurl.js +0 -24
  170. package/dist/format.d.ts +0 -18
  171. package/dist/format.js +0 -59
  172. package/dist/foundation.d.ts +0 -172
  173. package/dist/foundation.js +0 -312
  174. package/dist/index.d.ts +0 -735
  175. package/dist/index.js +0 -1020
  176. package/dist/integration.d.ts +0 -235
  177. package/dist/integration.js +0 -228
  178. package/dist/intelligence.d.ts +0 -93
  179. package/dist/intelligence.js +0 -144
  180. package/dist/isolation.d.ts +0 -128
  181. package/dist/isolation.js +0 -95
  182. package/dist/labels.d.ts +0 -665
  183. package/dist/labels.js +0 -751
  184. package/dist/localauth.d.ts +0 -127
  185. package/dist/localauth.js +0 -413
  186. package/dist/media.d.ts +0 -343
  187. package/dist/media.js +0 -985
  188. package/dist/mesh.d.ts +0 -177
  189. package/dist/mesh.js +0 -468
  190. package/dist/modalityregistry.d.ts +0 -109
  191. package/dist/modalityregistry.js +0 -1386
  192. package/dist/modes.d.ts +0 -358
  193. package/dist/modes.js +0 -133
  194. package/dist/onboardingstate.d.ts +0 -79
  195. package/dist/onboardingstate.js +0 -282
  196. package/dist/orchestrator.d.ts +0 -1338
  197. package/dist/orchestrator.js +0 -4310
  198. package/dist/packager/targetcli.js +0 -4
  199. package/dist/paths.d.ts +0 -2
  200. package/dist/paths.js +0 -14
  201. package/dist/performance.d.ts +0 -415
  202. package/dist/performance.js +0 -826
  203. package/dist/quantum.d.ts +0 -809
  204. package/dist/quantum.js +0 -2072
  205. package/dist/render.d.ts +0 -380
  206. package/dist/render.js +0 -1072
  207. package/dist/runall.d.ts +0 -2
  208. package/dist/runall.js +0 -39
  209. package/dist/runtimeprobe.d.ts +0 -4
  210. package/dist/runtimeprobe.js +0 -8
  211. package/dist/sandbox.d.ts +0 -355
  212. package/dist/sandbox.js +0 -1721
  213. package/dist/sandboxenvprofile.d.ts +0 -29
  214. package/dist/sandboxenvprofile.js +0 -89
  215. package/dist/sandboxprofile.d.ts +0 -137
  216. package/dist/sandboxprofile.js +0 -241
  217. package/dist/sandboxqueue.d.ts +0 -108
  218. package/dist/sandboxqueue.js +0 -201
  219. package/dist/sandboxquota.d.ts +0 -121
  220. package/dist/sandboxquota.js +0 -236
  221. package/dist/scheduler.d.ts +0 -595
  222. package/dist/scheduler.js +0 -1258
  223. package/dist/scriptlanes.d.ts +0 -182
  224. package/dist/scriptlanes.js +0 -660
  225. package/dist/scripts.buildengine.d.ts +0 -1
  226. package/dist/scripts.buildengine.js +0 -39
  227. package/dist/scripts.securitygate.d.ts +0 -1
  228. package/dist/scripts.securitygate.js +0 -68
  229. package/dist/scripts.validateflatnative.d.ts +0 -1
  230. package/dist/scripts.validateflatnative.js +0 -72
  231. package/dist/security.d.ts +0 -500
  232. package/dist/security.js +0 -1169
  233. package/dist/seedcatalog.d.ts +0 -13
  234. package/dist/seedcatalog.js +0 -46
  235. package/dist/server.d.ts +0 -20
  236. package/dist/server.js +0 -53
  237. package/dist/speccatalog.d.ts +0 -31
  238. package/dist/speccatalog.js +0 -163
  239. package/dist/tiers.d.ts +0 -1001
  240. package/dist/tiers.js +0 -2169
  241. package/dist/utils.d.ts +0 -2
  242. package/dist/utils.js +0 -5
  243. package/dist/vaulthandoff.d.ts +0 -141
  244. package/dist/vaulthandoff.js +0 -444
  245. package/dist/virtual.d.ts +0 -640
  246. package/dist/virtual.js +0 -1284
  247. package/dist/virtualcpu.d.ts +0 -315
  248. package/dist/virtualcpu.js +0 -1180
  249. package/dist/virtualgpu.d.ts +0 -344
  250. package/dist/virtualgpu.js +0 -1000
  251. package/dist/virtualization.d.ts +0 -606
  252. package/dist/virtualization.js +0 -1186
  253. package/dist/virtualmemory.d.ts +0 -817
  254. package/dist/virtualmemory.js +0 -1795
  255. package/dist/vite.config.d.ts +0 -3
  256. package/dist/vite.config.js +0 -43
  257. package/dist/vitest.config.d.ts +0 -2
  258. package/dist/vitest.config.js +0 -25
  259. package/dist/webserver.d.ts +0 -166
  260. package/dist/webserver.js +0 -2226
  261. package/dist/workloadlaw.d.ts +0 -133
  262. package/dist/workloadlaw.js +0 -193
  263. package/manifest.json +0 -20
  264. package/media.ts +0 -1337
  265. /package/docs/{architecture.1.8.19.md → architecture1819.md} +0 -0
  266. /package/docs/{architecture.1.8.19.mmd → architecture1819.mmd} +0 -0
  267. /package/docs/{artifactresearch.1.8.9.md → artifactresearch189.md} +0 -0
  268. /package/docs/{batches.1816.to.1820.md → batches1816to1820.md} +0 -0
  269. /package/docs/{brancharchive.2026.08.18.md → brancharchive20260818.md} +0 -0
  270. /package/docs/{containerplatforms.1.8.17.md → containerplatforms1817.md} +0 -0
  271. /package/docs/{e2ugh.engine.md → e2ughengine.md} +0 -0
  272. /package/docs/{flatbuildresearch.1.8.11.md → flatbuildresearch1811.md} +0 -0
  273. /package/docs/{licenseaudit.1.8.12.md → licenseaudit1812.md} +0 -0
  274. /package/docs/{mobileconversionresearch.1.8.10.md → mobileconversionresearch1810.md} +0 -0
  275. /package/docs/{nativeidentityresearch.1.8.11.md → nativeidentityresearch1811.md} +0 -0
  276. /package/docs/{native.wrappers.md → nativewrappers.md} +0 -0
  277. /package/docs/{planning.1.8.18.md → planning1818.md} +0 -0
  278. /package/docs/{planning.1.8.19.md → planning1819.md} +0 -0
  279. /package/docs/{planning.engine.md → planningengine.md} +0 -0
  280. /package/docs/{releasenotes.1.8.12.md → releasenotes1812.md} +0 -0
  281. /package/docs/{releasenotes.1.8.15.md → releasenotes1815.md} +0 -0
  282. /package/docs/{releasenotes.1.8.16.md → releasenotes1816.md} +0 -0
  283. /package/docs/{releasenotes.1.8.17.md → releasenotes1817.md} +0 -0
  284. /package/docs/{releasenotes.1.8.18.md → releasenotes1818.md} +0 -0
  285. /package/docs/{releasenotes.1.8.19.md → releasenotes1819.md} +0 -0
  286. /package/docs/{reorganization.1.8.8.md → reorganization188.md} +0 -0
  287. /package/docs/{reorganization.1.8.9.md → reorganization189.md} +0 -0
  288. /package/docs/{repo.analysis.1.8.21.json → repoanalysis1821.json} +0 -0
  289. /package/docs/{repo.candidates.1.8.21.jsonl → repocandidates1821.jsonl} +0 -0
  290. /package/docs/{repo.research.1.8.16.md → reporesearch1816.md} +0 -0
  291. /package/docs/{repo.research.1.8.21.md → reporesearch1821.md} +0 -0
  292. /package/docs/{repo.selected.1.8.21.json → reposelected1821.json} +0 -0
  293. /package/docs/{repo.synthesis.1.8.21.md → reposynthesis1821.md} +0 -0
  294. /package/docs/{research.1.8.18.isolation.md → research1818isolation.md} +0 -0
  295. /package/docs/{research.1.8.19.virtual.browser.md → research1819virtualbrowser.md} +0 -0
  296. /package/docs/{scanningresearch.1.8.11.md → scanningresearch1811.md} +0 -0
  297. /package/docs/{securityaudit.1.8.7.md → securityaudit187.md} +0 -0
  298. /package/docs/{securityreferences.1.8.11.md → securityreferences1811.md} +0 -0
  299. /package/docs/{securityresearch.1.8.11.md → securityresearch1811.md} +0 -0
  300. /package/docs/{signingresearch.1.8.12.md → signingresearch1812.md} +0 -0
  301. /package/docs/{tauri.glib.risk.1.8.11.md → tauriglibrisk1811.md} +0 -0
  302. /package/docs/{todo.1.8.18.md → todo1818.md} +0 -0
  303. /package/docs/{todo.1.8.19.md → todo1819.md} +0 -0
  304. /package/docs/{toolchainresearch.1.8.9.md → toolchainresearch189.md} +0 -0
  305. /package/docs/{web.duplicatecleanup.md → webduplicatecleanup.md} +0 -0
  306. /package/docs/{web.ideas.md → webideas.md} +0 -0
  307. /package/docs/{workflowimprovements.2026.08.19.md → workflowimprovements20260819.md} +0 -0
@@ -1,566 +0,0 @@
1
- /**
2
- * automation.ts — workflow, bot and surface automation contracts.
3
- *
4
- * This file is the v2.0.0 grand-merge consolidation of the former nested
5
- * folders of the repository (automation) into the single
6
- * root-level domain file of the saddle family standard: one optimized
7
- * TypeScript file per correlated domain, ordinal sections, JSDoc on every
8
- * block, intra-domain imports dissolved, cross-domain imports rewritten to
9
- * ./<domain>.js. Every capability of every source file is preserved here;
10
- * the pure re-export barrels (none in this domain) folded their surface into this file directly.
11
- */
12
- /* ════════════════════════════════════════════════════════════════════ */
13
- /* Section 1: automation/triggers.ts — workflow triggers normalize manual, event, schedule and retry starts without binding a forge. */
14
- /* ════════════════════════════════════════════════════════════════════ */
15
- /**
16
- * workflow triggers normalize manual, event, schedule and retry starts without binding a forge.
17
- */
18
- export const triggernames = Object.freeze(["manual", "dispatch", "webhook", "schedule", "retry", "heartbeat"]);
19
- /** Validates and normalizes a trigger declaration. */
20
- export function workflowtriggers(value = ["manual"]) {
21
- const list = Array.isArray(value) ? value : [value];
22
- if (list.length === 0 || list.some((name) => !triggernames.includes(name)))
23
- throw new TypeError("workflow trigger is unsupported");
24
- return [...new Set(list)];
25
- }
26
- /** Matches a workflow trigger against an incoming event without executing it. */
27
- export function triggermatch(manifest, event = {}) {
28
- const triggers = workflowtriggers(manifest?.trigger);
29
- const type = String(event.type ?? "manual");
30
- if (!triggers.includes(type))
31
- return { matched: false, type, reason: "trigger-not-declared" };
32
- if (type === "schedule" && event.at !== undefined && Number(event.at) > Date.now())
33
- return { matched: false, type, reason: "not-due" };
34
- const validation = validateworkflowinputs(manifest, event.inputs ?? {});
35
- if (!validation.valid)
36
- return { matched: false, type, reason: "invalid-inputs", errors: validation.errors };
37
- return { matched: true, type, inputs: validation.values, requestid: event.requestid ?? `${manifest.name}/${type}/${stablevalue(event)}` };
38
- }
39
- /** Validates and normalizes caller-supplied workflow inputs against a manifest schema. */
40
- export function validateworkflowinputs(manifest, values = {}) {
41
- const schema = manifest?.inputs ?? {};
42
- const source = values && typeof values === "object" && !Array.isArray(values) ? values : {};
43
- const errors = [];
44
- const normalized = {};
45
- for (const name of Object.keys(source))
46
- if (!Object.hasOwn(schema, name))
47
- errors.push({ name, reason: "unknown-input" });
48
- for (const [name, declaration] of Object.entries(schema)) {
49
- const rule = typeof declaration === "string" ? { type: declaration } : declaration ?? {};
50
- let value = source[name];
51
- if (value === undefined && rule.default !== undefined)
52
- value = rule.default;
53
- if (value === undefined) {
54
- if (rule.required)
55
- errors.push({ name, reason: "required" });
56
- continue;
57
- }
58
- const converted = convertinput(value, rule.type ?? "string");
59
- if (converted.error) {
60
- errors.push({ name, reason: converted.error });
61
- continue;
62
- }
63
- if (Array.isArray(rule.choices) && !rule.choices.includes(converted.value)) {
64
- errors.push({ name, reason: "choice" });
65
- continue;
66
- }
67
- normalized[name] = converted.value;
68
- }
69
- return { valid: errors.length === 0, values: normalized, errors };
70
- }
71
- /** Keeps trigger declarations and produces deterministic matching results. */
72
- export function triggerregistry() {
73
- const values = new Map();
74
- function register(manifest) { if (!manifest?.name)
75
- throw new TypeError("trigger manifest requires name"); const normalized = { ...manifest, trigger: workflowtriggers(manifest.trigger) }; values.set(normalized.name, normalized); return normalized; }
76
- function get(name) { return values.get(name); }
77
- function match(name, event) { const manifest = get(name); if (!manifest)
78
- throw new Error(`workflow not found: ${name}`); return triggermatch(manifest, event); }
79
- function list() { return [...values.values()].map((manifest) => ({ ...manifest, trigger: [...manifest.trigger] })); }
80
- return { register, get, match, list };
81
- }
82
- function convertinput(value, type) {
83
- if (type === "string")
84
- return { value: String(value) };
85
- if (type === "number") {
86
- const number = typeof value === "number" ? value : Number(value);
87
- return Number.isFinite(number) ? { value: number } : { error: "number" };
88
- }
89
- if (type === "boolean") {
90
- if (value === true || value === "true")
91
- return { value: true };
92
- if (value === false || value === "false")
93
- return { value: false };
94
- return { error: "boolean" };
95
- }
96
- if (type === "json") {
97
- try {
98
- return { value: typeof value === "string" ? JSON.parse(value) : value };
99
- }
100
- catch {
101
- return { error: "json" };
102
- }
103
- }
104
- return { error: "type" };
105
- }
106
- function stablevalue(value) {
107
- if (Array.isArray(value))
108
- return `[${value.map(stablevalue).join(",")}]`;
109
- if (value && typeof value === "object")
110
- return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stablevalue(value[key])}`).join(",")}}`;
111
- return JSON.stringify(value);
112
- }
113
- /* ════════════════════════════════════════════════════════════════════ */
114
- /* Section 2: automation/permissions.ts — command permissions keep bot actions explicit and platform neutral. */
115
- /* ════════════════════════════════════════════════════════════════════ */
116
- /**
117
- * command permissions keep bot actions explicit and platform neutral.
118
- */
119
- /** Creates a command guard from caller supplied command and scope policies. */
120
- export function commandguard(options = {}) {
121
- const policies = options.policies ?? {};
122
- function check(input = {}) {
123
- const command = String(input.command ?? "");
124
- const policy = policies[command] ?? { scopes: [] };
125
- const scopes = input.scopes ?? [];
126
- const missing = (policy.scopes ?? []).filter((scope) => !scopes.includes(scope));
127
- return { allowed: missing.length === 0, command, missing, platform: input.platform };
128
- }
129
- return { check };
130
- }
131
- /* ════════════════════════════════════════════════════════════════════ */
132
- /* Section 3: automation/commands.ts — command parsing keeps the bot surface serializable and platform neutral. */
133
- /* ════════════════════════════════════════════════════════════════════ */
134
- /**
135
- * command parsing keeps the bot surface serializable and platform neutral.
136
- */
137
- export function parsecommand(input) {
138
- const tokens = tokenize(String(input ?? ""));
139
- const command = tokens.shift() ?? "help";
140
- const flags = {};
141
- for (let index = 0; index < tokens.length; index += 1) {
142
- const token = tokens[index];
143
- if (!token.startsWith("--"))
144
- continue;
145
- const key = token.slice(2).toLowerCase();
146
- const value = tokens[index + 1]?.startsWith("--") ? true : tokens[++index] ?? true;
147
- flags[key] = value;
148
- }
149
- return { command: command.toLowerCase(), flags };
150
- }
151
- function tokenize(input) { return input.match(/(?:[^\s"]+|"[^"]*")+/g)?.map((value) => value.replace(/^"|"$/g, "")) ?? []; }
152
- /* ════════════════════════════════════════════════════════════════════ */
153
- /* Section 4: automation/adapter.ts — platform adapters expose only the operations a bot needs. */
154
- /* ════════════════════════════════════════════════════════════════════ */
155
- /**
156
- * platform adapters expose only the operations a bot needs.
157
- */
158
- export function platformadapter(methods) {
159
- const required = ["authenticate", "listrepos", "createwebhook", "executebot"];
160
- for (const name of required)
161
- if (typeof methods?.[name] !== "function")
162
- throw new TypeError(`platform adapter requires ${name}`);
163
- return methods;
164
- }
165
- /* ════════════════════════════════════════════════════════════════════ */
166
- /* Section 5: automation/workflowmanifest.ts — workflow manifests describe the same job surface for different forge runtimes. */
167
- /* ════════════════════════════════════════════════════════════════════ */
168
- /**
169
- * workflow manifests describe the same job surface for different forge runtimes.
170
- */
171
- export const forgeprofiles = Object.freeze(["github", "forgejo", "gitea", "woodpecker", "gitlab"]);
172
- export function workflowmanifest(options = {}) {
173
- if (!options.name || !options.command)
174
- throw new TypeError("workflow manifest requires name and command");
175
- return {
176
- name: options.name,
177
- command: options.command,
178
- trigger: options.trigger ?? ["manual", "dispatch"],
179
- inputs: options.inputs ?? {},
180
- platforms: options.platforms ?? forgeprofiles,
181
- environment: options.environment ?? {},
182
- artifacts: options.artifacts ?? ["results/**"],
183
- timeoutminutes: options.timeoutminutes ?? 30,
184
- publicrunner: options.publicrunner ?? true
185
- };
186
- }
187
- export function workflowinputs(manifest) { return { name: manifest.name, command: manifest.command, timeoutminutes: manifest.timeoutminutes, artifacts: manifest.artifacts.join(",") }; }
188
- /* ════════════════════════════════════════════════════════════════════ */
189
- /* Section 6: automation/workflowtemplates.ts — workflow templates use explicit environment names and keep provider secrets external. */
190
- /* ════════════════════════════════════════════════════════════════════ */
191
- /**
192
- * workflow templates use explicit environment names and keep provider secrets external.
193
- */
194
- export function githubworkflow(manifest) {
195
- const input = workflowinputs(manifest);
196
- return `name: ${manifest.name}\non:\n workflow_dispatch:\n inputs:\n jobid:\n required: true\n type: string\n command:\n required: true\n type: string\n default: ${manifest.command}\n repository_dispatch:\n types: [saddle-job]\npermissions:\n contents: read\njobs:\n process:\n runs-on: ubuntu-latest\n timeout-minutes: ${input.timeoutminutes}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 26.12.0\n - run: npm ci\n - run: \${{ github.event.inputs.command || '${manifest.command}' }}\n env:\n SBOT_JOB_ID: \${{ github.event.inputs.jobid || github.event.client_payload.jobid }}\n - uses: actions/upload-artifact@v4\n with:\n name: saddle-results\n path: ${manifest.artifacts.join("\n ")}\n if-no-files-found: ignore\n`;
197
- }
198
- export function forgejoworkflow(manifest) { return genericworkflow(manifest, "forgejo"); }
199
- export function giteaworkflow(manifest) { return genericworkflow(manifest, "gitea"); }
200
- export function woodpeckerworkflow(manifest) {
201
- return `when:\n - event: push\n - event: manual\nsteps:\n process:\n image: node:26.12.0\n commands:\n - npm ci\n - ${manifest.command}\n`;
202
- }
203
- export function gitlabworkflow(manifest) { return `stages:\n - process\nprocess:\n stage: process\n image: node:26.12.0\n script:\n - npm ci\n - ${manifest.command}\n artifacts:\n when: always\n paths:\n${manifest.artifacts.map((item) => ` - ${item}`).join("\n")}\n`; }
204
- function genericworkflow(manifest, name) {
205
- return `name: ${manifest.name}\non:\n workflow_dispatch:\njobs:\n process:\n runs-on: ubuntu-latest\n timeout-minutes: ${manifest.timeoutminutes}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 26.12.0\n - run: npm ci\n - run: ${manifest.command}\n`;
206
- }
207
- /* ════════════════════════════════════════════════════════════════════ */
208
- /* Section 7: automation/workflowregistry.ts — workflow registry stores generated manifests and templates for later dispatch. */
209
- /* ════════════════════════════════════════════════════════════════════ */
210
- /**
211
- * workflow registry stores generated manifests and templates for later dispatch.
212
- */
213
- export function workflowregistry() {
214
- const manifests = new Map();
215
- const renderers = { github: githubworkflow, forgejo: forgejoworkflow, gitea: giteaworkflow, woodpecker: woodpeckerworkflow, gitlab: gitlabworkflow };
216
- return {
217
- register(manifest) { manifests.set(manifest.name, manifest); return manifest; },
218
- get(name) { return manifests.get(name) ?? null; },
219
- render(name, forge) { if (!renderers[forge] || !forgeprofiles.includes(forge))
220
- throw new TypeError(`unsupported forge: ${forge}`); const manifest = manifests.get(name); if (!manifest)
221
- throw new Error(`workflow not found: ${name}`); return renderers[forge](manifest); },
222
- list() { return [...manifests.values()]; }
223
- };
224
- }
225
- /* ════════════════════════════════════════════════════════════════════ */
226
- /* Section 8: automation/bot.ts — saddlebot unifies commands without owning a platform credential or scheduler. */
227
- /* ════════════════════════════════════════════════════════════════════ */
228
- /**
229
- * saddlebot unifies commands without owning a platform credential or scheduler.
230
- */
231
- export function saddlebot(options = {}) {
232
- const adapters = new Map(Object.entries(options.adapters ?? {}));
233
- const tasks = new Map();
234
- const guard = options.guard;
235
- const keys = new Map();
236
- const state = { status: "stopped", startedat: undefined, commands: 0 };
237
- const timers = new Set();
238
- async function executecommand(input, context = {}) {
239
- const parsed = typeof input === "string" ? parsecommand(input) : input;
240
- state.commands += 1;
241
- if (parsed.command === "status")
242
- return getstatus();
243
- if (parsed.command === "help")
244
- return { commands: ["capture", "review", "deploy", "memory", "test", "release", "webhook", "schedule", "publish", "artifact", "status"] };
245
- const platform = parsed.flags.platform ?? context.platform;
246
- const adapter = adapters.get(platform);
247
- if (!adapter)
248
- throw new Error(`no adapter registered for ${platform ?? "command"}`);
249
- const permission = guard?.check({ command: parsed.command, platform, scopes: context.scopes ?? [] });
250
- if (permission && !permission.allowed) {
251
- const error = new Error(`bot command is not authorized: ${parsed.command}`);
252
- error.code = "BOT_COMMAND_UNAUTHORIZED";
253
- error.missing = permission.missing;
254
- throw error;
255
- }
256
- const key = context.idempotencykey;
257
- if (key && keys.has(key))
258
- return keys.get(key);
259
- const result = await adapter.executebot({ command: parsed.command, flags: parsed.flags, context });
260
- if (key)
261
- keys.set(key, result);
262
- return result;
263
- }
264
- function getstatus() { return { ...state, adapters: [...adapters.keys()], tasks: [...tasks.keys()] }; }
265
- return {
266
- register(name, adapter) { adapters.set(name, adapter); return this; },
267
- async start() { state.status = "running"; state.startedat = Date.now(); return getstatus(); },
268
- async stop() { for (const timer of timers)
269
- clearTimeout(timer); timers.clear(); state.status = "stopped"; return getstatus(); },
270
- executecommand,
271
- async handlewebhook(platform, event) { const adapter = adapters.get(platform); if (!adapter)
272
- throw new Error(`no adapter registered for ${platform}`); return adapter.executebot({ command: "webhook", event }); },
273
- scheduletask(name, task, delay) { if (typeof task !== "function" || !Number.isFinite(delay))
274
- throw new TypeError("schedule requires task and delay"); const timer = setTimeout(async () => { tasks.delete(name); await task(); }, delay); timers.add(timer); tasks.set(name, { name, delay, scheduledat: Date.now() }); return tasks.get(name); },
275
- getstatus
276
- };
277
- }
278
- /* ════════════════════════════════════════════════════════════════════ */
279
- /* Section 9: automation/n8n.ts — n8n node metadata keeps workflow automation as a packaging surface. */
280
- /* ════════════════════════════════════════════════════════════════════ */
281
- /**
282
- * n8n node metadata keeps workflow automation as a packaging surface.
283
- */
284
- export function n8nnode(options = {}) {
285
- const triggers = normalize(options.triggers ?? n8ntriggers);
286
- const actions = normalize(options.actions ?? n8nactions);
287
- return { name: options.name ?? "saddle", displayname: options.displayname ?? "Saddle", description: options.description ?? "Saddle engine operation", version: 1, inputs: options.inputs ?? ["main"], outputs: options.outputs ?? ["main"], triggers, actions, properties: options.properties ?? [{ displayname: "trigger", name: "trigger", type: "options", options: triggers.map((value) => ({ name: value, value })) }, { displayname: "command", name: "command", type: "options", options: actions.map((value) => ({ name: value, value })), default: "status" }] };
288
- }
289
- export const n8ntriggers = Object.freeze(["manual", "dispatch", "webhook", "schedule", "retry", "heartbeat"]);
290
- export const n8nactions = Object.freeze(["status", "browser", "memory", "sync"]);
291
- /** Matches an incoming event against the trigger declarations of a node. */
292
- export function n8nmatch(node, event = {}) { const type = String(event.type ?? "manual"); return { matched: Boolean(node?.triggers?.includes(type)), type, requestid: event.requestid ?? `${node?.name ?? "saddle"}/${type}` }; }
293
- /** Executes a declared n8n action through a caller-owned handler. */
294
- export async function n8nexecute(node, input, handler) {
295
- if (typeof handler !== "function")
296
- throw new TypeError("n8n handler is required");
297
- const action = String(input?.action ?? input?.command ?? "status");
298
- if (!node?.actions?.includes(action))
299
- throw new TypeError(`unsupported n8n action: ${action}`);
300
- try {
301
- return await handler({ node, input: { ...(input ?? {}), action } });
302
- }
303
- catch (error) {
304
- const failure = new Error(`n8n action failed: ${errormessage(error)}`, { cause: error });
305
- failure.code = String(error?.code ?? "N8N_ACTION_FAILED");
306
- throw failure;
307
- }
308
- }
309
- function normalize(values) { const list = Array.isArray(values) ? values : [values]; if (!list.length || list.some((value) => typeof value !== "string" || !value))
310
- throw new TypeError("n8n declarations are invalid"); return [...new Set(list)]; }
311
- /* ════════════════════════════════════════════════════════════════════ */
312
- /* Section 10: automation/surfacemanifest.ts — surface manifests describe repackaging targets without adding target code to the engine. */
313
- /* ════════════════════════════════════════════════════════════════════ */
314
- /**
315
- * surface manifests describe repackaging targets without adding target code to the engine.
316
- */
317
- export const surfaces = Object.freeze(["application", "computer", "browser", "extension", "desktop", "mobile", "cli", "binary", "web", "internet", "libreoffice", "mcp", "vsix", "container", "n8n", "library"]);
318
- export const surfaceformats = Object.freeze({
319
- library: Object.freeze(["npm", "github", "maven", "nuget", "rubygems", "oci", "tarball"]),
320
- application: Object.freeze(["zip", "tarball"]),
321
- computer: Object.freeze(["node", "bun", "deno", "singlefile"]),
322
- desktop: Object.freeze(["appimage", "deb", "rpm", "snap", "flatpak", "dmg", "pkg", "exe", "msi", "msix"]),
323
- mobile: Object.freeze(["apk", "aab", "ipa"]),
324
- browser: Object.freeze(["html", "pwa", "wasm"]),
325
- extension: Object.freeze(["crx", "xpi", "safariextz"]),
326
- cli: Object.freeze(["node", "bun", "deno", "singlefile", "exe", "deb", "rpm"]),
327
- binary: Object.freeze(["sea", "singlefile", "wasm"]),
328
- web: Object.freeze(["html", "pwa", "ssg", "ssr", "wasm"]),
329
- internet: Object.freeze(["http", "websocket", "grpc", "rest"]),
330
- libreoffice: Object.freeze(["oxt", "zip"]),
331
- mcp: Object.freeze(["stdio", "jsonrpc"]),
332
- vsix: Object.freeze(["vsix"]),
333
- container: Object.freeze(["oci", "docker"]),
334
- n8n: Object.freeze(["node", "json"])
335
- });
336
- export function surfacemanifest(options = {}) {
337
- const target = options.target ?? "library";
338
- if (!surfaces.includes(target))
339
- throw new TypeError(`unsupported surface: ${target}`);
340
- return { target, name: options.name ?? "saddle", entry: options.entry ?? "dist/index.js", runtime: options.runtime ?? "caller", formats: [...(options.formats ?? surfaceformats[target] ?? [])], permissions: options.permissions ?? [], capabilities: options.capabilities ?? ["memory", "workflow"], metadata: options.metadata ?? {} };
341
- }
342
- /** Creates a desktop surface manifest with caller-selected packaging formats. */
343
- export function desktopmanifest(options = {}) { return surfacemanifest({ ...options, target: "desktop", formats: options.formats ?? surfaceformats.desktop }); }
344
- /** Creates a mobile surface manifest with caller-selected packaging formats. */
345
- export function mobilemanifest(options = {}) { return surfacemanifest({ ...options, target: "mobile", formats: options.formats ?? surfaceformats.mobile }); }
346
- /** Creates a caller-owned target manifest for any supported surface. */
347
- export function applicationmanifest(options = {}) { return surfacemanifest({ ...options, target: options.target ?? "application" }); }
348
- /** Describes installation without performing a package or platform mutation. */
349
- export function surfacebundle(manifest) {
350
- if (!manifest?.target || !surfaces.includes(manifest.target))
351
- throw new TypeError("surface bundle requires a valid manifest");
352
- const install = { n8n: "n8n import", browser: "import by url", extension: "load unpacked", desktop: "caller desktop bundle", mobile: "caller mobile bundle" }[manifest.target] ?? "npm install";
353
- return { ...manifest, files: [...new Set([manifest.entry, ...(manifest.files ?? [])])], install };
354
- }
355
- /* ════════════════════════════════════════════════════════════════════ */
356
- /* Section 11: automation/surfaceadapters.ts — surface adapters define caller-owned desktop and mobile operations without selecting a vendor runtime. */
357
- /* ════════════════════════════════════════════════════════════════════ */
358
- /**
359
- * surface adapters define caller-owned desktop and mobile operations without selecting a vendor runtime.
360
- */
361
- const profiles = Object.freeze({
362
- desktop: { capabilities: ["window", "file", "notification"], formats: ["appimage", "dmg", "msi"] },
363
- mobile: { capabilities: ["screen", "storage", "network"], formats: ["apk", "ipa"] }
364
- });
365
- /** Creates a transport-neutral adapter contract for a desktop or mobile surface. */
366
- export function surfaceadapter(options = {}) {
367
- const target = String(options.target ?? "");
368
- const profile = profiles[target];
369
- if (!profile)
370
- throw new TypeError(`unsupported surface adapter: ${target}`);
371
- const operations = [...new Set(options.operations ?? ["open", "close", "invoke", "status"])]
372
- .map((operation) => String(operation));
373
- if (!operations.length || operations.some((operation) => !/^[a-z][a-z0-9]*$/.test(operation)))
374
- throw new TypeError("surface adapter operations are invalid");
375
- const handlers = { ...(options.handlers ?? {}) };
376
- for (const [operation, handler] of Object.entries(handlers))
377
- if (typeof handler !== "function")
378
- throw new TypeError(`surface handler is not callable: ${operation}`);
379
- async function invoke(operation, input, context = {}) {
380
- const name = String(operation ?? "");
381
- if (!operations.includes(name))
382
- throw new TypeError(`unsupported surface operation: ${name}`);
383
- const handler = handlers[name];
384
- if (!handler)
385
- return { target, operation: name, supported: false, result: undefined };
386
- try {
387
- return { target, operation: name, supported: true, result: await handler(input, context) };
388
- }
389
- catch (error) {
390
- return { target, operation: name, supported: true, ok: false, code: String(error?.code ?? "SURFACE_OPERATION_FAILED"), message: errormessage(error) };
391
- }
392
- }
393
- return {
394
- target,
395
- version: 1,
396
- capabilities: [...(options.capabilities ?? profile.capabilities)],
397
- formats: [...(options.formats ?? profile.formats)],
398
- operations,
399
- invoke,
400
- status: () => ({ target, ready: true, operations: [...operations], capabilities: [...(options.capabilities ?? profile.capabilities)] })
401
- };
402
- }
403
- /** Creates a desktop adapter contract with caller-owned handlers. */
404
- export function desktopadapter(options = {}) { return surfaceadapter({ ...options, target: "desktop" }); }
405
- /** Creates a mobile adapter contract with caller-owned handlers. */
406
- export function mobileadapter(options = {}) { return surfaceadapter({ ...options, target: "mobile" }); }
407
- /* ════════════════════════════════════════════════════════════════════ */
408
- /* Section 12: automation/surfacecontrols.ts — control surfaces provide an operator contract for jobs, sessions, storage, runners, permissions, logs and artifacts. */
409
- /* ════════════════════════════════════════════════════════════════════ */
410
- /**
411
- * control surfaces provide an operator contract for jobs, sessions, storage, runners, permissions, logs and artifacts.
412
- */
413
- export const controlresources = Object.freeze(["jobs", "sessions", "storage", "runners", "permissions", "logs", "artifacts"]);
414
- export const controloperations = Object.freeze(["list", "get", "create", "update", "cancel", "retry", "delete", "check"]);
415
- /** Creates a caller-owned operator surface with resource handlers and optional audit recording. */
416
- export function controlsurface(options = {}) {
417
- const adapters = { ...(options.adapters ?? {}) };
418
- const audit = options.audit;
419
- if (audit !== undefined && typeof audit !== "function")
420
- throw new TypeError("control audit must be callable");
421
- for (const resource of Object.keys(adapters))
422
- if (!controlresources.includes(resource) || !adapters[resource] || typeof adapters[resource] !== "object")
423
- throw new TypeError(`unsupported control resource: ${resource}`);
424
- async function execute(request = {}) {
425
- const resource = String(request.resource ?? "");
426
- const operation = String(request.operation ?? "");
427
- if (!controlresources.includes(resource) || !controloperations.includes(operation))
428
- throw new TypeError("control request is invalid");
429
- const handler = adapters[resource]?.[operation];
430
- const context = { requestid: String(request.requestid ?? `control${Date.now().toString(36)}`), resource, operation };
431
- if (typeof handler !== "function")
432
- return respond({ ok: false, code: "UNSUPPORTED_CONTROL", message: `${resource}.${operation} is not configured`, context, audit });
433
- try {
434
- const result = await handler(request.input ?? {}, context);
435
- return respond({ ok: true, result, context, audit });
436
- }
437
- catch (error) {
438
- return respond({ ok: false, code: String(error?.code ?? "CONTROL_FAILED"), message: errormessage(error), context, audit });
439
- }
440
- }
441
- return { version: 1, resources: [...controlresources], operations: [...controloperations], execute, describe: () => ({ version: 1, resources: [...controlresources], configured: Object.keys(adapters).filter((resource) => controlresources.includes(resource)) }) };
442
- }
443
- /** The message a caught value carries for the wire: an Error answers its message only (a property read the exposure model clears), and every other shape answers the fallback — the caught value as a whole never rides the payload, not even a thrown string (the object taint the exposure flow pinned walked exactly that lane). */
444
- const errormessage = (error, fallback = "operation failed") => {
445
- if (error instanceof Error)
446
- return error.message;
447
- return fallback;
448
- };
449
- async function respond({ ok, result, code, message, context, audit }) {
450
- const response = { version: 1, ok, ...context, ...(ok ? { result } : { code, message }) };
451
- if (audit)
452
- try {
453
- await audit(response);
454
- }
455
- catch (error) {
456
- response.auditerror = errormessage(error);
457
- }
458
- return response;
459
- }
460
- /* ════════════════════════════════════════════════════════════════════ */
461
- /* Section 13: automation/surfaceoperations.ts — operations describe bounded telemetry, retention, recovery and threat boundaries without selecting storage or telemetry vendors. */
462
- /* ════════════════════════════════════════════════════════════════════ */
463
- /**
464
- * operations describe bounded telemetry, retention, recovery and threat boundaries without selecting storage or telemetry vendors.
465
- */
466
- export const operationmetrics = Object.freeze(["latency", "retries", "queuedepth", "runnerselection", "storagebytes", "failures"]);
467
- /** Binds the standard operational metric names to an injected metric collector. */
468
- export function operationsmetrics(options = {}) {
469
- const collector = options.collector;
470
- if (!collector || typeof collector.count !== "function" || typeof collector.observe !== "function" || typeof collector.snapshot !== "function")
471
- throw new TypeError("operations metrics requires a collector");
472
- function record(name, value = 1, labels = {}) {
473
- if (!operationmetrics.includes(name))
474
- throw new TypeError(`unsupported operation metric: ${name}`);
475
- return name === "latency" ? collector.observe(name, value, labels) : collector.count(name, value, labels);
476
- }
477
- return { names: [...operationmetrics], record, snapshot: () => collector.snapshot() };
478
- }
479
- /** Creates a deterministic retention policy for caller-owned records. */
480
- export function retentionpolicy(options = {}) {
481
- const days = Number(options.days ?? 30);
482
- const maxbytes = Number(options.maxbytes ?? Number.POSITIVE_INFINITY);
483
- if (!Number.isFinite(days) || days < 0 || !Number.isFinite(maxbytes) || maxbytes < 0)
484
- throw new TypeError("retention policy limits are invalid");
485
- return { days, maxbytes, cutoff(now = Date.now()) { return Number(now) - days * 86400000; }, keeps(record, now = Date.now()) { return Number(record?.updatedat ?? record?.createdat ?? 0) >= this.cutoff(now) && Number(record?.bytes ?? 0) <= maxbytes; } };
486
- }
487
- /** Defines caller-owned backup and restore functions with explicit capability errors. */
488
- export function backupplan(options = {}) {
489
- const backup = options.backup;
490
- const restore = options.restore;
491
- return { version: 1, backup: async (input) => execute(backup, input, "backup"), restore: async (input) => execute(restore, input, "restore") };
492
- }
493
- /** Records the security boundary and prevents the core from claiming threat coverage it does not own. */
494
- export function threatmodel(options = {}) {
495
- const boundaries = [...new Set((options.boundaries ?? ["credentials", "network", "permissions", "persistence"]).map(String))];
496
- if (!boundaries.length)
497
- throw new TypeError("threat model requires boundaries");
498
- return { version: 1, boundaries, owner: options.owner ?? "caller", controls: [...new Set((options.controls ?? []).map(String))], disclaimer: "The caller remains responsible for deployment, credentials, service terms and abuse response." };
499
- }
500
- async function execute(handler, input, name) {
501
- if (typeof handler !== "function") {
502
- const error = new Error(`${name} handler is not configured`);
503
- error.code = "UNSUPPORTED_OPERATION";
504
- throw error;
505
- }
506
- try {
507
- return await handler(input);
508
- }
509
- catch (error) {
510
- const failure = new Error(`${name} failed: ${errormessage(error)}`, { cause: error });
511
- failure.code = String(error?.code ?? "OPERATION_FAILED");
512
- throw failure;
513
- }
514
- }
515
- /* ════════════════════════════════════════════════════════════════════ */
516
- /* Section 14: automation/surfacerequirements.ts — application surface requirements describe Mini App, DNS, and bridge needs without owning credentials or infrastructure. */
517
- /* ════════════════════════════════════════════════════════════════════ */
518
- /**
519
- * application surface requirements describe Mini App, DNS, and bridge needs without owning credentials or infrastructure.
520
- */
521
- /** Creates a Mini App plan that requires caller-owned origin and validation. */
522
- export function miniappplan(input = {}) {
523
- if ("token" in input || "bottoken" in input || "secret" in input)
524
- throw new TypeError("Mini App credentials must not be supplied to the library plan");
525
- const origin = publichttps(input.origin, "Mini App origin");
526
- const validation = nonempty(input.validation, "Mini App validation");
527
- return Object.freeze({ version: 1, origin, validation, capabilities: Object.freeze(unique(input.capabilities ?? [])), state: "caller-validates", provider: String(input.provider ?? "telegram") });
528
- }
529
- /** Creates a DNS and HTTPS requirement record without a registrar or DNS operation. */
530
- export function dnsplan(input = {}) {
531
- const hostname = hostnamevalue(input.hostname);
532
- const dnssec = input.dnssec === true;
533
- const https = input.https === true;
534
- return Object.freeze({ version: 1, hostname, dnssec, https, owner: nonempty(input.owner, "DNS owner"), state: "caller-configures", operations: Object.freeze(["record", "certificate", ...(dnssec ? ["dnssec"] : [])]) });
535
- }
536
- /** Maps declared application surfaces without selecting a runtime vendor. */
537
- export function applicationbridge(input = {}) {
538
- if (!Array.isArray(input.surfaces) || input.surfaces.length === 0)
539
- throw new TypeError("application bridge surfaces are required");
540
- const ids = new Set();
541
- const surfaces = input.surfaces.map((value) => {
542
- const id = nonempty(value?.id, "application bridge surface id");
543
- if (ids.has(id))
544
- throw new TypeError(`application bridge surface id is duplicated: ${id}`);
545
- ids.add(id);
546
- const target = String(value.target ?? "");
547
- if (!targets.has(target))
548
- throw new TypeError(`application bridge target is invalid: ${target}`);
549
- return Object.freeze({ id, target, capabilities: Object.freeze(unique(value.capabilities ?? [])) });
550
- });
551
- return Object.freeze({ version: 1, surfaces: Object.freeze(surfaces), state: "caller-connects" });
552
- }
553
- const targets = new Set(["browser", "desktop", "mobile", "extension", "miniapp"]);
554
- function publichttps(value, name) { const output = String(value ?? ""); let url; try {
555
- url = new URL(output);
556
- }
557
- catch {
558
- throw new TypeError(`${name} is invalid`);
559
- } if (url.protocol !== "https:" || !url.hostname)
560
- throw new TypeError(`${name} must use HTTPS`); return url.toString(); }
561
- function hostnamevalue(value) { const output = String(value ?? "").toLowerCase(); if (!/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/.test(output))
562
- throw new TypeError("DNS hostname is invalid"); return output; }
563
- function nonempty(value, name) { const output = String(value ?? ""); if (!output)
564
- throw new TypeError(`${name} is required`); return output; }
565
- function unique(values) { if (!Array.isArray(values))
566
- throw new TypeError("surface capabilities must be an array"); return [...new Set(values.map((value) => nonempty(value, "surface capability")))].sort(); }