@hailer/mcp 1.1.12 → 1.1.13

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 (271) hide show
  1. package/CHANGELOG.md +0 -7
  2. package/{.claude → dist}/CLAUDE.md +2 -2
  3. package/dist/app.js +18 -5
  4. package/dist/bot/bot-config.d.ts +10 -1
  5. package/dist/bot/bot-config.js +64 -3
  6. package/dist/bot/bot-manager.d.ts +2 -0
  7. package/dist/bot/bot-manager.js +9 -2
  8. package/dist/bot/bot.d.ts +33 -0
  9. package/dist/bot/bot.js +461 -160
  10. package/dist/bot/services/message-classifier.js +17 -0
  11. package/dist/bot/services/permission-guard.d.ts +52 -0
  12. package/dist/bot/services/permission-guard.js +149 -0
  13. package/dist/bot/services/types.d.ts +5 -0
  14. package/dist/bot/services/typing-indicator.d.ts +6 -1
  15. package/dist/bot/services/typing-indicator.js +19 -3
  16. package/dist/cli.js +0 -0
  17. package/dist/config.d.ts +6 -1
  18. package/dist/config.js +43 -0
  19. package/dist/core.js +3 -6
  20. package/dist/lib/discussion-lock.d.ts +42 -0
  21. package/dist/lib/discussion-lock.js +110 -0
  22. package/dist/mcp/UserContextCache.d.ts +5 -0
  23. package/dist/mcp/UserContextCache.js +51 -19
  24. package/dist/mcp/hailer-clients.d.ts +19 -1
  25. package/dist/mcp/hailer-clients.js +158 -24
  26. package/dist/mcp/session-store.d.ts +68 -0
  27. package/dist/mcp/session-store.js +169 -0
  28. package/dist/mcp/signal-handler.js +2 -0
  29. package/dist/mcp/tool-registry.d.ts +17 -4
  30. package/dist/mcp/tool-registry.js +37 -7
  31. package/dist/mcp/tools/activity.js +99 -7
  32. package/dist/mcp/tools/app-scaffold.js +304 -336
  33. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  34. package/dist/mcp/tools/bot-config/constants.js +94 -0
  35. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  36. package/dist/mcp/tools/bot-config/core.js +2456 -0
  37. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  38. package/dist/mcp/tools/bot-config/index.js +59 -0
  39. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  40. package/dist/mcp/tools/bot-config/tools.js +15 -0
  41. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  42. package/dist/mcp/tools/bot-config/types.js +6 -0
  43. package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
  44. package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
  45. package/dist/mcp/tools/company.d.ts +9 -0
  46. package/dist/mcp/tools/company.js +88 -0
  47. package/dist/mcp/tools/discussion.js +68 -0
  48. package/dist/mcp/tools/document.d.ts +11 -0
  49. package/dist/mcp/tools/document.js +741 -0
  50. package/dist/mcp/tools/investigate.d.ts +9 -0
  51. package/dist/mcp/tools/investigate.js +254 -0
  52. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  53. package/dist/mcp/tools/workflow-permissions.js +204 -0
  54. package/dist/mcp/tools/workflow.js +57 -18
  55. package/dist/mcp/utils/index.d.ts +2 -0
  56. package/dist/mcp/utils/index.js +12 -1
  57. package/dist/mcp/utils/role-utils.d.ts +74 -0
  58. package/dist/mcp/utils/role-utils.js +151 -0
  59. package/dist/mcp/utils/types.d.ts +43 -1
  60. package/dist/mcp/utils/types.js +14 -0
  61. package/dist/mcp/webhook-handler.d.ts +4 -0
  62. package/dist/mcp/webhook-handler.js +8 -0
  63. package/dist/mcp-server.d.ts +23 -2
  64. package/dist/mcp-server.js +639 -127
  65. package/dist/plugins/vipunen/client.d.ts +150 -0
  66. package/dist/plugins/vipunen/client.js +535 -0
  67. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  68. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  69. package/dist/plugins/vipunen/index.d.ts +41 -0
  70. package/dist/plugins/vipunen/index.js +88 -0
  71. package/dist/plugins/vipunen/tools.d.ts +26 -0
  72. package/dist/plugins/vipunen/tools.js +501 -0
  73. package/dist/stdio-server.d.ts +14 -0
  74. package/dist/stdio-server.js +101 -0
  75. package/package.json +2 -1
  76. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  77. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  78. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  79. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  80. package/.claude/agents/agent-code-simplifier.md +0 -53
  81. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  82. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  83. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  84. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  85. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  86. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  87. package/.claude/agents/agent-ivan-monolith.md +0 -154
  88. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  89. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  90. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  91. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  92. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  93. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  94. package/.claude/agents/agent-permissions-handler.md +0 -208
  95. package/.claude/agents/agent-simple-writer.md +0 -48
  96. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  97. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  98. package/.claude/agents/agent-ui-designer.md +0 -100
  99. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  100. package/.claude/agents/agent-web-search.md +0 -55
  101. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  102. package/.claude/agents/agent-zara-zapier.md +0 -159
  103. package/.claude/commands/app-squad.md +0 -135
  104. package/.claude/commands/audit-squad.md +0 -158
  105. package/.claude/commands/autoplan.md +0 -563
  106. package/.claude/commands/cleanup-squad.md +0 -98
  107. package/.claude/commands/config-squad.md +0 -106
  108. package/.claude/commands/crud-squad.md +0 -87
  109. package/.claude/commands/data-squad.md +0 -97
  110. package/.claude/commands/debug-squad.md +0 -303
  111. package/.claude/commands/doc-squad.md +0 -65
  112. package/.claude/commands/handoff.md +0 -137
  113. package/.claude/commands/health.md +0 -49
  114. package/.claude/commands/help.md +0 -29
  115. package/.claude/commands/help:agents.md +0 -151
  116. package/.claude/commands/help:commands.md +0 -78
  117. package/.claude/commands/help:faq.md +0 -79
  118. package/.claude/commands/help:plugins.md +0 -50
  119. package/.claude/commands/help:skills.md +0 -93
  120. package/.claude/commands/help:tools.md +0 -75
  121. package/.claude/commands/hotfix-squad.md +0 -112
  122. package/.claude/commands/integration-squad.md +0 -82
  123. package/.claude/commands/janitor-squad.md +0 -167
  124. package/.claude/commands/learn-auto.md +0 -120
  125. package/.claude/commands/learn.md +0 -120
  126. package/.claude/commands/mcp-list.md +0 -27
  127. package/.claude/commands/onboard-squad.md +0 -140
  128. package/.claude/commands/plan-workspace.md +0 -732
  129. package/.claude/commands/prd.md +0 -130
  130. package/.claude/commands/project-status.md +0 -82
  131. package/.claude/commands/publish.md +0 -138
  132. package/.claude/commands/recap.md +0 -69
  133. package/.claude/commands/restore.md +0 -64
  134. package/.claude/commands/review-squad.md +0 -152
  135. package/.claude/commands/save.md +0 -24
  136. package/.claude/commands/stats.md +0 -19
  137. package/.claude/commands/swarm.md +0 -210
  138. package/.claude/commands/tool-builder.md +0 -39
  139. package/.claude/commands/ws-pull.md +0 -44
  140. package/.claude/hooks/_shared-memory.cjs +0 -305
  141. package/.claude/hooks/_utils.cjs +0 -108
  142. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  143. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  144. package/.claude/hooks/app-edit-guard.cjs +0 -494
  145. package/.claude/hooks/auto-learn.cjs +0 -304
  146. package/.claude/hooks/bash-guard.cjs +0 -272
  147. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  148. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  149. package/.claude/hooks/context-watchdog.cjs +0 -230
  150. package/.claude/hooks/delegation-reminder.cjs +0 -465
  151. package/.claude/hooks/design-system-lint.cjs +0 -271
  152. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  153. package/.claude/hooks/prompt-guard.cjs +0 -354
  154. package/.claude/hooks/publish-template-guard.cjs +0 -147
  155. package/.claude/hooks/session-start.cjs +0 -35
  156. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  157. package/.claude/hooks/skill-injector.cjs +0 -140
  158. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  159. package/.claude/hooks/src-edit-guard.cjs +0 -240
  160. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  161. package/.claude/settings.json +0 -257
  162. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  163. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  164. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  165. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  166. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  167. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  168. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  169. package/.claude/skills/agent-structure/SKILL.md +0 -98
  170. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  171. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  172. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  173. package/.claude/skills/frontend-design/SKILL.md +0 -254
  174. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  175. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  176. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  177. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  178. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  179. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  180. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  181. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  182. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  183. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  184. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  185. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  186. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  187. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  188. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  189. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  190. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  191. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  192. package/.claude/skills/json-only-output/SKILL.md +0 -72
  193. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  194. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  195. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  196. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  197. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  198. package/.claude/skills/tool-builder/SKILL.md +0 -250
  199. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  200. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  201. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  202. package/.mcp.json +0 -13
  203. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  204. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  205. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  206. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  207. package/.opencode/agent/agent-code-simplifier.md +0 -31
  208. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  209. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  210. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  211. package/.opencode/agent/agent-helga-workflow-config.md +0 -203
  212. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  213. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  214. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  215. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  216. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  217. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  218. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  219. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  220. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  221. package/.opencode/agent/agent-permissions-handler.md +0 -50
  222. package/.opencode/agent/agent-simple-writer.md +0 -45
  223. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  224. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  225. package/.opencode/agent/agent-ui-designer.md +0 -56
  226. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  227. package/.opencode/agent/agent-web-search.md +0 -42
  228. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  229. package/.opencode/agent/agent-zara-zapier.md +0 -53
  230. package/.opencode/commands/app-squad.md +0 -135
  231. package/.opencode/commands/audit-squad.md +0 -158
  232. package/.opencode/commands/autoplan.md +0 -563
  233. package/.opencode/commands/cleanup-squad.md +0 -98
  234. package/.opencode/commands/config-squad.md +0 -106
  235. package/.opencode/commands/crud-squad.md +0 -87
  236. package/.opencode/commands/data-squad.md +0 -97
  237. package/.opencode/commands/debug-squad.md +0 -303
  238. package/.opencode/commands/doc-squad.md +0 -65
  239. package/.opencode/commands/handoff.md +0 -137
  240. package/.opencode/commands/health.md +0 -49
  241. package/.opencode/commands/help-agents.md +0 -151
  242. package/.opencode/commands/help-commands.md +0 -32
  243. package/.opencode/commands/help-faq.md +0 -29
  244. package/.opencode/commands/help-plugins.md +0 -28
  245. package/.opencode/commands/help-skills.md +0 -7
  246. package/.opencode/commands/help-tools.md +0 -40
  247. package/.opencode/commands/help.md +0 -28
  248. package/.opencode/commands/hotfix-squad.md +0 -112
  249. package/.opencode/commands/integration-squad.md +0 -82
  250. package/.opencode/commands/janitor-squad.md +0 -167
  251. package/.opencode/commands/learn-auto.md +0 -120
  252. package/.opencode/commands/learn.md +0 -120
  253. package/.opencode/commands/mcp-list.md +0 -27
  254. package/.opencode/commands/onboard-squad.md +0 -140
  255. package/.opencode/commands/plan-workspace.md +0 -732
  256. package/.opencode/commands/prd.md +0 -131
  257. package/.opencode/commands/project-status.md +0 -82
  258. package/.opencode/commands/publish.md +0 -138
  259. package/.opencode/commands/recap.md +0 -69
  260. package/.opencode/commands/restore.md +0 -64
  261. package/.opencode/commands/review-squad.md +0 -152
  262. package/.opencode/commands/save.md +0 -24
  263. package/.opencode/commands/stats.md +0 -19
  264. package/.opencode/commands/swarm.md +0 -210
  265. package/.opencode/commands/tool-builder.md +0 -39
  266. package/.opencode/commands/ws-pull.md +0 -44
  267. package/.opencode/opencode.json +0 -28
  268. package/SESSION-HANDOFF.md +0 -68
  269. package/inbox/2026-03-04-bot-config-patterns.md +0 -24
  270. package/scripts/postinstall.cjs +0 -64
  271. package/scripts/test-hal-tools.ts +0 -154
@@ -142,7 +142,7 @@ exports.scaffoldHailerAppTool = {
142
142
  const path = await Promise.resolve().then(() => __importStar(require('path')));
143
143
  const fs = await Promise.resolve().then(() => __importStar(require('fs')));
144
144
  try {
145
- const targetDir = args.targetDirectory || config_1.environment.DEV_APPS_PATH || process.cwd();
145
+ const targetDir = args.targetDirectory || config_1.environment.DEV_APPS_PATH || path.join(process.cwd(), 'apps');
146
146
  const projectPath = path.join(targetDir, args.projectName);
147
147
  // Check if directory already exists
148
148
  if (fs.existsSync(projectPath)) {
@@ -160,7 +160,7 @@ exports.scaffoldHailerAppTool = {
160
160
  // Step 1: Scaffold project
161
161
  responseText += `⏳ Step 1/8: Creating project from template...\n`;
162
162
  try {
163
- const createCmd = `npm create @hailer/app@latest ${args.projectName} -- --template ${args.template}`;
163
+ const createCmd = `npm create @hailer/app@beta ${args.projectName} -- --template ${args.template}`;
164
164
  execSync(createCmd, {
165
165
  cwd: targetDir,
166
166
  stdio: 'pipe',
@@ -250,63 +250,79 @@ exports.scaffoldHailerAppTool = {
250
250
  }
251
251
  let appId;
252
252
  let workspaceId;
253
- // Step 4: Create dev app in Hailer (with auto-generated icon)
253
+ // Step 4: Find existing dev app or create new one
254
254
  if (args.autoCreateDevApp !== false) {
255
- responseText += `⏳ Step 4/8: Creating dev app entry in Hailer...\n`;
255
+ responseText += `⏳ Step 4/8: Setting up dev app entry in Hailer...\n`;
256
256
  try {
257
257
  workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)({}, context);
258
258
  if (!workspaceId) {
259
259
  responseText += `⚠️ Workspace cache not available, skipping app creation\n\n`;
260
260
  }
261
261
  else {
262
- // Generate and upload app icon
263
- let imageId;
262
+ // Check for existing dev app (localhost:3000) to reuse
264
263
  try {
265
- responseText += ` 📸 Generating app icon...\n`;
266
- const iconBuffer = await generateAppIcon(args.projectName);
267
- // Save to temp file and upload
268
- const os = await Promise.resolve().then(() => __importStar(require('os')));
269
- const tempPath = path.join(os.tmpdir(), `app-icon-${Date.now()}.jpg`);
270
- fs.writeFileSync(tempPath, iconBuffer);
271
- const uploadResult = await context.hailer.uploadFile({
272
- path: tempPath,
273
- filename: 'app-icon.jpg',
274
- isPublic: true
275
- });
276
- // Clean up temp file
277
- try {
278
- fs.unlinkSync(tempPath);
279
- }
280
- catch { /* ignore */ }
281
- if (uploadResult.success && uploadResult.fileId) {
282
- imageId = uploadResult.fileId;
283
- responseText += ` ✅ Icon uploaded: ${imageId}\n`;
264
+ const apps = await context.hailer.request('v3.app.list', [workspaceId]);
265
+ const existingDevApp = apps.find((app) => app.url === 'http://localhost:3000' || app.url === 'http://localhost:3000/');
266
+ if (existingDevApp) {
267
+ appId = existingDevApp._id;
268
+ responseText += `✅ Reusing existing dev app: ${existingDevApp.name} (${appId})\n`;
269
+ responseText += ` URL: http://localhost:3000\n\n`;
284
270
  }
285
271
  }
286
- catch (iconError) {
287
- logger.warn('Failed to generate/upload app icon', { error: iconError });
288
- responseText += ` ⚠️ Icon generation skipped\n`;
289
- }
290
- const appData = {
291
- cid: workspaceId,
292
- name: args.projectName,
293
- url: 'http://localhost:3000'
294
- };
295
- if (args.description) {
296
- appData.description = args.description;
272
+ catch {
273
+ // Non-fatal, will create new app below
297
274
  }
298
- if (imageId) {
299
- appData.image = imageId;
275
+ // Create new dev app only if no existing one found
276
+ if (!appId) {
277
+ // Generate and upload app icon
278
+ let imageId;
279
+ try {
280
+ responseText += ` 📸 Generating app icon...\n`;
281
+ const iconBuffer = await generateAppIcon(args.projectName);
282
+ // Save to temp file and upload
283
+ const os = await Promise.resolve().then(() => __importStar(require('os')));
284
+ const tempPath = path.join(os.tmpdir(), `app-icon-${Date.now()}.jpg`);
285
+ fs.writeFileSync(tempPath, iconBuffer);
286
+ const uploadResult = await context.hailer.uploadFile({
287
+ path: tempPath,
288
+ filename: 'app-icon.jpg',
289
+ isPublic: true
290
+ });
291
+ // Clean up temp file
292
+ try {
293
+ fs.unlinkSync(tempPath);
294
+ }
295
+ catch { /* ignore */ }
296
+ if (uploadResult.success && uploadResult.fileId) {
297
+ imageId = uploadResult.fileId;
298
+ responseText += ` ✅ Icon uploaded: ${imageId}\n`;
299
+ }
300
+ }
301
+ catch (iconError) {
302
+ logger.warn('Failed to generate/upload app icon', { error: iconError });
303
+ responseText += ` ⚠️ Icon generation skipped\n`;
304
+ }
305
+ const appData = {
306
+ cid: workspaceId,
307
+ name: args.projectName,
308
+ url: 'http://localhost:3000'
309
+ };
310
+ if (args.description) {
311
+ appData.description = args.description;
312
+ }
313
+ if (imageId) {
314
+ appData.image = imageId;
315
+ }
316
+ const result = await context.hailer.request('v3.app.create', [appData]);
317
+ appId = result.appId || result._id;
318
+ responseText += `✅ Dev app created: ${appId}\n`;
319
+ responseText += ` URL: http://localhost:3000\n\n`;
300
320
  }
301
- const result = await context.hailer.request('v3.app.create', [appData]);
302
- appId = result.appId || result._id;
303
- responseText += `✅ App created: ${appId}\n`;
304
- responseText += ` URL: http://localhost:3000\n\n`;
305
321
  }
306
322
  }
307
323
  catch (error) {
308
324
  const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
309
- responseText += `⚠️ Failed to create app in Hailer: ${errorMessage}\n\n`;
325
+ responseText += `⚠️ Failed to set up app in Hailer: ${errorMessage}\n\n`;
310
326
  }
311
327
  }
312
328
  else {
@@ -363,7 +379,7 @@ exports.scaffoldHailerAppTool = {
363
379
  cwd: projectPath,
364
380
  detached: true,
365
381
  stdio: 'ignore',
366
- shell: true
382
+ shell: '/bin/bash'
367
383
  });
368
384
  devServer.unref();
369
385
  responseText += `✅ Dev server started in background\n`;
@@ -501,377 +517,329 @@ exports.publishHailerAppTool = {
501
517
  schema: zod_1.z.object({
502
518
  projectDirectory: zod_1.z.string().optional().describe("Path to app project (defaults to DEV_APPS_PATH or current directory)"),
503
519
  appId: zod_1.z.string().optional().describe("App ID to publish to (reads from manifest.json if not provided)"),
504
- email: zod_1.z.string().email().optional().describe("Hailer account email (uses session credentials if not provided)"),
505
- password: zod_1.z.string().min(1).optional().describe("Hailer account password (uses session credentials if not provided)"),
506
- publishToMarket: zod_1.z.boolean().optional().default(false).describe("Set true to publish to marketplace and get targetId")
520
+ targetId: zod_1.z.string().optional().describe("Optional marketplace target ID for publishing to marketplace"),
507
521
  }),
508
522
  async execute(args, context) {
509
523
  const path = await Promise.resolve().then(() => __importStar(require('path')));
510
524
  const fs = await Promise.resolve().then(() => __importStar(require('fs')));
511
- // Use provided credentials or fall back to session credentials
512
- const email = args.email || context.email;
513
- const password = args.password || context.password;
514
- const { publishToMarket } = args;
515
- if (!email || !password) {
525
+ const projectDir = args.projectDirectory || config_1.environment.DEV_APPS_PATH || path.join(process.cwd(), 'apps');
526
+ // Check if directory exists
527
+ if (!fs.existsSync(projectDir)) {
528
+ if (!request_logger_1.RequestLogger.getCurrent())
529
+ logger.error('Project directory does not exist', { projectDir });
516
530
  return {
517
531
  content: [{
518
532
  type: "text",
519
- text: `❌ **Credentials Required**\n\nNo credentials found in session or parameters.\n\n**Options:**\n1. Provide email/password parameters\n2. Configure CLIENT_CONFIGS in .env.local with credentials`,
533
+ text: `❌ **Directory Not Found**\n\nThe directory \`${projectDir}\` does not exist.\n\n**Check:**\n- Project directory path is correct\n- You're in the right location`,
520
534
  }],
521
535
  };
522
536
  }
537
+ // Check if package.json exists and has build script
538
+ const packageJsonPath = path.join(projectDir, 'package.json');
539
+ if (!fs.existsSync(packageJsonPath)) {
540
+ if (!request_logger_1.RequestLogger.getCurrent())
541
+ logger.error('package.json not found', { packageJsonPath });
542
+ return {
543
+ content: [{
544
+ type: "text",
545
+ text: `❌ **Not a Hailer App Project**\n\nNo package.json found in \`${projectDir}\`\n\n**This doesn't appear to be a Hailer app project.**\n\nMake sure you're in the correct directory.`,
546
+ }],
547
+ };
548
+ }
549
+ let packageJson;
523
550
  try {
524
- const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
525
- // Check if expect is installed
526
- try {
527
- execSync('which expect', { encoding: 'utf-8', stdio: 'pipe' });
528
- }
529
- catch {
551
+ packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
552
+ }
553
+ catch (error) {
554
+ const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
555
+ if (!request_logger_1.RequestLogger.getCurrent())
556
+ logger.error('Failed to parse package.json', { error: errorMessage });
557
+ return {
558
+ content: [{ type: "text", text: `❌ **Invalid package.json**\n\n${errorMessage}` }],
559
+ };
560
+ }
561
+ if (!packageJson.scripts?.build) {
562
+ return {
563
+ content: [{ type: "text", text: '❌ **Missing Build Script**\n\npackage.json has no "build" script.' }],
564
+ };
565
+ }
566
+ // Read manifest.json to get/verify appId
567
+ const manifestPath = path.join(projectDir, 'public', 'manifest.json');
568
+ let appId = args.appId;
569
+ let appName = 'Unknown';
570
+ let manifest;
571
+ if (!fs.existsSync(manifestPath)) {
572
+ if (!appId) {
530
573
  if (!request_logger_1.RequestLogger.getCurrent())
531
- logger.error('expect command not found');
574
+ logger.error('Manifest not found and no appId provided');
532
575
  return {
533
576
  content: [{
534
577
  type: "text",
535
- text: `❌ **\`expect\` Not Installed**\n\nThe \`expect\` command is required for automated app publishing.\n\n**How to fix:**\n\n**macOS:**\n\`\`\`bash\nbrew install expect\n\`\`\`\n\n**Ubuntu/Debian:**\n\`\`\`bash\nsudo apt-get install expect\n\`\`\`\n\n**RHEL/CentOS:**\n\`\`\`bash\nsudo yum install expect\n\`\`\`\n\n**After installation, try again.**`,
578
+ text: `❌ **Manifest Not Found**\n\nNo \`public/manifest.json\` found and no appId provided.\n\n**Steps to fix:**\n1. Ensure \`public/manifest.json\` exists\n2. Set appId in manifest\n\nOr provide appId parameter when calling this tool.`,
536
579
  }],
537
580
  };
538
581
  }
539
- // Determine project directory
540
- const projectDir = args.projectDirectory || config_1.environment.DEV_APPS_PATH || process.cwd();
541
- // Check if directory exists
542
- if (!fs.existsSync(projectDir)) {
582
+ manifest = {};
583
+ }
584
+ else {
585
+ try {
586
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
587
+ }
588
+ catch (error) {
589
+ const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
543
590
  if (!request_logger_1.RequestLogger.getCurrent())
544
- logger.error('Project directory does not exist', { projectDir });
591
+ logger.error('Failed to parse manifest.json', { error: errorMessage });
545
592
  return {
546
593
  content: [{
547
594
  type: "text",
548
- text: `❌ **Directory Not Found**\n\nThe directory \`${projectDir}\` does not exist.\n\n**Check:**\n- Project directory path is correct\n- You're in the right location`,
595
+ text: `❌ **Invalid manifest.json**\n\nFailed to parse \`public/manifest.json\`: ${errorMessage}\n\n**Check:**\n- manifest.json is valid JSON\n- appId field is set correctly`,
549
596
  }],
550
597
  };
551
598
  }
552
- // Check if package.json exists
553
- const packageJsonPath = path.join(projectDir, 'package.json');
554
- if (!fs.existsSync(packageJsonPath)) {
599
+ if (!appId) {
600
+ appId = manifest.appId;
601
+ }
602
+ appName = manifest.name || appName;
603
+ if (!appId) {
555
604
  if (!request_logger_1.RequestLogger.getCurrent())
556
- logger.error('package.json not found', { packageJsonPath });
605
+ logger.error('No appId found');
557
606
  return {
558
607
  content: [{
559
608
  type: "text",
560
- text: `❌ **Not a Hailer App Project**\n\nNo package.json found in \`${projectDir}\`\n\n**This doesn't appear to be a Hailer app project.**\n\nMake sure you're in the correct directory.`,
609
+ text: `❌ **App ID Not Configured**\n\nNo appId found in \`manifest.json\` and none provided.\n\n**Steps to fix:**\n1. Get your published app ID from Hailer\n2. Edit \`public/manifest.json\`\n3. Set \`"appId": "your-app-id-here"\`\n\nOr provide appId parameter when calling this tool.`,
561
610
  }],
562
611
  };
563
612
  }
564
- // Read manifest.json to get/verify appId
565
- const manifestPath = path.join(projectDir, 'public', 'manifest.json');
566
- let appId = args.appId;
567
- let appName = 'Unknown';
568
- if (fs.existsSync(manifestPath)) {
569
- try {
570
- const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
571
- const manifest = JSON.parse(manifestContent);
572
- if (!appId) {
573
- appId = manifest.appId;
574
- }
575
- appName = manifest.name || appName;
576
- if (!appId) {
577
- if (!request_logger_1.RequestLogger.getCurrent())
578
- logger.error('No appId found');
579
- return {
580
- content: [{
581
- type: "text",
582
- text: `❌ **App ID Not Configured**\n\nNo appId found in \`manifest.json\` and none provided.\n\n**Steps to fix:**\n1. Get your published app ID from Hailer\n2. Edit \`public/manifest.json\`\n3. Set \`"appId": "your-app-id-here"\`\n\nOr provide appId parameter when calling this tool.`,
583
- }],
584
- };
585
- }
586
- // Validate version fields are present and not empty
587
- if (!manifest.version || manifest.version.trim() === '') {
588
- if (!request_logger_1.RequestLogger.getCurrent())
589
- logger.error('version field missing or empty');
590
- return {
591
- content: [{
592
- type: "text",
593
- text: `❌ **Version Not Set**\n\nThe \`version\` field in \`manifest.json\` is missing or empty.\n\n**Steps to fix:**\nEdit \`public/manifest.json\` and add:\n\`\`\`json\n"version": "1.0.0",\n"versionDescription": "Initial release"\n\`\`\`\n\nThe SDK requires version fields for publishing.`,
594
- }],
595
- };
596
- }
597
- if (!manifest.versionDescription || manifest.versionDescription.trim() === '') {
598
- if (!request_logger_1.RequestLogger.getCurrent())
599
- logger.error('versionDescription field missing or empty');
600
- return {
601
- content: [{
602
- type: "text",
603
- text: `❌ **Version Description Not Set**\n\nThe \`versionDescription\` field in \`manifest.json\` is missing or empty.\n\n**Steps to fix:**\nEdit \`public/manifest.json\` and add:\n\`\`\`json\n"version": "${manifest.version}",\n"versionDescription": "Description of this version"\n\`\`\`\n\nThe SDK requires version fields for publishing.`,
604
- }],
605
- };
606
- }
607
- }
608
- catch (error) {
609
- const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
610
- if (!request_logger_1.RequestLogger.getCurrent())
611
- logger.error('Failed to parse manifest.json', { error: errorMessage });
612
- return {
613
- content: [{
614
- type: "text",
615
- text: `❌ **Invalid manifest.json**\n\nFailed to parse \`public/manifest.json\`: ${errorMessage}\n\n**Check:**\n- manifest.json is valid JSON\n- appId field is set correctly`,
616
- }],
617
- };
618
- }
619
- }
620
- else if (!appId) {
613
+ // Validate version fields
614
+ if (!manifest.version || manifest.version.trim() === '') {
621
615
  if (!request_logger_1.RequestLogger.getCurrent())
622
- logger.error('Manifest not found and no appId provided');
616
+ logger.error('version field missing or empty');
623
617
  return {
624
618
  content: [{
625
619
  type: "text",
626
- text: `❌ **Manifest Not Found**\n\nNo \`public/manifest.json\` found and no appId provided.\n\n**Steps to fix:**\n1. Ensure \`public/manifest.json\` exists\n2. Set appId in manifest\n\nOr provide appId parameter when calling this tool.`,
620
+ text: `❌ **Version Not Set**\n\nThe \`version\` field in \`manifest.json\` is missing or empty.\n\n**Steps to fix:**\nEdit \`public/manifest.json\` and add:\n\`\`\`json\n"version": "1.0.0",\n"versionDescription": "Initial release"\n\`\`\`\n\nThe SDK requires version fields for publishing.`,
627
621
  }],
628
622
  };
629
623
  }
630
- // Check if publish-production script exists
631
- try {
632
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
633
- if (!packageJson.scripts?.['publish-production']) {
634
- if (!request_logger_1.RequestLogger.getCurrent())
635
- logger.error('publish-production script not found');
636
- return {
637
- content: [{
638
- type: "text",
639
- text: `❌ **Publish Script Not Found**\n\nNo \`publish-production\` script found in package.json.\n\n**This may not be a Hailer app created with @hailer/create-app.**\n\nHailer apps should have:\n\`\`\`json\n"scripts": {\n "publish-production": "...",\n ...\n}\n\`\`\``,
640
- }],
641
- };
642
- }
643
- }
644
- catch (error) {
645
- const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
624
+ if (!manifest.versionDescription || manifest.versionDescription.trim() === '') {
646
625
  if (!request_logger_1.RequestLogger.getCurrent())
647
- logger.error('Failed to parse package.json for scripts', { error: errorMessage });
626
+ logger.error('versionDescription field missing or empty');
648
627
  return {
649
628
  content: [{
650
629
  type: "text",
651
- text: `❌ **Invalid package.json**\n\n${errorMessage}`,
630
+ text: `❌ **Version Description Not Set**\n\nThe \`versionDescription\` field in \`manifest.json\` is missing or empty.\n\n**Steps to fix:**\nEdit \`public/manifest.json\` and add:\n\`\`\`json\n"version": "${manifest.version}",\n"versionDescription": "Description of this version"\n\`\`\`\n\nThe SDK requires version fields for publishing.`,
652
631
  }],
653
632
  };
654
633
  }
655
- // Log the start of publish
656
- logger.debug('Publishing app...', { appId, appName, workspace: context.workspaceCache.currentWorkspace.name });
657
- let responseText = `🚀 **Publishing Hailer App**\n\n`;
658
- responseText += `**App Name:** ${appName}\n`;
659
- responseText += `**App ID:** \`${appId}\`\n`;
660
- responseText += `**Project:** ${projectDir}\n`;
661
- responseText += `**Marketplace:** ${publishToMarket ? 'Yes (will get targetId)' : 'No'}\n\n`;
662
- // Run the SDK publish script using `expect` for interactive automation
663
- const os = await Promise.resolve().then(() => __importStar(require('os')));
664
- const result = await new Promise((resolve) => {
665
- // Create temp expect script file
666
- const tmpDir = os.tmpdir();
667
- const expectScriptPath = path.join(tmpDir, `hailer-publish-${Date.now()}.exp`);
668
- // For Tcl, use braces {} for literal strings (no escaping needed except for braces)
669
- const escapedPassword = password
670
- .replace(/\\/g, '\\\\')
671
- .replace(/\{/g, '\\{')
672
- .replace(/\}/g, '\\}');
673
- const expectScript = `#!/usr/bin/expect -f
674
- set timeout 45
675
- log_user 1
676
- exp_internal 0
677
- set password {${escapedPassword}}
678
- puts "\\n>>> SPAWNING npm run publish-production..."
679
- spawn npm run publish-production
680
- puts "\\n>>> WAITING FOR PASSWORD PROMPT..."
681
- expect {
682
- -nocase "password" {
683
- puts "\\n>>> MATCHED PASSWORD PROMPT"
684
- }
685
- timeout {
686
- puts "\\n>>> TIMEOUT waiting for password prompt (45s)"
687
- puts "\\n>>> This usually means the SDK is not prompting for credentials"
688
- exit 1
689
- }
690
- eof {
691
- puts "\\n>>> EOF before password prompt - process ended unexpectedly"
692
- exit 1
693
- }
694
- }
695
- puts "\\n>>> MATCHED PASSWORD, SENDING..."
696
- sleep 0.5
697
- # Try human-like typing
698
- set send_human {.05 .15 1 .02 1}
699
- send -h -- "$password"
700
- sleep 0.2
701
- send "\\r"
702
- puts "\\n>>> SENT, WAITING..."
703
- expect {
704
- -re "Invalid|invalid|wrong|Wrong" {
705
- puts "\\n>>> INVALID PASSWORD"
706
- exit 1
707
- }
708
- "Y/n" {
709
- puts "\\n>>> MATCHED Y/n"
710
- sleep 0.3
711
- send "Y\\r"
712
- exp_continue
713
- }
714
- "successfully" {
715
- puts "\\n>>> SUCCESS! Exiting immediately..."
716
- exit 0
717
- }
718
- timeout {
719
- puts "\\n>>> TIMEOUT 30s"
720
- exit 1
721
- }
722
- eof {
723
- puts "\\n>>> EOF - process ended"
724
- exit 0
725
- }
726
- }
727
- `;
728
- try {
729
- // Write expect script to temp file
730
- fs.writeFileSync(expectScriptPath, expectScript, { mode: 0o755 });
731
- // Build environment
732
- const envVars = {
733
- ...process.env,
734
- EMAIL: email
735
- };
736
- if (publishToMarket) {
737
- envVars.MARKET = 'true';
738
- }
739
- const output = execSync(`expect "${expectScriptPath}"`, {
740
- cwd: projectDir,
741
- encoding: 'utf-8',
742
- timeout: 60000,
743
- maxBuffer: 10 * 1024 * 1024,
744
- env: envVars
745
- });
746
- // Clean up temp file
634
+ }
635
+ // Check if appId points to a dev app (localhost) — if so, create a new production app
636
+ const workspaceId = context.workspaceCache.currentWorkspace._id;
637
+ let isNewProdApp = false;
638
+ try {
639
+ const apps = await context.hailer.request('v3.app.list', [workspaceId]);
640
+ const currentApp = apps.find((app) => app._id === appId);
641
+ if (currentApp && currentApp.url && (currentApp.url.includes('localhost') || currentApp.url.includes('127.0.0.1'))) {
642
+ // Dev app create a new production app entry
643
+ logger.debug('Dev app detected, creating production app entry', { devAppId: appId });
644
+ const prodAppData = {
645
+ cid: workspaceId,
646
+ name: appName,
647
+ url: `https://apps.hailer.com/${workspaceId}/placeholder/`
648
+ };
649
+ const prodResult = await context.hailer.request('v3.app.create', [prodAppData]);
650
+ const prodAppId = prodResult.appId || prodResult._id;
651
+ if (prodAppId) {
652
+ // Share with workspace
747
653
  try {
748
- fs.unlinkSync(expectScriptPath);
654
+ await context.hailer.request('v3.app.member.add', [prodAppId, `network_${workspaceId}`]);
749
655
  }
750
- catch { }
751
- // Check if manifest was updated with targetId
752
- let targetId;
753
- try {
656
+ catch { /* non-fatal */ }
657
+ logger.debug('Production app created', { devAppId: appId, prodAppId });
658
+ appId = prodAppId;
659
+ isNewProdApp = true;
660
+ // Update manifest with new production appId
661
+ if (fs.existsSync(manifestPath)) {
754
662
  const updatedManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
755
- targetId = updatedManifest.targetId;
756
- }
757
- catch {
758
- // Ignore
759
- }
760
- if (output.includes('Published successfully')) {
761
- resolve({ success: true, output, targetId });
762
- }
763
- else {
764
- resolve({ success: false, output });
663
+ updatedManifest.appId = appId;
664
+ fs.writeFileSync(manifestPath, JSON.stringify(updatedManifest, null, 2));
765
665
  }
766
666
  }
767
- catch (error) {
768
- if (!request_logger_1.RequestLogger.getCurrent())
769
- logger.error('Expect script execution failed', {
770
- errorCode: error.code,
771
- signal: error.signal
772
- });
773
- // Clean up temp file
774
- try {
775
- fs.unlinkSync(expectScriptPath);
776
- }
777
- catch { }
778
- const output = error.stdout || error.stderr || error.message || String(error);
779
- // Check for specific error messages
780
- if (output.includes('not found') || output.includes('command not found')) {
781
- resolve({
782
- success: false,
783
- output: `ERROR: expect command not found. Try: apt-get install expect (Ubuntu) or brew install expect (macOS)\n\n${output}`
784
- });
785
- }
786
- else if (output.includes('TIMEOUT')) {
787
- resolve({
788
- success: false,
789
- output: `ERROR: expect script timed out waiting for prompts. Check your internet connection and Hailer API status.\n\n${output}`
790
- });
791
- }
792
- else if (output.includes('INVALID PASSWORD')) {
667
+ }
668
+ }
669
+ catch (error) {
670
+ logger.warn('Failed to check dev app status, proceeding with provided appId', { error: (0, tool_helpers_1.extractErrorMessage)(error) });
671
+ }
672
+ // Log the start of publish
673
+ logger.debug('Publishing app via API key...', { appId, appName, isNewProdApp, workspace: context.workspaceCache.currentWorkspace.name });
674
+ // Step 1: Run npm run build
675
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
676
+ try {
677
+ execSync('npm run build', { cwd: projectDir, stdio: 'pipe', timeout: 120000 });
678
+ }
679
+ catch (error) {
680
+ const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
681
+ if (!request_logger_1.RequestLogger.getCurrent())
682
+ logger.error('Build failed', { error: errorMessage });
683
+ return {
684
+ content: [{ type: "text", text: `❌ **Build Failed**\n\n\`\`\`\n${errorMessage}\n\`\`\`` }],
685
+ };
686
+ }
687
+ // Step 2: Verify dist/ exists
688
+ const distPath = path.join(projectDir, 'dist');
689
+ if (!fs.existsSync(distPath)) {
690
+ return {
691
+ content: [{ type: "text", text: `❌ **dist/ directory not found after build at ${distPath}**` }],
692
+ };
693
+ }
694
+ // Step 3: Copy manifest.json into dist/ (backend expects package/dist/manifest.json)
695
+ const distManifestPath = path.join(projectDir, 'dist', 'manifest.json');
696
+ try {
697
+ fs.copyFileSync(manifestPath, distManifestPath);
698
+ }
699
+ catch (error) {
700
+ const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
701
+ return {
702
+ content: [{ type: "text", text: `❌ **Failed to copy manifest.json to dist/:** ${errorMessage}` }],
703
+ };
704
+ }
705
+ // Step 4: Create .tgz archive with package/ prefix (matches npm pack format)
706
+ const tar = await Promise.resolve().then(() => __importStar(require('tar')));
707
+ const tgzFilename = `${appId}-${manifest.version || 'latest'}.tgz`;
708
+ const tgzPath = path.join(projectDir, tgzFilename);
709
+ try {
710
+ await tar.create({ gzip: true, file: tgzPath, cwd: projectDir, prefix: 'package' }, ['dist', 'package.json']);
711
+ }
712
+ catch (error) {
713
+ const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
714
+ return {
715
+ content: [{ type: "text", text: `❌ **Failed to create .tgz archive:** ${errorMessage}` }],
716
+ };
717
+ }
718
+ // Step 5: Upload to /app/publish using API key (following file-upload.ts pattern)
719
+ const FormData = (await Promise.resolve().then(() => __importStar(require('form-data')))).default;
720
+ const formData = new FormData();
721
+ formData.append('file', fs.createReadStream(tgzPath), {
722
+ filename: tgzFilename,
723
+ contentType: 'application/gzip',
724
+ });
725
+ formData.append('appId', appId);
726
+ if (args.targetId) {
727
+ formData.append('targetId', args.targetId);
728
+ }
729
+ const apiBaseUrl = context.client.socket.host;
730
+ const sessionKey = context.client.sessionKey;
731
+ const publishUrl = new URL(`${apiBaseUrl}/app/publish`);
732
+ const https = await Promise.resolve().then(() => __importStar(require('https')));
733
+ const http = await Promise.resolve().then(() => __importStar(require('http')));
734
+ try {
735
+ const uploadResponse = await new Promise((resolve, reject) => {
736
+ const options = {
737
+ hostname: publishUrl.hostname,
738
+ port: publishUrl.port || (publishUrl.protocol === 'https:' ? 443 : 80),
739
+ path: publishUrl.pathname,
740
+ method: 'POST',
741
+ headers: {
742
+ 'hlrkey': sessionKey,
743
+ ...formData.getHeaders(),
744
+ },
745
+ };
746
+ const req = (publishUrl.protocol === 'https:' ? https : http).request(options, (res) => {
747
+ const chunks = [];
748
+ res.on('data', (chunk) => chunks.push(chunk));
749
+ res.on('end', () => {
793
750
  resolve({
794
- success: false,
795
- output: `ERROR: Invalid credentials provided. Check your email and password.\n\n${output}`
751
+ statusCode: res.statusCode || 500,
752
+ body: Buffer.concat(chunks).toString('utf-8'),
796
753
  });
797
- }
798
- else if (output.includes('Published successfully')) {
799
- // Even if exit code is non-zero, check if it actually succeeded
800
- let targetId;
801
- try {
802
- const updatedManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
803
- targetId = updatedManifest.targetId;
804
- }
805
- catch {
806
- // Ignore
807
- }
808
- resolve({ success: true, output, targetId });
809
- }
810
- else {
811
- resolve({ success: false, output });
812
- }
813
- }
754
+ });
755
+ });
756
+ req.on('error', reject);
757
+ formData.pipe(req);
814
758
  });
815
- if (!result.success) {
759
+ // Clean up .tgz file
760
+ try {
761
+ fs.unlinkSync(tgzPath);
762
+ }
763
+ catch { /* non-fatal */ }
764
+ if (uploadResponse.statusCode < 200 || uploadResponse.statusCode >= 300) {
816
765
  if (!request_logger_1.RequestLogger.getCurrent())
817
- logger.error('Publish failed');
766
+ logger.error('Publish failed', { status: uploadResponse.statusCode });
818
767
  return {
819
768
  content: [{
820
769
  type: "text",
821
- text: `❌ **Publish Failed**\n\n\`\`\`\n${result.output}\n\`\`\`\n\n**Common Issues:**\n- Invalid credentials\n- App ID not found\n- Network issues`,
770
+ text: `❌ **Publish Failed (${uploadResponse.statusCode})**\n\n\`\`\`\n${uploadResponse.body}\n\`\`\`\n\n**Common Issues:**\n- Invalid API key\n- App ID not found\n- Network issues`,
822
771
  }],
823
772
  };
824
773
  }
825
- // Log success
774
+ let result;
775
+ try {
776
+ result = JSON.parse(uploadResponse.body);
777
+ }
778
+ catch {
779
+ result = { raw: uploadResponse.body };
780
+ }
826
781
  logger.debug('App published successfully', { appId, appName });
827
- responseText += `✅ **App Published Successfully!**\n\n`;
828
- if (result.targetId) {
829
- responseText += `**Target ID:** \`${result.targetId}\`\n\n`;
830
- responseText += `💡 **Marketplace Publishing:**\n`;
831
- responseText += `Use this targetId to create a marketplace listing:\n`;
832
- responseText += `\`\`\`javascript\n`;
833
- responseText += `publish_app({\n`;
834
- responseText += ` appId: "${appId}",\n`;
835
- responseText += ` versionId: "${result.targetId}",\n`;
836
- responseText += ` title: "App Name",\n`;
837
- responseText += ` description: "Description",\n`;
838
- responseText += ` version: "1.0.0",\n`;
839
- responseText += ` versionDescription: "Release notes",\n`;
840
- responseText += ` publisher: "Publisher Name",\n`;
841
- responseText += ` iconFileId: "<upload-icon-first>"\n`;
842
- responseText += `})\n`;
843
- responseText += `\`\`\`\n\n`;
782
+ // Auto-update app URL to production after successful publish
783
+ const productionUrl = `https://apps.hailer.com/${workspaceId}/${appId}/`;
784
+ let urlUpdated = false;
785
+ try {
786
+ await context.hailer.request('v3.app.update', [appId, { name: appName, url: productionUrl }]);
787
+ urlUpdated = true;
788
+ logger.debug('App URL updated to production', { appId, productionUrl });
789
+ }
790
+ catch (urlError) {
791
+ const urlErrorMsg = (0, tool_helpers_1.extractErrorMessage)(urlError);
792
+ logger.warn('Failed to update app URL after publish', { appId, error: urlErrorMsg });
793
+ }
794
+ let responseText = `✅ **App Published Successfully!**\n\n`;
795
+ if (isNewProdApp) {
796
+ responseText += `🆕 **New production app created** (dev app kept at localhost)\n`;
797
+ }
798
+ responseText += `**App Name:** ${appName}\n`;
799
+ responseText += `**App ID:** \`${appId}\`\n`;
800
+ responseText += `**Version:** ${manifest.version || 'unknown'}\n`;
801
+ responseText += `**Description:** ${manifest.versionDescription || 'N/A'}\n`;
802
+ if (urlUpdated) {
803
+ responseText += `**URL:** ${productionUrl}\n`;
804
+ }
805
+ else {
806
+ responseText += `⚠️ **URL not updated** — manually call \`update_app\` to set production URL\n`;
807
+ }
808
+ if (typeof result.size === 'number') {
809
+ responseText += `**Size:** ${(result.size / 1024).toFixed(2)} KB\n`;
810
+ }
811
+ if (args.targetId) {
812
+ responseText += `**Marketplace Target:** ${args.targetId}\n`;
844
813
  }
845
- else if (publishToMarket) {
846
- responseText += `⚠️ **No targetId returned**\n`;
847
- responseText += `The MARKET flag was set but no targetId was found in the response.\n`;
848
- responseText += `Check if the manifest.json was updated.\n\n`;
814
+ if (result.manifest) {
815
+ responseText += `\n**Manifest:**\n\`\`\`json\n${JSON.stringify(result.manifest, null, 2)}\n\`\`\`\n`;
849
816
  }
850
- responseText += `## 📋 Next Steps\n\n`;
851
- responseText += `1. **Open published app in Hailer** (not the dev version)\n`;
852
- responseText += `2. **Verify functionality** - test all features work\n`;
853
- responseText += `3. **Share with users** - use \`add_app_member\` tool:\n`;
817
+ responseText += `\n## Next Steps\n\n`;
818
+ responseText += `1. **Open published app in Hailer** to verify\n`;
819
+ responseText += `2. **Share with users** - use \`add_app_member\` tool:\n`;
854
820
  responseText += ` \`\`\`javascript\n`;
855
821
  responseText += ` add_app_member({\n`;
856
822
  responseText += ` appId: "${appId}",\n`;
857
- responseText += ` member: "network_<workspace-id>" // or team_*, user_*\n`;
823
+ responseText += ` member: "network_${workspaceId}" // or team_*, user_*\n`;
858
824
  responseText += ` })\n`;
859
825
  responseText += ` \`\`\`\n`;
860
826
  return {
861
- content: [{
862
- type: "text",
863
- text: responseText,
864
- }],
827
+ content: [{ type: "text", text: responseText }],
865
828
  };
866
829
  }
867
830
  catch (error) {
831
+ // Clean up .tgz file on failure
832
+ try {
833
+ fs.unlinkSync(tgzPath);
834
+ }
835
+ catch { /* non-fatal */ }
868
836
  const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
869
837
  if (!request_logger_1.RequestLogger.getCurrent())
870
838
  logger.error('Error publishing Hailer app', { error: errorMessage });
871
839
  return {
872
840
  content: [{
873
841
  type: "text",
874
- text: `❌ **Error publishing Hailer app**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Not in a Hailer app directory\n- Missing package.json or manifest.json\n- Network connectivity issues\n- Build failures`,
842
+ text: `❌ **Upload Failed**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Network connectivity issues\n- Invalid API key\n- Hailer API unavailable`,
875
843
  }],
876
844
  };
877
845
  }