stageflow 0.27.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +4 -0
  2. package/builtin-skills/browser/SKILL.md +42 -0
  3. package/dist/agent/claudeAdapter.js +2 -2
  4. package/dist/agent/cursorProvider.d.ts +2 -0
  5. package/dist/agent/cursorProvider.js +30 -5
  6. package/dist/agent/fakeAgent.d.ts +1 -0
  7. package/dist/agent/fakeAgent.js +13 -0
  8. package/dist/agent/piAdapter.d.ts +3 -24
  9. package/dist/agent/piAdapter.js +49 -47
  10. package/dist/agent/piSessionFactory.d.ts +38 -0
  11. package/dist/agent/piSessionFactory.js +60 -0
  12. package/dist/agent/port.d.ts +2 -0
  13. package/dist/agent/streamLogRedact.d.ts +6 -0
  14. package/dist/agent/streamLogRedact.js +23 -1
  15. package/dist/browser/anchor.d.ts +27 -0
  16. package/dist/browser/anchor.js +127 -0
  17. package/dist/browser/auditSink.d.ts +38 -0
  18. package/dist/browser/auditSink.js +34 -0
  19. package/dist/browser/browserHost.d.ts +86 -0
  20. package/dist/browser/browserHost.js +7 -0
  21. package/dist/browser/browserSweep.d.ts +14 -0
  22. package/dist/browser/browserSweep.js +54 -0
  23. package/dist/browser/browserTeardown.d.ts +56 -0
  24. package/dist/browser/browserTeardown.js +235 -0
  25. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
  26. package/dist/browser/fakeRemoteBrowserHost.js +34 -0
  27. package/dist/browser/gateHandoff.d.ts +25 -0
  28. package/dist/browser/gateHandoff.js +83 -0
  29. package/dist/browser/humanLogin.d.ts +18 -0
  30. package/dist/browser/humanLogin.js +34 -0
  31. package/dist/browser/keyProvider.d.ts +10 -0
  32. package/dist/browser/keyProvider.js +39 -0
  33. package/dist/browser/localBrowserHost.d.ts +10 -0
  34. package/dist/browser/localBrowserHost.js +152 -0
  35. package/dist/browser/localProfileLock.d.ts +2 -0
  36. package/dist/browser/localProfileLock.js +218 -0
  37. package/dist/browser/localProfileStore.d.ts +5 -0
  38. package/dist/browser/localProfileStore.js +91 -0
  39. package/dist/browser/loginCheck.d.ts +53 -0
  40. package/dist/browser/loginCheck.js +147 -0
  41. package/dist/browser/memoryProfileLock.d.ts +2 -0
  42. package/dist/browser/memoryProfileLock.js +53 -0
  43. package/dist/browser/memoryProfileStore.d.ts +2 -0
  44. package/dist/browser/memoryProfileStore.js +31 -0
  45. package/dist/browser/navigationAudit.d.ts +16 -0
  46. package/dist/browser/navigationAudit.js +53 -0
  47. package/dist/browser/persistedEnv.d.ts +3 -0
  48. package/dist/browser/persistedEnv.js +15 -0
  49. package/dist/browser/profileLock.d.ts +40 -0
  50. package/dist/browser/profileLock.js +10 -0
  51. package/dist/browser/profileStore.d.ts +23 -0
  52. package/dist/browser/profileStore.js +39 -0
  53. package/dist/browser/runLiveness.d.ts +13 -0
  54. package/dist/browser/runLiveness.js +39 -0
  55. package/dist/browser/sitePolicy.d.ts +10 -0
  56. package/dist/browser/sitePolicy.js +61 -0
  57. package/dist/browser/stageBrowserEnv.d.ts +22 -0
  58. package/dist/browser/stageBrowserEnv.js +147 -0
  59. package/dist/browser/stageProfileLock.d.ts +17 -0
  60. package/dist/browser/stageProfileLock.js +39 -0
  61. package/dist/cli/browserCommand.d.ts +44 -0
  62. package/dist/cli/browserCommand.js +472 -0
  63. package/dist/cli/hostClient.d.ts +20 -0
  64. package/dist/cli/hostClient.js +36 -2
  65. package/dist/cli/initCommand.d.ts +1 -0
  66. package/dist/cli/initCommand.js +23 -7
  67. package/dist/cli/initTemplates.d.ts +5 -2
  68. package/dist/cli/initTemplates.js +25 -2
  69. package/dist/cli/runsCommand.js +6 -2
  70. package/dist/cli/triggerCommand.d.ts +12 -0
  71. package/dist/cli/triggerCommand.js +183 -0
  72. package/dist/cli/validateCommand.d.ts +1 -1
  73. package/dist/cli/validateCommand.js +23 -6
  74. package/dist/cli.js +31 -2
  75. package/dist/config/browseCatalog.d.ts +1 -0
  76. package/dist/config/browseCatalog.js +5 -4
  77. package/dist/config/builtinSkills.d.ts +4 -0
  78. package/dist/config/builtinSkills.js +23 -0
  79. package/dist/config/catalogRelativePath.d.ts +1 -0
  80. package/dist/config/catalogRelativePath.js +10 -2
  81. package/dist/config/createTrigger.d.ts +27 -0
  82. package/dist/config/createTrigger.js +232 -0
  83. package/dist/config/draftPackage.d.ts +117 -0
  84. package/dist/config/draftPackage.js +550 -0
  85. package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
  86. package/dist/config/ensureCatalogScanRoot.js +73 -0
  87. package/dist/config/hostConfig.d.ts +2 -0
  88. package/dist/config/hostConfig.js +27 -0
  89. package/dist/config/listSkills.d.ts +2 -2
  90. package/dist/config/listSkills.js +17 -3
  91. package/dist/config/loadOutcome.d.ts +6 -0
  92. package/dist/config/loadPipeline.js +16 -0
  93. package/dist/config/loadStage.js +7 -0
  94. package/dist/config/loadStageflowManifest.js +25 -0
  95. package/dist/config/loadTrigger.d.ts +9 -0
  96. package/dist/config/loadTrigger.js +120 -0
  97. package/dist/config/multiProjectCatalog.d.ts +2 -0
  98. package/dist/config/multiProjectCatalog.js +26 -18
  99. package/dist/config/normalizePipelineStageEntry.js +8 -1
  100. package/dist/config/parseCompletionContract.js +6 -0
  101. package/dist/config/pipelineStageKeys.js +1 -0
  102. package/dist/config/publishDraftPackage.d.ts +7 -0
  103. package/dist/config/publishDraftPackage.js +25 -0
  104. package/dist/config/resolveCatalogContext.js +1 -1
  105. package/dist/config/resolvePipelineDag.js +1 -1
  106. package/dist/config/scanCatalogPaths.d.ts +1 -1
  107. package/dist/config/scanCatalogPaths.js +6 -2
  108. package/dist/config/stageBrowser.d.ts +12 -0
  109. package/dist/config/stageBrowser.js +118 -0
  110. package/dist/config/triggerCatalog.d.ts +10 -0
  111. package/dist/config/triggerCatalog.js +37 -0
  112. package/dist/config/validateCatalog.d.ts +13 -3
  113. package/dist/config/validateCatalog.js +83 -2
  114. package/dist/config/validateCompletionContract.js +8 -0
  115. package/dist/envelope/preEmitChecks.d.ts +1 -1
  116. package/dist/envelope/preEmitChecks.js +8 -0
  117. package/dist/logging/redact.d.ts +4 -0
  118. package/dist/logging/redact.js +17 -0
  119. package/dist/mcp/catalogTools.js +43 -8
  120. package/dist/mcp/controlTools.js +3 -1
  121. package/dist/mcp/deps.d.ts +2 -0
  122. package/dist/mcp/server.js +1 -0
  123. package/dist/mcp/tools.js +2 -0
  124. package/dist/mcp/triggerTools.d.ts +3 -0
  125. package/dist/mcp/triggerTools.js +176 -0
  126. package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
  127. package/dist/operatorAgent/docsRetrieval.js +269 -0
  128. package/dist/operatorAgent/draftContext.d.ts +16 -0
  129. package/dist/operatorAgent/draftContext.js +68 -0
  130. package/dist/operatorAgent/fakeHost.d.ts +39 -0
  131. package/dist/operatorAgent/fakeHost.js +92 -0
  132. package/dist/operatorAgent/host.d.ts +40 -0
  133. package/dist/operatorAgent/host.js +179 -0
  134. package/dist/operatorAgent/index.d.ts +9 -0
  135. package/dist/operatorAgent/index.js +8 -0
  136. package/dist/operatorAgent/piModel.d.ts +80 -0
  137. package/dist/operatorAgent/piModel.js +528 -0
  138. package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
  139. package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
  140. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
  141. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
  142. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
  143. package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
  144. package/dist/operatorAgent/proposals.d.ts +32 -0
  145. package/dist/operatorAgent/proposals.js +144 -0
  146. package/dist/operatorAgent/types.d.ts +128 -0
  147. package/dist/operatorAgent/types.js +1 -0
  148. package/dist/package-meta.d.ts +1 -1
  149. package/dist/package-meta.js +1 -1
  150. package/dist/project/findManifestRoot.d.ts +3 -0
  151. package/dist/project/findManifestRoot.js +35 -0
  152. package/dist/project/resolveStageflowContext.d.ts +7 -0
  153. package/dist/project/resolveStageflowContext.js +44 -28
  154. package/dist/runstore/assertTmpdir.js +6 -2
  155. package/dist/runstore/port.d.ts +28 -0
  156. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -1
  157. package/dist/runstore/sqlite/SqliteRunStore.js +90 -0
  158. package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
  159. package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
  160. package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
  161. package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
  162. package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
  163. package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
  164. package/dist/runstore/sqlite/migrations/index.js +6 -0
  165. package/dist/runtime/answerResume.d.ts +1 -0
  166. package/dist/runtime/answerResume.js +3 -1
  167. package/dist/runtime/completionCheckRunner.d.ts +12 -1
  168. package/dist/runtime/completionCheckRunner.js +22 -0
  169. package/dist/runtime/credentialBinding.d.ts +2 -0
  170. package/dist/runtime/credentialBinding.js +3 -0
  171. package/dist/runtime/emailSource.d.ts +72 -0
  172. package/dist/runtime/emailSource.js +304 -0
  173. package/dist/runtime/githubPollSource.d.ts +86 -0
  174. package/dist/runtime/githubPollSource.js +284 -0
  175. package/dist/runtime/pipelineRunner.d.ts +4 -0
  176. package/dist/runtime/pipelineRunner.js +3 -0
  177. package/dist/runtime/pipelineScheduler.d.ts +2 -0
  178. package/dist/runtime/pipelineScheduler.js +85 -1
  179. package/dist/runtime/repositoryMaterialize.d.ts +1 -1
  180. package/dist/runtime/runManager.d.ts +11 -0
  181. package/dist/runtime/runManager.js +149 -1
  182. package/dist/runtime/runRetryCoordinator.d.ts +2 -0
  183. package/dist/runtime/runRetryCoordinator.js +1 -0
  184. package/dist/runtime/scheduleSource.d.ts +38 -0
  185. package/dist/runtime/scheduleSource.js +119 -0
  186. package/dist/runtime/settingsFile.d.ts +5 -0
  187. package/dist/runtime/settingsFile.js +61 -54
  188. package/dist/runtime/stageAttemptBootstrap.js +55 -3
  189. package/dist/runtime/stageEnvironment.js +1 -0
  190. package/dist/runtime/stageHitl.d.ts +1 -0
  191. package/dist/runtime/stageHitl.js +5 -1
  192. package/dist/runtime/stageProcessLauncher.d.ts +2 -0
  193. package/dist/runtime/stageProcessLauncher.js +44 -5
  194. package/dist/runtime/stageRunner.d.ts +5 -0
  195. package/dist/runtime/stageRunner.js +30 -6
  196. package/dist/runtime/stageStreamLog.d.ts +1 -0
  197. package/dist/runtime/stageStreamLog.js +2 -1
  198. package/dist/runtime/triggerCatalog.d.ts +6 -0
  199. package/dist/runtime/triggerCatalog.js +24 -0
  200. package/dist/runtime/triggerEventMatch.d.ts +2 -0
  201. package/dist/runtime/triggerEventMatch.js +6 -0
  202. package/dist/runtime/triggerPort.d.ts +14 -0
  203. package/dist/runtime/triggerPort.js +1 -0
  204. package/dist/runtime/triggerRunner.d.ts +17 -0
  205. package/dist/runtime/triggerRunner.js +89 -0
  206. package/dist/runtime/verifiedStageExecution.d.ts +7 -0
  207. package/dist/runtime/verifiedStageExecution.js +14 -0
  208. package/dist/runtime/webhookSignature.d.ts +1 -0
  209. package/dist/runtime/webhookSignature.js +12 -0
  210. package/dist/server/bootstrap.d.ts +6 -0
  211. package/dist/server/bootstrap.js +64 -0
  212. package/dist/server/controlToken.js +13 -0
  213. package/dist/server/createHttpHost.js +3 -0
  214. package/dist/server/healthSurfaces.js +31 -6
  215. package/dist/server/http.d.ts +15 -0
  216. package/dist/server/http.js +1251 -74
  217. package/dist/server/requestAuthContext.d.ts +1 -1
  218. package/dist/server/triggerRoutes.d.ts +15 -0
  219. package/dist/server/triggerRoutes.js +268 -0
  220. package/dist/tools/askOperator.d.ts +3 -2
  221. package/dist/types/completion.d.ts +3 -0
  222. package/dist/types/pipeline.d.ts +3 -1
  223. package/dist/types/preEmitCheck.d.ts +7 -0
  224. package/dist/types/stage.d.ts +14 -0
  225. package/dist/types/stageflowManifest.d.ts +3 -0
  226. package/dist/types/trigger.d.ts +18 -0
  227. package/dist/types/trigger.js +1 -0
  228. package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
  229. package/dist/ui/assets/index-CntB4bZR.js +133 -0
  230. package/dist/ui/index.html +2 -2
  231. package/dist/workshop/autosave.d.ts +58 -0
  232. package/dist/workshop/autosave.js +222 -0
  233. package/dist/workshop/buildStore.d.ts +79 -0
  234. package/dist/workshop/buildStore.js +326 -0
  235. package/dist/workshop/chatTurn.d.ts +232 -0
  236. package/dist/workshop/chatTurn.js +475 -0
  237. package/dist/workshop/modelSettings.d.ts +18 -0
  238. package/dist/workshop/modelSettings.js +44 -0
  239. package/dist/workshop/sessionStore.d.ts +61 -0
  240. package/dist/workshop/sessionStore.js +254 -0
  241. package/dist/workshop/workshopPin.d.ts +72 -0
  242. package/dist/workshop/workshopPin.js +193 -0
  243. package/examples/README.md +2 -1
  244. package/examples/browser-session/README.md +77 -0
  245. package/examples/browser-session/browser-session.pipeline.yaml +50 -0
  246. package/examples/browser-session/browser-session.task.yaml +6 -0
  247. package/examples/browser-session/check-login.yaml +22 -0
  248. package/examples/browser-session/fixture-server.mjs +64 -0
  249. package/examples/browser-session/human-login.yaml +31 -0
  250. package/examples/browser-session/merge.yaml +36 -0
  251. package/examples/browser-session/work-a.yaml +41 -0
  252. package/examples/browser-session/work-b.yaml +41 -0
  253. package/examples/stage-mcp/README.md +3 -2
  254. package/examples/stageflow.yaml +51 -0
  255. package/package.json +5 -1
  256. package/skills/stageflow/references/control-surface.md +3 -1
  257. package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
  258. package/skills/stageflow-run/SKILL.md +2 -0
  259. package/skills/stageflow-run/references/task-and-pipeline-selection.md +3 -1
  260. package/skills/stageflow-setup/SKILL.md +11 -0
  261. package/dist/ui/assets/index-B6hlSXXn.js +0 -120
@@ -0,0 +1,232 @@
1
+ import { mkdir, realpath, unlink, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { Cron } from "croner";
4
+ import { stringify as stringifyYaml } from "yaml";
5
+ import { STAGE_ID_PATTERN } from "./createStage.js";
6
+ import { getCatalogScanPaths } from "./browseCatalog.js";
7
+ import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
8
+ import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
9
+ import { loadTriggerOutcome } from "./loadTrigger.js";
10
+ import { collectPipelineIdsFromPaths, collectTaskIdsFromPaths } from "./validateCatalog.js";
11
+ import { findCatalogPathById } from "../runtime/triggerCatalog.js";
12
+ function isPlainObject(value) {
13
+ return value !== null && typeof value === "object" && !Array.isArray(value);
14
+ }
15
+ function validateTriggerId(id) {
16
+ if (id.length === 0 || id.length > 64) {
17
+ return "id must be 1-64 characters";
18
+ }
19
+ if (!STAGE_ID_PATTERN.test(id)) {
20
+ return "id must be lowercase kebab-case";
21
+ }
22
+ return null;
23
+ }
24
+ function parseScheduleField(raw) {
25
+ if (raw === undefined)
26
+ return { ok: true, value: undefined };
27
+ if (!isPlainObject(raw)) {
28
+ return { ok: false, message: "schedule must be an object" };
29
+ }
30
+ if (typeof raw.cron !== "string" || raw.cron.trim().length === 0) {
31
+ return { ok: false, message: "schedule.cron is required" };
32
+ }
33
+ if (raw.timezone !== undefined && typeof raw.timezone !== "string") {
34
+ return { ok: false, message: "schedule.timezone must be a string" };
35
+ }
36
+ return {
37
+ ok: true,
38
+ value: {
39
+ cron: raw.cron,
40
+ ...(typeof raw.timezone === "string" ? { timezone: raw.timezone } : {}),
41
+ },
42
+ };
43
+ }
44
+ function parseEventField(raw) {
45
+ if (raw === undefined)
46
+ return { ok: true, value: undefined };
47
+ if (!isPlainObject(raw)) {
48
+ return { ok: false, message: "event must be an object" };
49
+ }
50
+ if (typeof raw.source !== "string" || raw.source.trim().length === 0) {
51
+ return { ok: false, message: "event.source is required" };
52
+ }
53
+ if (raw.match !== undefined && !isPlainObject(raw.match)) {
54
+ return { ok: false, message: "event.match must be an object" };
55
+ }
56
+ if (raw.config !== undefined && !isPlainObject(raw.config)) {
57
+ return { ok: false, message: "event.config must be an object" };
58
+ }
59
+ return {
60
+ ok: true,
61
+ value: {
62
+ source: raw.source,
63
+ ...(isPlainObject(raw.match) ? { match: raw.match } : {}),
64
+ ...(isPlainObject(raw.config) ? { config: raw.config } : {}),
65
+ },
66
+ };
67
+ }
68
+ export function parseCreateTriggerBody(body) {
69
+ if (!isPlainObject(body)) {
70
+ return { ok: false, status: 400, error: "Request body must be an object" };
71
+ }
72
+ if (typeof body.directory !== "string" || !body.directory.trim()) {
73
+ return { ok: false, status: 400, error: "directory is required" };
74
+ }
75
+ const directory = body.directory.trim().replace(/\\/g, "/");
76
+ if (typeof body.id !== "string") {
77
+ return { ok: false, status: 400, error: "id is required" };
78
+ }
79
+ const idError = validateTriggerId(body.id);
80
+ if (idError) {
81
+ return { ok: false, status: 400, error: idError };
82
+ }
83
+ if (typeof body.pipeline !== "string" || !body.pipeline.trim()) {
84
+ return { ok: false, status: 400, error: "pipeline is required" };
85
+ }
86
+ if (body.task !== undefined && (typeof body.task !== "string" || !body.task.trim())) {
87
+ return { ok: false, status: 400, error: "task must be a non-empty string" };
88
+ }
89
+ if (body.kind !== "manual" && body.kind !== "schedule" && body.kind !== "event") {
90
+ return {
91
+ ok: false,
92
+ status: 400,
93
+ error: "kind must be one of: manual, schedule, event",
94
+ };
95
+ }
96
+ const schedule = parseScheduleField(body.schedule);
97
+ if (!schedule.ok) {
98
+ return { ok: false, status: 400, error: schedule.message };
99
+ }
100
+ const event = parseEventField(body.event);
101
+ if (!event.ok) {
102
+ return { ok: false, status: 400, error: event.message };
103
+ }
104
+ if (body.kind === "schedule" && schedule.value === undefined) {
105
+ return { ok: false, status: 400, error: "schedule.cron is required for kind=schedule" };
106
+ }
107
+ if (body.kind === "event" && event.value === undefined) {
108
+ return { ok: false, status: 400, error: "event.source is required for kind=event" };
109
+ }
110
+ if (body.enabled !== undefined && typeof body.enabled !== "boolean") {
111
+ return { ok: false, status: 400, error: "enabled must be a boolean" };
112
+ }
113
+ return {
114
+ directory,
115
+ id: body.id,
116
+ pipeline: body.pipeline,
117
+ ...(typeof body.task === "string" ? { task: body.task } : {}),
118
+ kind: body.kind,
119
+ ...(schedule.value !== undefined ? { schedule: schedule.value } : {}),
120
+ ...(event.value !== undefined ? { event: event.value } : {}),
121
+ ...(body.enabled !== undefined ? { enabled: body.enabled } : {}),
122
+ };
123
+ }
124
+ function resolveDirectory(projectRoot, directory) {
125
+ const absDirectory = path.resolve(projectRoot, directory);
126
+ const rel = path.relative(projectRoot, absDirectory);
127
+ if (rel.startsWith("..") || path.isAbsolute(rel)) {
128
+ return null;
129
+ }
130
+ return absDirectory;
131
+ }
132
+ function triggerInputToYaml(input, enabled) {
133
+ const doc = {
134
+ id: input.id,
135
+ pipeline: input.pipeline,
136
+ ...(input.task !== undefined ? { task: input.task } : {}),
137
+ kind: input.kind,
138
+ };
139
+ if (input.kind === "schedule" && input.schedule) {
140
+ doc.schedule = input.schedule;
141
+ }
142
+ if (input.kind === "event" && input.event) {
143
+ doc.event = input.event;
144
+ }
145
+ doc.enabled = enabled;
146
+ return stringifyYaml(doc);
147
+ }
148
+ export async function createTrigger(rawProjectRoot, input) {
149
+ const idError = validateTriggerId(input.id);
150
+ if (idError) {
151
+ return { ok: false, status: 400, error: idError };
152
+ }
153
+ const projectRoot = await realpath(rawProjectRoot).catch(() => rawProjectRoot);
154
+ const directory = resolveDirectory(projectRoot, input.directory);
155
+ if (!directory) {
156
+ return { ok: false, status: 400, error: "directory must be inside the project root" };
157
+ }
158
+ const ctx = catalogContextFromStageflow(await resolveStageflowContext(projectRoot));
159
+ const scanPaths = await getCatalogScanPaths(ctx);
160
+ if (!scanPaths) {
161
+ return {
162
+ ok: false,
163
+ status: 422,
164
+ error: "Catalog manifest not found; cannot validate trigger references",
165
+ };
166
+ }
167
+ const collisionPath = await findCatalogPathById(scanPaths.triggerPaths, input.id);
168
+ if (collisionPath) {
169
+ return {
170
+ ok: false,
171
+ status: 409,
172
+ error: `Trigger id already exists (${path.relative(projectRoot, collisionPath).replace(/\\/g, "/")})`,
173
+ };
174
+ }
175
+ const pipelineIds = await collectPipelineIdsFromPaths(scanPaths.pipelinePaths);
176
+ if (!pipelineIds.has(input.pipeline)) {
177
+ return {
178
+ ok: false,
179
+ status: 422,
180
+ error: `Trigger references unknown pipeline "${input.pipeline}"`,
181
+ };
182
+ }
183
+ if (input.task !== undefined) {
184
+ const taskIds = await collectTaskIdsFromPaths(scanPaths.taskPaths);
185
+ if (!taskIds.has(input.task)) {
186
+ return {
187
+ ok: false,
188
+ status: 422,
189
+ error: `Trigger references unknown task "${input.task}"`,
190
+ };
191
+ }
192
+ }
193
+ if (input.kind === "schedule") {
194
+ if (!input.schedule) {
195
+ return { ok: false, status: 422, error: "schedule.cron is required for kind=schedule" };
196
+ }
197
+ try {
198
+ new Cron(input.schedule.cron, input.schedule.timezone !== undefined ? { timezone: input.schedule.timezone } : {});
199
+ }
200
+ catch (err) {
201
+ const message = err instanceof Error ? err.message : String(err);
202
+ return { ok: false, status: 422, error: `Invalid schedule.cron: ${message}` };
203
+ }
204
+ }
205
+ const filePath = path.join(directory, `${input.id}.trigger.yaml`);
206
+ const relPath = path.relative(projectRoot, filePath).replace(/\\/g, "/");
207
+ const enabled = input.enabled ?? true;
208
+ try {
209
+ await mkdir(directory, { recursive: true });
210
+ await writeFile(filePath, triggerInputToYaml(input, enabled), "utf8");
211
+ }
212
+ catch (err) {
213
+ const message = err instanceof Error ? err.message : String(err);
214
+ return { ok: false, status: 500, error: message };
215
+ }
216
+ const loadOutcome = await loadTriggerOutcome(filePath);
217
+ if (!loadOutcome.ok) {
218
+ await unlink(filePath).catch(() => { });
219
+ return {
220
+ ok: false,
221
+ status: 500,
222
+ error: loadOutcome.issues[0]?.message ?? "Failed to load created trigger",
223
+ };
224
+ }
225
+ return {
226
+ ok: true,
227
+ trigger: {
228
+ ...loadOutcome.value,
229
+ definition_ref: relPath,
230
+ },
231
+ };
232
+ }
@@ -0,0 +1,117 @@
1
+ import type { PipelineListing } from "./listConfig.js";
2
+ import { type ValidationFinding, type ValidationResult } from "./validateCatalog.js";
3
+ export type DraftStageArtifact = {
4
+ path: string;
5
+ body: Record<string, unknown>;
6
+ };
7
+ export type DraftPackage = {
8
+ pipeline: {
9
+ id: string;
10
+ stages: Array<Record<string, unknown>>;
11
+ agent?: unknown;
12
+ model?: unknown;
13
+ schemas?: unknown;
14
+ requires?: unknown;
15
+ };
16
+ stages?: DraftStageArtifact[];
17
+ task?: {
18
+ filename: string;
19
+ body: Record<string, unknown>;
20
+ };
21
+ };
22
+ export type ValidateDraftPackageOptions = {
23
+ cwd?: string;
24
+ projectRoot?: string;
25
+ strict?: boolean;
26
+ };
27
+ export type OverwriteDraftPackageInput = {
28
+ directory: string;
29
+ draft: DraftPackage;
30
+ pipelineFilename?: string;
31
+ /**
32
+ * Escape hatch for Workshop “Save invalid anyway” (ticket 07).
33
+ * When true, skips the validate gate and writes the package as-is.
34
+ */
35
+ allowInvalid?: boolean;
36
+ };
37
+ export type CreateDraftPackageInput = OverwriteDraftPackageInput;
38
+ export type DraftPackageWriteResult = {
39
+ ok: true;
40
+ pipeline: PipelineListing;
41
+ pipelinePath: string;
42
+ stagePaths: string[];
43
+ taskPath?: string;
44
+ } | {
45
+ ok: false;
46
+ status: 400 | 404 | 409 | 422 | 500;
47
+ error: string;
48
+ findings?: ValidationFinding[];
49
+ };
50
+ export type OverwriteDraftPackageResult = DraftPackageWriteResult;
51
+ export type CreateDraftPackageResult = DraftPackageWriteResult;
52
+ export type DraftPackageDestination = {
53
+ directory: string;
54
+ pipelineFilename: string;
55
+ };
56
+ export type LoadDraftPackageOptions = {
57
+ /** When set, attach this task file into the draft. Otherwise task stays absent. */
58
+ taskPath?: string;
59
+ };
60
+ export type LoadDraftPackageResult = {
61
+ ok: true;
62
+ draft: DraftPackage;
63
+ destination: DraftPackageDestination;
64
+ pipelinePath: string;
65
+ taskPath?: string;
66
+ } | {
67
+ ok: false;
68
+ status: 400 | 404;
69
+ error: string;
70
+ };
71
+ export type DraftTaskArtifact = {
72
+ filename: string;
73
+ body: Record<string, unknown>;
74
+ };
75
+ export type LoadTaskArtifactResult = {
76
+ ok: true;
77
+ task: DraftTaskArtifact;
78
+ taskPath: string;
79
+ } | {
80
+ ok: false;
81
+ status: 400 | 404;
82
+ error: string;
83
+ };
84
+ export declare function validateDraftPackage(draft: DraftPackage, options?: ValidateDraftPackageOptions): Promise<ValidationResult>;
85
+ export declare function overwriteDraftPackage(projectRoot: string, input: OverwriteDraftPackageInput): Promise<OverwriteDraftPackageResult>;
86
+ export declare function createDraftPackage(projectRoot: string, input: CreateDraftPackageInput): Promise<CreateDraftPackageResult>;
87
+ export declare function parseDraftPackageBody(body: unknown): DraftPackage | {
88
+ ok: false;
89
+ status: 400;
90
+ error: string;
91
+ };
92
+ export declare function parseCreateDraftPackageBody(body: unknown): CreateDraftPackageInput | {
93
+ ok: false;
94
+ status: 400;
95
+ error: string;
96
+ };
97
+ export declare const parseOverwriteDraftPackageBody: typeof parseCreateDraftPackageBody;
98
+ export type OpenDraftPackageInput = {
99
+ path: string;
100
+ taskPath?: string;
101
+ };
102
+ export declare function parseOpenDraftPackageBody(body: unknown): OpenDraftPackageInput | {
103
+ ok: false;
104
+ status: 400;
105
+ error: string;
106
+ };
107
+ /** Load a catalog task file into a draft task artifact (attach without reloading the pipeline). */
108
+ export declare function loadTaskArtifact(projectRoot: string, taskRelPath: string): Promise<LoadTaskArtifactResult>;
109
+ export type AttachTaskBody = {
110
+ taskPath: string;
111
+ };
112
+ export declare function parseAttachTaskBody(body: unknown): AttachTaskBody | {
113
+ ok: false;
114
+ status: 400;
115
+ error: string;
116
+ };
117
+ export declare function loadDraftPackage(projectRoot: string, pipelineRelPath: string, options?: LoadDraftPackageOptions): Promise<LoadDraftPackageResult>;