patchwork-os 0.2.0-alpha.33 → 0.2.0-alpha.35

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 (270) hide show
  1. package/README.md +248 -48
  2. package/deploy/bootstrap-new-vps.sh +12 -12
  3. package/deploy/bootstrap-vps.sh +6 -3
  4. package/deploy/deploy-landing.sh +59 -2
  5. package/dist/bridge.js +35 -1
  6. package/dist/bridge.js.map +1 -1
  7. package/dist/commands/recipe.d.ts +11 -0
  8. package/dist/commands/recipe.js +32 -3
  9. package/dist/commands/recipe.js.map +1 -1
  10. package/dist/commands/recipeInstall.d.ts +79 -1
  11. package/dist/commands/recipeInstall.js +241 -13
  12. package/dist/commands/recipeInstall.js.map +1 -1
  13. package/dist/connectors/asana.d.ts +198 -0
  14. package/dist/connectors/asana.js +680 -0
  15. package/dist/connectors/asana.js.map +1 -0
  16. package/dist/connectors/baseConnector.d.ts +16 -0
  17. package/dist/connectors/baseConnector.js +107 -25
  18. package/dist/connectors/baseConnector.js.map +1 -1
  19. package/dist/connectors/discord.d.ts +150 -0
  20. package/dist/connectors/discord.js +544 -0
  21. package/dist/connectors/discord.js.map +1 -0
  22. package/dist/connectors/github.js +15 -7
  23. package/dist/connectors/github.js.map +1 -1
  24. package/dist/connectors/gitlab.d.ts +180 -0
  25. package/dist/connectors/gitlab.js +582 -0
  26. package/dist/connectors/gitlab.js.map +1 -0
  27. package/dist/connectors/gmail.js +45 -0
  28. package/dist/connectors/gmail.js.map +1 -1
  29. package/dist/connectors/googleDrive.d.ts +34 -0
  30. package/dist/connectors/googleDrive.js +305 -0
  31. package/dist/connectors/googleDrive.js.map +1 -0
  32. package/dist/connectors/htmlEscape.d.ts +5 -0
  33. package/dist/connectors/htmlEscape.js +13 -0
  34. package/dist/connectors/htmlEscape.js.map +1 -0
  35. package/dist/connectors/linear.js +26 -6
  36. package/dist/connectors/linear.js.map +1 -1
  37. package/dist/connectors/mcpOAuth.d.ts +2 -0
  38. package/dist/connectors/mcpOAuth.js +8 -4
  39. package/dist/connectors/mcpOAuth.js.map +1 -1
  40. package/dist/connectors/pagerduty.d.ts +160 -0
  41. package/dist/connectors/pagerduty.js +464 -0
  42. package/dist/connectors/pagerduty.js.map +1 -0
  43. package/dist/connectors/sentry.js +3 -2
  44. package/dist/connectors/sentry.js.map +1 -1
  45. package/dist/connectors/slack.d.ts +1 -1
  46. package/dist/connectors/slack.js +7 -4
  47. package/dist/connectors/slack.js.map +1 -1
  48. package/dist/featureFlags.d.ts +17 -11
  49. package/dist/featureFlags.js +52 -47
  50. package/dist/featureFlags.js.map +1 -1
  51. package/dist/index.js +262 -129
  52. package/dist/index.js.map +1 -1
  53. package/dist/oauth.js +3 -2
  54. package/dist/oauth.js.map +1 -1
  55. package/dist/recipeOrchestration.d.ts +7 -0
  56. package/dist/recipeOrchestration.js +154 -28
  57. package/dist/recipeOrchestration.js.map +1 -1
  58. package/dist/recipes/agentExecutor.d.ts +1 -0
  59. package/dist/recipes/agentExecutor.js +7 -0
  60. package/dist/recipes/agentExecutor.js.map +1 -1
  61. package/dist/recipes/captureForRunlog.d.ts +27 -0
  62. package/dist/recipes/captureForRunlog.js +128 -0
  63. package/dist/recipes/captureForRunlog.js.map +1 -0
  64. package/dist/recipes/chainedRunner.d.ts +39 -3
  65. package/dist/recipes/chainedRunner.js +183 -28
  66. package/dist/recipes/chainedRunner.js.map +1 -1
  67. package/dist/recipes/detectSilentFail.d.ts +34 -0
  68. package/dist/recipes/detectSilentFail.js +105 -0
  69. package/dist/recipes/detectSilentFail.js.map +1 -0
  70. package/dist/recipes/legacyRecipeCompat.d.ts +8 -0
  71. package/dist/recipes/legacyRecipeCompat.js +20 -1
  72. package/dist/recipes/legacyRecipeCompat.js.map +1 -1
  73. package/dist/recipes/manifest.js +21 -6
  74. package/dist/recipes/manifest.js.map +1 -1
  75. package/dist/recipes/migrations/index.d.ts +24 -0
  76. package/dist/recipes/migrations/index.js +55 -0
  77. package/dist/recipes/migrations/index.js.map +1 -0
  78. package/dist/recipes/migrations/types.d.ts +28 -0
  79. package/dist/recipes/migrations/types.js +2 -0
  80. package/dist/recipes/migrations/types.js.map +1 -0
  81. package/dist/recipes/migrations/v1.d.ts +11 -0
  82. package/dist/recipes/migrations/v1.js +18 -0
  83. package/dist/recipes/migrations/v1.js.map +1 -0
  84. package/dist/recipes/replayRun.d.ts +62 -0
  85. package/dist/recipes/replayRun.js +97 -0
  86. package/dist/recipes/replayRun.js.map +1 -0
  87. package/dist/recipes/scheduler.js +102 -11
  88. package/dist/recipes/scheduler.js.map +1 -1
  89. package/dist/recipes/schemaGenerator.js +3 -3
  90. package/dist/recipes/schemaGenerator.js.map +1 -1
  91. package/dist/recipes/templateEngine.js +8 -1
  92. package/dist/recipes/templateEngine.js.map +1 -1
  93. package/dist/recipes/toolRegistry.d.ts +5 -0
  94. package/dist/recipes/toolRegistry.js +9 -0
  95. package/dist/recipes/toolRegistry.js.map +1 -1
  96. package/dist/recipes/tools/asana.d.ts +16 -0
  97. package/dist/recipes/tools/asana.js +524 -0
  98. package/dist/recipes/tools/asana.js.map +1 -0
  99. package/dist/recipes/tools/discord.d.ts +18 -0
  100. package/dist/recipes/tools/discord.js +254 -0
  101. package/dist/recipes/tools/discord.js.map +1 -0
  102. package/dist/recipes/tools/github.js +29 -4
  103. package/dist/recipes/tools/github.js.map +1 -1
  104. package/dist/recipes/tools/gitlab.d.ts +11 -0
  105. package/dist/recipes/tools/gitlab.js +285 -0
  106. package/dist/recipes/tools/gitlab.js.map +1 -0
  107. package/dist/recipes/tools/gmail.d.ts +1 -1
  108. package/dist/recipes/tools/gmail.js +230 -6
  109. package/dist/recipes/tools/gmail.js.map +1 -1
  110. package/dist/recipes/tools/googleDrive.d.ts +1 -0
  111. package/dist/recipes/tools/googleDrive.js +55 -0
  112. package/dist/recipes/tools/googleDrive.js.map +1 -0
  113. package/dist/recipes/tools/index.d.ts +6 -0
  114. package/dist/recipes/tools/index.js +6 -0
  115. package/dist/recipes/tools/index.js.map +1 -1
  116. package/dist/recipes/tools/linear.d.ts +2 -1
  117. package/dist/recipes/tools/linear.js +222 -1
  118. package/dist/recipes/tools/linear.js.map +1 -1
  119. package/dist/recipes/tools/meetingNotes.d.ts +21 -0
  120. package/dist/recipes/tools/meetingNotes.js +701 -0
  121. package/dist/recipes/tools/meetingNotes.js.map +1 -0
  122. package/dist/recipes/tools/pagerduty.d.ts +15 -0
  123. package/dist/recipes/tools/pagerduty.js +451 -0
  124. package/dist/recipes/tools/pagerduty.js.map +1 -0
  125. package/dist/recipes/tools/slack.js +8 -2
  126. package/dist/recipes/tools/slack.js.map +1 -1
  127. package/dist/recipes/validation.js +54 -15
  128. package/dist/recipes/validation.js.map +1 -1
  129. package/dist/recipes/yamlRunner.d.ts +23 -2
  130. package/dist/recipes/yamlRunner.js +265 -60
  131. package/dist/recipes/yamlRunner.js.map +1 -1
  132. package/dist/recipesHttp.d.ts +60 -0
  133. package/dist/recipesHttp.js +418 -3
  134. package/dist/recipesHttp.js.map +1 -1
  135. package/dist/runLog.d.ts +64 -2
  136. package/dist/runLog.js +116 -2
  137. package/dist/runLog.js.map +1 -1
  138. package/dist/server.d.ts +21 -0
  139. package/dist/server.js +387 -8
  140. package/dist/server.js.map +1 -1
  141. package/dist/streamableHttp.d.ts +31 -1
  142. package/dist/streamableHttp.js +20 -2
  143. package/dist/streamableHttp.js.map +1 -1
  144. package/dist/tools/activityLog.d.ts +2 -0
  145. package/dist/tools/addLinearComment.d.ts +1 -0
  146. package/dist/tools/batchLsp.d.ts +3 -0
  147. package/dist/tools/bridgeDoctor.d.ts +1 -0
  148. package/dist/tools/bridgeStatus.d.ts +1 -0
  149. package/dist/tools/cancelClaudeTask.d.ts +1 -0
  150. package/dist/tools/checkDocumentDirty.d.ts +1 -0
  151. package/dist/tools/clipboard.d.ts +2 -0
  152. package/dist/tools/closeTabs.d.ts +2 -0
  153. package/dist/tools/codeLens.d.ts +1 -0
  154. package/dist/tools/contextBundle.d.ts +1 -0
  155. package/dist/tools/createIssueFromAIComment.d.ts +1 -0
  156. package/dist/tools/createLinearIssue.d.ts +1 -0
  157. package/dist/tools/ctxGetTaskContext.d.ts +1 -0
  158. package/dist/tools/ctxQueryTraces.d.ts +1 -0
  159. package/dist/tools/ctxSaveTrace.d.ts +1 -0
  160. package/dist/tools/debug.d.ts +4 -0
  161. package/dist/tools/decorations.d.ts +2 -0
  162. package/dist/tools/documentLinks.d.ts +1 -0
  163. package/dist/tools/editText.d.ts +1 -0
  164. package/dist/tools/enrichCommit.d.ts +1 -0
  165. package/dist/tools/enrichStackTrace.d.ts +1 -0
  166. package/dist/tools/explainDiagnostic.d.ts +1 -0
  167. package/dist/tools/explainSymbol.d.ts +1 -0
  168. package/dist/tools/fetchCalendarEvents.d.ts +1 -0
  169. package/dist/tools/fetchGithubIssue.d.ts +1 -0
  170. package/dist/tools/fetchGithubPR.d.ts +1 -0
  171. package/dist/tools/fetchLinearIssue.d.ts +1 -0
  172. package/dist/tools/fetchSentryIssue.d.ts +1 -0
  173. package/dist/tools/fetchSlackProfile.d.ts +1 -0
  174. package/dist/tools/fileOperations.d.ts +3 -0
  175. package/dist/tools/fileWatcher.d.ts +2 -0
  176. package/dist/tools/findFiles.d.ts +1 -0
  177. package/dist/tools/findRelatedTests.d.ts +1 -0
  178. package/dist/tools/fixAllLintErrors.d.ts +1 -0
  179. package/dist/tools/foldingRanges.d.ts +1 -0
  180. package/dist/tools/formatDocument.d.ts +1 -0
  181. package/dist/tools/generateTests.d.ts +1 -0
  182. package/dist/tools/getAIComments.d.ts +1 -0
  183. package/dist/tools/getAnalyticsReport.d.ts +1 -0
  184. package/dist/tools/getArchitectureContext.d.ts +1 -0
  185. package/dist/tools/getBufferContent.d.ts +1 -0
  186. package/dist/tools/getChangeImpact.d.ts +1 -0
  187. package/dist/tools/getClaudeTaskStatus.d.ts +1 -0
  188. package/dist/tools/getCodeCoverage.d.ts +1 -0
  189. package/dist/tools/getCommitsForIssue.d.ts +1 -0
  190. package/dist/tools/getConnectorStatus.d.ts +1 -0
  191. package/dist/tools/getCurrentSelection.d.ts +2 -0
  192. package/dist/tools/getDebugState.d.ts +1 -0
  193. package/dist/tools/getDependencyTree.d.ts +1 -0
  194. package/dist/tools/getDiagnostics.d.ts +1 -0
  195. package/dist/tools/getDiffFromHandoff.d.ts +1 -0
  196. package/dist/tools/getDocumentSymbols.d.ts +1 -0
  197. package/dist/tools/getFileTree.d.ts +1 -0
  198. package/dist/tools/getGitDiff.d.ts +1 -0
  199. package/dist/tools/getGitHotspots.d.ts +1 -0
  200. package/dist/tools/getGitLog.d.ts +1 -0
  201. package/dist/tools/getGitStatus.d.ts +1 -0
  202. package/dist/tools/getImportTree.d.ts +1 -0
  203. package/dist/tools/getImportedSignatures.d.ts +1 -0
  204. package/dist/tools/getOpenEditors.d.ts +1 -0
  205. package/dist/tools/getPRTemplate.d.ts +1 -0
  206. package/dist/tools/getProjectContext.d.ts +1 -0
  207. package/dist/tools/getProjectInfo.d.ts +1 -0
  208. package/dist/tools/getSecurityAdvisories.d.ts +1 -0
  209. package/dist/tools/getSessionUsage.d.ts +1 -0
  210. package/dist/tools/getSymbolHistory.d.ts +1 -0
  211. package/dist/tools/getToolCapabilities.d.ts +1 -0
  212. package/dist/tools/getTypeSignature.d.ts +1 -0
  213. package/dist/tools/getWorkspaceFolders.d.ts +1 -0
  214. package/dist/tools/getWorkspaceSettings.d.ts +1 -0
  215. package/dist/tools/gitHistory.d.ts +2 -0
  216. package/dist/tools/gitWrite.d.ts +11 -0
  217. package/dist/tools/github/actions.d.ts +2 -0
  218. package/dist/tools/github/composite.d.ts +3 -0
  219. package/dist/tools/github/issues.d.ts +4 -0
  220. package/dist/tools/github/pr.d.ts +7 -0
  221. package/dist/tools/handoffNote.d.ts +2 -0
  222. package/dist/tools/hoverAtCursor.d.ts +1 -0
  223. package/dist/tools/httpClient.d.ts +2 -0
  224. package/dist/tools/inlayHints.d.ts +1 -0
  225. package/dist/tools/launchQuickTask.d.ts +1 -0
  226. package/dist/tools/listClaudeTasks.d.ts +1 -0
  227. package/dist/tools/listTerminals.d.ts +1 -0
  228. package/dist/tools/lsp.d.ts +14 -0
  229. package/dist/tools/navigateToSymbolByName.d.ts +1 -0
  230. package/dist/tools/openDiff.d.ts +1 -0
  231. package/dist/tools/openFile.d.ts +1 -0
  232. package/dist/tools/openInBrowser.d.ts +1 -0
  233. package/dist/tools/organizeImports.d.ts +1 -0
  234. package/dist/tools/performanceReport.d.ts +1 -0
  235. package/dist/tools/planPersistence.d.ts +5 -0
  236. package/dist/tools/previewEdit.d.ts +1 -0
  237. package/dist/tools/refactorAnalyze.d.ts +1 -0
  238. package/dist/tools/refactorPreview.d.ts +1 -0
  239. package/dist/tools/replaceBlock.d.ts +1 -0
  240. package/dist/tools/resumeClaudeTask.d.ts +1 -0
  241. package/dist/tools/runClaudeTask.d.ts +1 -0
  242. package/dist/tools/runCommand.d.ts +1 -0
  243. package/dist/tools/runTests.d.ts +1 -0
  244. package/dist/tools/saveDocument.d.ts +1 -0
  245. package/dist/tools/screenshotAndAnnotate.d.ts +1 -0
  246. package/dist/tools/searchAndReplace.d.ts +1 -0
  247. package/dist/tools/searchTools.d.ts +1 -0
  248. package/dist/tools/searchWorkspace.d.ts +1 -0
  249. package/dist/tools/selectionRanges.d.ts +1 -0
  250. package/dist/tools/semanticTokens.d.ts +1 -0
  251. package/dist/tools/setActiveWorkspaceFolder.d.ts +1 -0
  252. package/dist/tools/signatureHelp.d.ts +1 -0
  253. package/dist/tools/slackListChannels.d.ts +1 -0
  254. package/dist/tools/slackPostMessage.d.ts +1 -0
  255. package/dist/tools/slackPostMessage.js +1 -1
  256. package/dist/tools/slackPostMessage.js.map +1 -1
  257. package/dist/tools/terminal.d.ts +6 -0
  258. package/dist/tools/testTraceToSource.d.ts +1 -0
  259. package/dist/tools/transaction.d.ts +4 -0
  260. package/dist/tools/typeHierarchy.d.ts +1 -0
  261. package/dist/tools/updateLinearIssue.d.ts +1 -0
  262. package/dist/tools/utils.d.ts +2 -0
  263. package/dist/tools/utils.js.map +1 -1
  264. package/dist/tools/vscodeCommands.d.ts +2 -0
  265. package/dist/tools/vscodeTasks.d.ts +2 -0
  266. package/dist/tools/workspaceSettings.d.ts +1 -0
  267. package/package.json +20 -4
  268. package/templates/recipes/project-health-check.yaml +1 -1
  269. package/dist/schemas/dry-run-plan.v1.json +0 -139
  270. package/dist/schemas/recipe.v1.json +0 -684
package/dist/server.js CHANGED
@@ -97,6 +97,10 @@ export class Server extends EventEmitter {
97
97
  loadRecipeContentFn = null;
98
98
  /** Patchwork: set by bridge to save raw recipe source content by name. */
99
99
  saveRecipeContentFn = null;
100
+ /** Patchwork: set by bridge to delete a recipe by name. */
101
+ deleteRecipeContentFn = null;
102
+ /** Patchwork: set by bridge to lint raw recipe content without saving. */
103
+ lintRecipeContentFn = null;
100
104
  /** Patchwork: set by bridge to save a new recipe draft to disk. */
101
105
  saveRecipeFn = null;
102
106
  /** Patchwork: set by bridge to query the recipe run audit log. */
@@ -105,6 +109,9 @@ export class Server extends EventEmitter {
105
109
  runDetailFn = null;
106
110
  /** Patchwork: set by bridge to generate a dry-run plan for a recipe by name. */
107
111
  runPlanFn = null;
112
+ /** Patchwork (VD-4): mocked replay of an existing run. Returns the new
113
+ * run's seq plus any unmocked steps the caller may want to surface. */
114
+ runReplayFn = null;
108
115
  /** Patchwork: set by bridge to launch a named recipe via the orchestrator. */
109
116
  runRecipeFn = null;
110
117
  /** Patchwork: admin-controlled managed settings path (highest rule precedence). */
@@ -439,6 +446,21 @@ export class Server extends EventEmitter {
439
446
  })();
440
447
  return;
441
448
  }
449
+ if (parsedUrl.pathname === "/connections/google-drive/callback" &&
450
+ req.method === "GET") {
451
+ void (async () => {
452
+ const { handleDriveCallback } = await import("./connectors/googleDrive.js");
453
+ const code = parsedUrl.searchParams.get("code");
454
+ const state = parsedUrl.searchParams.get("state");
455
+ const error = parsedUrl.searchParams.get("error");
456
+ const result = await handleDriveCallback(code, state, error);
457
+ res.writeHead(result.status, {
458
+ "Content-Type": result.contentType ?? "application/json",
459
+ });
460
+ res.end(result.body);
461
+ })();
462
+ return;
463
+ }
442
464
  if (parsedUrl.pathname === "/connections/slack/callback" &&
443
465
  req.method === "GET") {
444
466
  void (async () => {
@@ -1086,6 +1108,177 @@ export class Server extends EventEmitter {
1086
1108
  res.end(result.body);
1087
1109
  return;
1088
1110
  }
1111
+ // ── Discord connector routes ───────────────────────────────────
1112
+ if ((parsedUrl.pathname === "/connections/discord/auth" ||
1113
+ parsedUrl.pathname === "/connections/discord/authorize") &&
1114
+ req.method === "GET") {
1115
+ const { handleDiscordAuthorize } = await import("./connectors/discord.js");
1116
+ const result = handleDiscordAuthorize();
1117
+ if (result.redirect) {
1118
+ res.writeHead(302, { Location: result.redirect });
1119
+ res.end();
1120
+ }
1121
+ else {
1122
+ res.writeHead(result.status, {
1123
+ "Content-Type": result.contentType ?? "application/json",
1124
+ });
1125
+ res.end(result.body);
1126
+ }
1127
+ return;
1128
+ }
1129
+ if (parsedUrl.pathname === "/connections/discord/callback" &&
1130
+ req.method === "GET") {
1131
+ void (async () => {
1132
+ const { handleDiscordCallback } = await import("./connectors/discord.js");
1133
+ const code = parsedUrl.searchParams.get("code");
1134
+ const state = parsedUrl.searchParams.get("state");
1135
+ const error = parsedUrl.searchParams.get("error");
1136
+ const result = await handleDiscordCallback(code, state, error);
1137
+ res.writeHead(result.status, {
1138
+ "Content-Type": result.contentType ?? "text/html",
1139
+ });
1140
+ res.end(result.body);
1141
+ })();
1142
+ return;
1143
+ }
1144
+ if (parsedUrl.pathname === "/connections/discord/test" &&
1145
+ req.method === "POST") {
1146
+ void (async () => {
1147
+ const { handleDiscordTest } = await import("./connectors/discord.js");
1148
+ const result = await handleDiscordTest();
1149
+ res.writeHead(result.status, {
1150
+ "Content-Type": result.contentType ?? "application/json",
1151
+ });
1152
+ res.end(result.body);
1153
+ })();
1154
+ return;
1155
+ }
1156
+ if (parsedUrl.pathname === "/connections/discord" &&
1157
+ req.method === "DELETE") {
1158
+ void (async () => {
1159
+ const { handleDiscordDisconnect } = await import("./connectors/discord.js");
1160
+ const result = await handleDiscordDisconnect();
1161
+ res.writeHead(result.status, {
1162
+ "Content-Type": result.contentType ?? "application/json",
1163
+ });
1164
+ res.end(result.body);
1165
+ })();
1166
+ return;
1167
+ }
1168
+ // ── Asana connector routes ─────────────────────────────────────
1169
+ if ((parsedUrl.pathname === "/connections/asana/auth" ||
1170
+ parsedUrl.pathname === "/connections/asana/authorize") &&
1171
+ req.method === "GET") {
1172
+ const { handleAsanaAuthorize } = await import("./connectors/asana.js");
1173
+ const result = handleAsanaAuthorize();
1174
+ if (result.redirect) {
1175
+ res.writeHead(302, { Location: result.redirect });
1176
+ res.end();
1177
+ }
1178
+ else {
1179
+ res.writeHead(result.status, {
1180
+ "Content-Type": result.contentType ?? "application/json",
1181
+ });
1182
+ res.end(result.body);
1183
+ }
1184
+ return;
1185
+ }
1186
+ if (parsedUrl.pathname === "/connections/asana/callback" &&
1187
+ req.method === "GET") {
1188
+ void (async () => {
1189
+ const { handleAsanaCallback } = await import("./connectors/asana.js");
1190
+ const code = parsedUrl.searchParams.get("code");
1191
+ const state = parsedUrl.searchParams.get("state");
1192
+ const error = parsedUrl.searchParams.get("error");
1193
+ const result = await handleAsanaCallback(code, state, error);
1194
+ res.writeHead(result.status, {
1195
+ "Content-Type": result.contentType ?? "text/html",
1196
+ });
1197
+ res.end(result.body);
1198
+ })();
1199
+ return;
1200
+ }
1201
+ if (parsedUrl.pathname === "/connections/asana/test" &&
1202
+ req.method === "POST") {
1203
+ void (async () => {
1204
+ const { handleAsanaTest } = await import("./connectors/asana.js");
1205
+ const result = await handleAsanaTest();
1206
+ res.writeHead(result.status, {
1207
+ "Content-Type": result.contentType ?? "application/json",
1208
+ });
1209
+ res.end(result.body);
1210
+ })();
1211
+ return;
1212
+ }
1213
+ if (parsedUrl.pathname === "/connections/asana" &&
1214
+ req.method === "DELETE") {
1215
+ void (async () => {
1216
+ const { handleAsanaDisconnect } = await import("./connectors/asana.js");
1217
+ const result = await handleAsanaDisconnect();
1218
+ res.writeHead(result.status, {
1219
+ "Content-Type": result.contentType ?? "application/json",
1220
+ });
1221
+ res.end(result.body);
1222
+ })();
1223
+ return;
1224
+ }
1225
+ // ── GitLab connector routes ────────────────────────────────────
1226
+ if ((parsedUrl.pathname === "/connections/gitlab/auth" ||
1227
+ parsedUrl.pathname === "/connections/gitlab/authorize") &&
1228
+ req.method === "GET") {
1229
+ const { handleGitLabAuthorize } = await import("./connectors/gitlab.js");
1230
+ const result = handleGitLabAuthorize();
1231
+ if (result.redirect) {
1232
+ res.writeHead(302, { Location: result.redirect });
1233
+ res.end();
1234
+ }
1235
+ else {
1236
+ res.writeHead(result.status, {
1237
+ "Content-Type": result.contentType ?? "application/json",
1238
+ });
1239
+ res.end(result.body);
1240
+ }
1241
+ return;
1242
+ }
1243
+ if (parsedUrl.pathname === "/connections/gitlab/callback" &&
1244
+ req.method === "GET") {
1245
+ void (async () => {
1246
+ const { handleGitLabCallback } = await import("./connectors/gitlab.js");
1247
+ const code = parsedUrl.searchParams.get("code");
1248
+ const state = parsedUrl.searchParams.get("state");
1249
+ const error = parsedUrl.searchParams.get("error");
1250
+ const result = await handleGitLabCallback(code, state, error);
1251
+ res.writeHead(result.status, {
1252
+ "Content-Type": result.contentType ?? "text/html",
1253
+ });
1254
+ res.end(result.body);
1255
+ })();
1256
+ return;
1257
+ }
1258
+ if (parsedUrl.pathname === "/connections/gitlab/test" &&
1259
+ req.method === "POST") {
1260
+ void (async () => {
1261
+ const { handleGitLabTest } = await import("./connectors/gitlab.js");
1262
+ const result = await handleGitLabTest();
1263
+ res.writeHead(result.status, {
1264
+ "Content-Type": result.contentType ?? "application/json",
1265
+ });
1266
+ res.end(result.body);
1267
+ })();
1268
+ return;
1269
+ }
1270
+ if (parsedUrl.pathname === "/connections/gitlab" &&
1271
+ req.method === "DELETE") {
1272
+ void (async () => {
1273
+ const { handleGitLabDisconnect } = await import("./connectors/gitlab.js");
1274
+ const result = await handleGitLabDisconnect();
1275
+ res.writeHead(result.status, {
1276
+ "Content-Type": result.contentType ?? "application/json",
1277
+ });
1278
+ res.end(result.body);
1279
+ })();
1280
+ return;
1281
+ }
1089
1282
  // ── Notion routes ──────────────────────────────────────────────
1090
1283
  if (parsedUrl.pathname === "/connections/notion/connect" &&
1091
1284
  req.method === "POST") {
@@ -1318,6 +1511,45 @@ export class Server extends EventEmitter {
1318
1511
  res.end(result.body);
1319
1512
  return;
1320
1513
  }
1514
+ // ── PagerDuty routes ───────────────────────────────────────────
1515
+ if (parsedUrl.pathname === "/connections/pagerduty/connect" &&
1516
+ req.method === "POST") {
1517
+ const chunks = [];
1518
+ req.on("data", (c) => chunks.push(c));
1519
+ req.on("end", () => {
1520
+ void (async () => {
1521
+ const { handlePagerDutyConnect } = await import("./connectors/pagerduty.js");
1522
+ const result = await handlePagerDutyConnect(Buffer.concat(chunks).toString("utf-8"));
1523
+ res.writeHead(result.status, {
1524
+ "Content-Type": result.contentType ?? "application/json",
1525
+ });
1526
+ res.end(result.body);
1527
+ })();
1528
+ });
1529
+ return;
1530
+ }
1531
+ if (parsedUrl.pathname === "/connections/pagerduty/test" &&
1532
+ req.method === "POST") {
1533
+ void (async () => {
1534
+ const { handlePagerDutyTest } = await import("./connectors/pagerduty.js");
1535
+ const result = await handlePagerDutyTest();
1536
+ res.writeHead(result.status, {
1537
+ "Content-Type": result.contentType ?? "application/json",
1538
+ });
1539
+ res.end(result.body);
1540
+ })();
1541
+ return;
1542
+ }
1543
+ if (parsedUrl.pathname === "/connections/pagerduty" &&
1544
+ req.method === "DELETE") {
1545
+ const { handlePagerDutyDisconnect } = await import("./connectors/pagerduty.js");
1546
+ const result = handlePagerDutyDisconnect();
1547
+ res.writeHead(result.status, {
1548
+ "Content-Type": result.contentType ?? "application/json",
1549
+ });
1550
+ res.end(result.body);
1551
+ return;
1552
+ }
1321
1553
  // ── Stripe routes ───────────────────────────────────────────────
1322
1554
  if (parsedUrl.pathname === "/connections/stripe/connect" &&
1323
1555
  req.method === "POST") {
@@ -1415,6 +1647,63 @@ export class Server extends EventEmitter {
1415
1647
  })();
1416
1648
  return;
1417
1649
  }
1650
+ if (parsedUrl.pathname === "/connections/google-drive/auth" &&
1651
+ req.method === "GET") {
1652
+ void (async () => {
1653
+ const { handleDriveAuthRedirect } = await import("./connectors/googleDrive.js");
1654
+ const result = handleDriveAuthRedirect();
1655
+ if (result.redirect) {
1656
+ res.writeHead(302, { Location: result.redirect });
1657
+ res.end();
1658
+ }
1659
+ else {
1660
+ res.writeHead(result.status, {
1661
+ "Content-Type": result.contentType ?? "application/json",
1662
+ });
1663
+ res.end(result.body);
1664
+ }
1665
+ })();
1666
+ return;
1667
+ }
1668
+ if (parsedUrl.pathname === "/connections/google-drive/callback" &&
1669
+ req.method === "GET") {
1670
+ void (async () => {
1671
+ const { handleDriveCallback } = await import("./connectors/googleDrive.js");
1672
+ const code = parsedUrl.searchParams.get("code");
1673
+ const state = parsedUrl.searchParams.get("state");
1674
+ const error = parsedUrl.searchParams.get("error");
1675
+ const result = await handleDriveCallback(code, state, error);
1676
+ res.writeHead(result.status, {
1677
+ "Content-Type": result.contentType ?? "application/json",
1678
+ });
1679
+ res.end(result.body);
1680
+ })();
1681
+ return;
1682
+ }
1683
+ if (parsedUrl.pathname === "/connections/google-drive/test" &&
1684
+ req.method === "POST") {
1685
+ void (async () => {
1686
+ const { handleDriveTest } = await import("./connectors/googleDrive.js");
1687
+ const result = await handleDriveTest();
1688
+ res.writeHead(result.status, {
1689
+ "Content-Type": result.contentType ?? "application/json",
1690
+ });
1691
+ res.end(result.body);
1692
+ })();
1693
+ return;
1694
+ }
1695
+ if (parsedUrl.pathname === "/connections/google-drive" &&
1696
+ req.method === "DELETE") {
1697
+ void (async () => {
1698
+ const { handleDriveDisconnect } = await import("./connectors/googleDrive.js");
1699
+ const result = await handleDriveDisconnect();
1700
+ res.writeHead(result.status, {
1701
+ "Content-Type": result.contentType ?? "application/json",
1702
+ });
1703
+ res.end(result.body);
1704
+ })();
1705
+ return;
1706
+ }
1418
1707
  // ── Inbox routes ────────────────────────────────────────────────────
1419
1708
  if (parsedUrl.pathname === "/inbox" && req.method === "GET") {
1420
1709
  void (async () => {
@@ -1515,10 +1804,11 @@ export class Server extends EventEmitter {
1515
1804
  const parsed = body
1516
1805
  ? JSON.parse(body)
1517
1806
  : {};
1518
- const vars = parsed.vars &&
1519
- typeof parsed.vars === "object" &&
1520
- !Array.isArray(parsed.vars)
1521
- ? parsed.vars
1807
+ const varsRaw = parsed.vars ?? parsed.inputs;
1808
+ const vars = varsRaw &&
1809
+ typeof varsRaw === "object" &&
1810
+ !Array.isArray(varsRaw)
1811
+ ? varsRaw
1522
1812
  : undefined;
1523
1813
  if (!this.runRecipeFn) {
1524
1814
  res.writeHead(503, { "Content-Type": "application/json" });
@@ -1652,6 +1942,41 @@ export class Server extends EventEmitter {
1652
1942
  }
1653
1943
  return;
1654
1944
  }
1945
+ // POST /runs/:seq/replay — VD-4 mocked replay. Re-runs the recipe
1946
+ // with all tool/agent execution intercepted to return captured
1947
+ // outputs from the original run. No external IO, no side effects.
1948
+ // Real-mode replay is not exposed here yet — must ship separately
1949
+ // with confirmation UX + kill-switch interaction.
1950
+ const runReplayMatch = req.method === "POST"
1951
+ ? /^\/runs\/(\d+)\/replay$/.exec(parsedUrl.pathname)
1952
+ : null;
1953
+ if (runReplayMatch?.[1]) {
1954
+ const seq = Number.parseInt(runReplayMatch[1], 10);
1955
+ try {
1956
+ if (!this.runReplayFn) {
1957
+ res.writeHead(503, { "Content-Type": "application/json" });
1958
+ res.end(JSON.stringify({ error: "replay_unavailable" }));
1959
+ return;
1960
+ }
1961
+ const result = await this.runReplayFn(seq);
1962
+ if (result.error === "run_not_found") {
1963
+ res.writeHead(404, { "Content-Type": "application/json" });
1964
+ }
1965
+ else if (!result.ok) {
1966
+ res.writeHead(500, { "Content-Type": "application/json" });
1967
+ }
1968
+ else {
1969
+ res.writeHead(200, { "Content-Type": "application/json" });
1970
+ }
1971
+ res.end(JSON.stringify(result));
1972
+ }
1973
+ catch (err) {
1974
+ const msg = err instanceof Error ? err.message : String(err);
1975
+ res.writeHead(500, { "Content-Type": "application/json" });
1976
+ res.end(JSON.stringify({ error: msg }));
1977
+ }
1978
+ return;
1979
+ }
1655
1980
  // GET /runs/:seq/plan — dry-run plan for the recipe that produced this run
1656
1981
  const runPlanMatch = req.method === "GET"
1657
1982
  ? /^\/runs\/(\d+)\/plan$/.exec(parsedUrl.pathname)
@@ -1670,7 +1995,8 @@ export class Server extends EventEmitter {
1670
1995
  res.end(JSON.stringify({ error: "plan_unavailable" }));
1671
1996
  return;
1672
1997
  }
1673
- const recipeName = run.recipeName;
1998
+ // triggerSource appends ":agent" suffix — strip before file lookup
1999
+ const recipeName = run.recipeName.replace(/:agent$/, "");
1674
2000
  const plan = await this.runPlanFn(recipeName);
1675
2001
  res.writeHead(200, { "Content-Type": "application/json" });
1676
2002
  res.end(JSON.stringify({ plan }));
@@ -1718,7 +2044,7 @@ export class Server extends EventEmitter {
1718
2044
  }
1719
2045
  const recipePatchMatch = /^\/recipes\/([^/]+)$/.exec(parsedUrl.pathname);
1720
2046
  if (recipePatchMatch && req.method === "PATCH") {
1721
- const name = decodeURIComponent(recipePatchMatch[1]);
2047
+ const name = decodeURIComponent(recipePatchMatch[1] ?? "");
1722
2048
  const chunks = [];
1723
2049
  req.on("data", (c) => chunks.push(c));
1724
2050
  req.on("end", () => {
@@ -1750,9 +2076,42 @@ export class Server extends EventEmitter {
1750
2076
  });
1751
2077
  return;
1752
2078
  }
2079
+ if (parsedUrl.pathname === "/recipes/lint" && req.method === "POST") {
2080
+ const chunks = [];
2081
+ req.on("data", (c) => chunks.push(c));
2082
+ req.on("end", () => {
2083
+ try {
2084
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
2085
+ if (typeof body?.content !== "string") {
2086
+ res.writeHead(400, { "Content-Type": "application/json" });
2087
+ res.end(JSON.stringify({
2088
+ ok: false,
2089
+ error: "content (string) required",
2090
+ }));
2091
+ return;
2092
+ }
2093
+ if (!this.lintRecipeContentFn) {
2094
+ res.writeHead(503, { "Content-Type": "application/json" });
2095
+ res.end(JSON.stringify({
2096
+ ok: false,
2097
+ error: "Recipe lint unavailable",
2098
+ }));
2099
+ return;
2100
+ }
2101
+ const result = this.lintRecipeContentFn(body.content);
2102
+ res.writeHead(200, { "Content-Type": "application/json" });
2103
+ res.end(JSON.stringify(result));
2104
+ }
2105
+ catch {
2106
+ res.writeHead(400, { "Content-Type": "application/json" });
2107
+ res.end(JSON.stringify({ ok: false, error: "Invalid JSON body" }));
2108
+ }
2109
+ });
2110
+ return;
2111
+ }
1753
2112
  const recipeContentMatch = /^\/recipes\/([^/]+)$/.exec(parsedUrl.pathname);
1754
2113
  if (recipeContentMatch && req.method === "GET") {
1755
- const name = decodeURIComponent(recipeContentMatch[1]);
2114
+ const name = decodeURIComponent(recipeContentMatch[1] ?? "");
1756
2115
  if (!this.loadRecipeContentFn) {
1757
2116
  res.writeHead(503, { "Content-Type": "application/json" });
1758
2117
  res.end(JSON.stringify({ ok: false, error: "Recipe content unavailable" }));
@@ -1769,7 +2128,7 @@ export class Server extends EventEmitter {
1769
2128
  return;
1770
2129
  }
1771
2130
  if (recipeContentMatch && req.method === "PUT") {
1772
- const name = decodeURIComponent(recipeContentMatch[1]);
2131
+ const name = decodeURIComponent(recipeContentMatch[1] ?? "");
1773
2132
  const chunks = [];
1774
2133
  req.on("data", (c) => chunks.push(c));
1775
2134
  req.on("end", () => {
@@ -1804,6 +2163,26 @@ export class Server extends EventEmitter {
1804
2163
  });
1805
2164
  return;
1806
2165
  }
2166
+ if (recipeContentMatch && req.method === "DELETE") {
2167
+ const name = decodeURIComponent(recipeContentMatch[1] ?? "");
2168
+ if (!this.deleteRecipeContentFn) {
2169
+ res.writeHead(503, { "Content-Type": "application/json" });
2170
+ res.end(JSON.stringify({
2171
+ ok: false,
2172
+ error: "Recipe deletion unavailable",
2173
+ }));
2174
+ return;
2175
+ }
2176
+ const result = this.deleteRecipeContentFn(name);
2177
+ const status = result.ok
2178
+ ? 200
2179
+ : result.error === "Recipe not found"
2180
+ ? 404
2181
+ : 400;
2182
+ res.writeHead(status, { "Content-Type": "application/json" });
2183
+ res.end(JSON.stringify(result));
2184
+ return;
2185
+ }
1807
2186
  if (req.url === "/recipes" && req.method === "GET") {
1808
2187
  try {
1809
2188
  const data = this.recipesFn?.() ?? { recipesDir: null, recipes: [] };