@hailer/mcp 1.1.11 → 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 (252) hide show
  1. package/dist/app.js +18 -5
  2. package/dist/bot/bot-config.d.ts +12 -1
  3. package/dist/bot/bot-config.js +98 -14
  4. package/dist/bot/bot-manager.d.ts +13 -3
  5. package/dist/bot/bot-manager.js +80 -25
  6. package/dist/bot/bot.d.ts +46 -0
  7. package/dist/bot/bot.js +542 -166
  8. package/dist/bot/services/message-classifier.js +17 -0
  9. package/dist/bot/services/permission-guard.d.ts +52 -0
  10. package/dist/bot/services/permission-guard.js +149 -0
  11. package/dist/bot/services/types.d.ts +5 -0
  12. package/dist/bot/services/typing-indicator.d.ts +6 -1
  13. package/dist/bot/services/typing-indicator.js +19 -3
  14. package/dist/config.d.ts +6 -1
  15. package/dist/config.js +43 -0
  16. package/dist/core.js +3 -6
  17. package/dist/mcp/UserContextCache.d.ts +5 -0
  18. package/dist/mcp/UserContextCache.js +51 -19
  19. package/dist/mcp/hailer-clients.d.ts +19 -1
  20. package/dist/mcp/hailer-clients.js +157 -20
  21. package/dist/mcp/session-store.d.ts +68 -0
  22. package/dist/mcp/session-store.js +169 -0
  23. package/dist/mcp/signal-handler.js +12 -12
  24. package/dist/mcp/tool-registry.d.ts +17 -4
  25. package/dist/mcp/tool-registry.js +37 -7
  26. package/dist/mcp/tools/activity.js +99 -7
  27. package/dist/mcp/tools/app-scaffold.js +304 -336
  28. package/dist/mcp/tools/company.d.ts +9 -0
  29. package/dist/mcp/tools/company.js +88 -0
  30. package/dist/mcp/tools/discussion.js +68 -0
  31. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  32. package/dist/mcp/tools/workflow-permissions.js +204 -0
  33. package/dist/mcp/tools/workflow.js +57 -18
  34. package/dist/mcp/utils/index.d.ts +2 -0
  35. package/dist/mcp/utils/index.js +12 -1
  36. package/dist/mcp/utils/role-utils.d.ts +74 -0
  37. package/dist/mcp/utils/role-utils.js +151 -0
  38. package/dist/mcp/utils/types.d.ts +43 -1
  39. package/dist/mcp/utils/types.js +14 -0
  40. package/dist/mcp/webhook-handler.d.ts +6 -0
  41. package/dist/mcp/webhook-handler.js +11 -0
  42. package/dist/mcp-server.d.ts +23 -2
  43. package/dist/mcp-server.js +639 -111
  44. package/dist/plugins/vipunen/client.d.ts +150 -0
  45. package/dist/plugins/vipunen/client.js +535 -0
  46. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  47. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  48. package/dist/plugins/vipunen/index.d.ts +41 -0
  49. package/dist/plugins/vipunen/index.js +88 -0
  50. package/dist/plugins/vipunen/tools.d.ts +26 -0
  51. package/dist/plugins/vipunen/tools.js +501 -0
  52. package/package.json +2 -1
  53. package/.claude/.context-watchdog.json +0 -1
  54. package/.claude/.session-checked +0 -1
  55. package/.claude/CLAUDE.md +0 -370
  56. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  57. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  58. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  59. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  60. package/.claude/agents/agent-code-simplifier.md +0 -53
  61. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  62. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  63. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  64. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  65. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  66. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  67. package/.claude/agents/agent-ivan-monolith.md +0 -154
  68. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  69. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  70. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  71. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  72. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  73. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  74. package/.claude/agents/agent-permissions-handler.md +0 -208
  75. package/.claude/agents/agent-simple-writer.md +0 -48
  76. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  77. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  78. package/.claude/agents/agent-ui-designer.md +0 -100
  79. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  80. package/.claude/agents/agent-web-search.md +0 -55
  81. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  82. package/.claude/agents/agent-zara-zapier.md +0 -159
  83. package/.claude/commands/app-squad.md +0 -135
  84. package/.claude/commands/audit-squad.md +0 -158
  85. package/.claude/commands/autoplan.md +0 -563
  86. package/.claude/commands/cleanup-squad.md +0 -98
  87. package/.claude/commands/config-squad.md +0 -106
  88. package/.claude/commands/crud-squad.md +0 -87
  89. package/.claude/commands/data-squad.md +0 -97
  90. package/.claude/commands/debug-squad.md +0 -303
  91. package/.claude/commands/doc-squad.md +0 -65
  92. package/.claude/commands/handoff.md +0 -137
  93. package/.claude/commands/health.md +0 -49
  94. package/.claude/commands/help.md +0 -29
  95. package/.claude/commands/help:agents.md +0 -151
  96. package/.claude/commands/help:commands.md +0 -78
  97. package/.claude/commands/help:faq.md +0 -79
  98. package/.claude/commands/help:plugins.md +0 -50
  99. package/.claude/commands/help:skills.md +0 -93
  100. package/.claude/commands/help:tools.md +0 -75
  101. package/.claude/commands/hotfix-squad.md +0 -112
  102. package/.claude/commands/integration-squad.md +0 -82
  103. package/.claude/commands/janitor-squad.md +0 -167
  104. package/.claude/commands/learn-auto.md +0 -120
  105. package/.claude/commands/learn.md +0 -120
  106. package/.claude/commands/mcp-list.md +0 -27
  107. package/.claude/commands/onboard-squad.md +0 -140
  108. package/.claude/commands/plan-workspace.md +0 -732
  109. package/.claude/commands/prd.md +0 -130
  110. package/.claude/commands/project-status.md +0 -82
  111. package/.claude/commands/publish.md +0 -138
  112. package/.claude/commands/recap.md +0 -69
  113. package/.claude/commands/restore.md +0 -64
  114. package/.claude/commands/review-squad.md +0 -152
  115. package/.claude/commands/save.md +0 -24
  116. package/.claude/commands/stats.md +0 -19
  117. package/.claude/commands/swarm.md +0 -210
  118. package/.claude/commands/tool-builder.md +0 -39
  119. package/.claude/commands/ws-pull.md +0 -44
  120. package/.claude/hooks/_shared-memory.cjs +0 -305
  121. package/.claude/hooks/_utils.cjs +0 -108
  122. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  123. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  124. package/.claude/hooks/app-edit-guard.cjs +0 -494
  125. package/.claude/hooks/auto-learn.cjs +0 -304
  126. package/.claude/hooks/bash-guard.cjs +0 -272
  127. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  128. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  129. package/.claude/hooks/context-watchdog.cjs +0 -230
  130. package/.claude/hooks/delegation-reminder.cjs +0 -465
  131. package/.claude/hooks/design-system-lint.cjs +0 -271
  132. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  133. package/.claude/hooks/prompt-guard.cjs +0 -354
  134. package/.claude/hooks/publish-template-guard.cjs +0 -147
  135. package/.claude/hooks/session-start.cjs +0 -35
  136. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  137. package/.claude/hooks/skill-injector.cjs +0 -140
  138. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  139. package/.claude/hooks/src-edit-guard.cjs +0 -240
  140. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  141. package/.claude/settings.json +0 -257
  142. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  143. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  144. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  145. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  146. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  147. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  148. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  149. package/.claude/skills/agent-structure/SKILL.md +0 -98
  150. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  151. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  152. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  153. package/.claude/skills/frontend-design/SKILL.md +0 -254
  154. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  155. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  156. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  157. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  158. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  159. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  160. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  161. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  162. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  163. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  164. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  165. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  166. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  167. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  168. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  169. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  170. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  171. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  172. package/.claude/skills/json-only-output/SKILL.md +0 -72
  173. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  174. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  175. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  176. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  177. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  178. package/.claude/skills/tool-builder/SKILL.md +0 -250
  179. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  180. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  181. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  182. package/.hailer-mcp-port +0 -1
  183. package/.mcp.json +0 -13
  184. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  185. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  186. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  187. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  188. package/.opencode/agent/agent-code-simplifier.md +0 -31
  189. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  190. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  191. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  192. package/.opencode/agent/agent-helga-workflow-config.md +0 -204
  193. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  194. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  195. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  196. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  197. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  198. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  199. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  200. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  201. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  202. package/.opencode/agent/agent-permissions-handler.md +0 -50
  203. package/.opencode/agent/agent-simple-writer.md +0 -45
  204. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  205. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  206. package/.opencode/agent/agent-ui-designer.md +0 -56
  207. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  208. package/.opencode/agent/agent-web-search.md +0 -42
  209. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  210. package/.opencode/agent/agent-zara-zapier.md +0 -53
  211. package/.opencode/commands/app-squad.md +0 -135
  212. package/.opencode/commands/audit-squad.md +0 -158
  213. package/.opencode/commands/autoplan.md +0 -563
  214. package/.opencode/commands/cleanup-squad.md +0 -98
  215. package/.opencode/commands/config-squad.md +0 -106
  216. package/.opencode/commands/crud-squad.md +0 -87
  217. package/.opencode/commands/data-squad.md +0 -97
  218. package/.opencode/commands/debug-squad.md +0 -303
  219. package/.opencode/commands/doc-squad.md +0 -65
  220. package/.opencode/commands/handoff.md +0 -137
  221. package/.opencode/commands/health.md +0 -49
  222. package/.opencode/commands/help-agents.md +0 -151
  223. package/.opencode/commands/help-commands.md +0 -32
  224. package/.opencode/commands/help-faq.md +0 -29
  225. package/.opencode/commands/help-plugins.md +0 -28
  226. package/.opencode/commands/help-skills.md +0 -7
  227. package/.opencode/commands/help-tools.md +0 -40
  228. package/.opencode/commands/help.md +0 -28
  229. package/.opencode/commands/hotfix-squad.md +0 -112
  230. package/.opencode/commands/integration-squad.md +0 -82
  231. package/.opencode/commands/janitor-squad.md +0 -167
  232. package/.opencode/commands/learn-auto.md +0 -120
  233. package/.opencode/commands/learn.md +0 -120
  234. package/.opencode/commands/mcp-list.md +0 -27
  235. package/.opencode/commands/onboard-squad.md +0 -140
  236. package/.opencode/commands/plan-workspace.md +0 -732
  237. package/.opencode/commands/prd.md +0 -131
  238. package/.opencode/commands/project-status.md +0 -82
  239. package/.opencode/commands/publish.md +0 -138
  240. package/.opencode/commands/recap.md +0 -69
  241. package/.opencode/commands/restore.md +0 -64
  242. package/.opencode/commands/review-squad.md +0 -152
  243. package/.opencode/commands/save.md +0 -24
  244. package/.opencode/commands/stats.md +0 -19
  245. package/.opencode/commands/swarm.md +0 -210
  246. package/.opencode/commands/tool-builder.md +0 -39
  247. package/.opencode/commands/ws-pull.md +0 -44
  248. package/.opencode/opencode.json +0 -21
  249. package/inbox/failures.log +0 -1
  250. package/inbox/usage.jsonl +0 -4
  251. package/scripts/postinstall.cjs +0 -64
  252. 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
  }