@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.
- package/CHANGELOG.md +0 -7
- package/{.claude → dist}/CLAUDE.md +2 -2
- package/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +10 -1
- package/dist/bot/bot-config.js +64 -3
- package/dist/bot/bot-manager.d.ts +2 -0
- package/dist/bot/bot-manager.js +9 -2
- package/dist/bot/bot.d.ts +33 -0
- package/dist/bot/bot.js +461 -160
- package/dist/bot/services/message-classifier.js +17 -0
- package/dist/bot/services/permission-guard.d.ts +52 -0
- package/dist/bot/services/permission-guard.js +149 -0
- package/dist/bot/services/types.d.ts +5 -0
- package/dist/bot/services/typing-indicator.d.ts +6 -1
- package/dist/bot/services/typing-indicator.js +19 -3
- package/dist/cli.js +0 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/mcp/UserContextCache.d.ts +5 -0
- package/dist/mcp/UserContextCache.js +51 -19
- package/dist/mcp/hailer-clients.d.ts +19 -1
- package/dist/mcp/hailer-clients.js +158 -24
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +2 -0
- package/dist/mcp/tool-registry.d.ts +17 -4
- package/dist/mcp/tool-registry.js +37 -7
- package/dist/mcp/tools/activity.js +99 -7
- package/dist/mcp/tools/app-scaffold.js +304 -336
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
- package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
- package/dist/mcp/tools/company.d.ts +9 -0
- package/dist/mcp/tools/company.js +88 -0
- package/dist/mcp/tools/discussion.js +68 -0
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
- package/dist/mcp/tools/workflow-permissions.js +204 -0
- package/dist/mcp/tools/workflow.js +57 -18
- package/dist/mcp/utils/index.d.ts +2 -0
- package/dist/mcp/utils/index.js +12 -1
- package/dist/mcp/utils/role-utils.d.ts +74 -0
- package/dist/mcp/utils/role-utils.js +151 -0
- package/dist/mcp/utils/types.d.ts +43 -1
- package/dist/mcp/utils/types.js +14 -0
- package/dist/mcp/webhook-handler.d.ts +4 -0
- package/dist/mcp/webhook-handler.js +8 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -127
- package/dist/plugins/vipunen/client.d.ts +150 -0
- package/dist/plugins/vipunen/client.js +535 -0
- package/dist/plugins/vipunen/config/schema-config.json +19 -0
- package/dist/plugins/vipunen/config/schema-doc.json +22 -0
- package/dist/plugins/vipunen/index.d.ts +41 -0
- package/dist/plugins/vipunen/index.js +88 -0
- package/dist/plugins/vipunen/tools.d.ts +26 -0
- package/dist/plugins/vipunen/tools.js +501 -0
- package/dist/stdio-server.d.ts +14 -0
- package/dist/stdio-server.js +101 -0
- package/package.json +2 -1
- package/.claude/agents/agent-ada-skill-builder.md +0 -94
- package/.claude/agents/agent-alejandro-function-fields.md +0 -342
- package/.claude/agents/agent-bjorn-config-audit.md +0 -103
- package/.claude/agents/agent-builder-agent-creator.md +0 -130
- package/.claude/agents/agent-code-simplifier.md +0 -53
- package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
- package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
- package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
- package/.claude/agents/agent-helga-workflow-config.md +0 -204
- package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
- package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
- package/.claude/agents/agent-ivan-monolith.md +0 -154
- package/.claude/agents/agent-kenji-data-reader.md +0 -86
- package/.claude/agents/agent-lars-code-inspector.md +0 -102
- package/.claude/agents/agent-marco-mockup-builder.md +0 -110
- package/.claude/agents/agent-marcus-api-documenter.md +0 -323
- package/.claude/agents/agent-marketplace-publisher.md +0 -280
- package/.claude/agents/agent-marketplace-reviewer.md +0 -309
- package/.claude/agents/agent-permissions-handler.md +0 -208
- package/.claude/agents/agent-simple-writer.md +0 -48
- package/.claude/agents/agent-svetlana-code-review.md +0 -171
- package/.claude/agents/agent-tanya-test-runner.md +0 -333
- package/.claude/agents/agent-ui-designer.md +0 -100
- package/.claude/agents/agent-viktor-sql-insights.md +0 -212
- package/.claude/agents/agent-web-search.md +0 -55
- package/.claude/agents/agent-yevgeni-discussions.md +0 -45
- package/.claude/agents/agent-zara-zapier.md +0 -159
- package/.claude/commands/app-squad.md +0 -135
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/autoplan.md +0 -563
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/debug-squad.md +0 -303
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/handoff.md +0 -137
- package/.claude/commands/health.md +0 -49
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -151
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -93
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/learn-auto.md +0 -120
- package/.claude/commands/learn.md +0 -120
- package/.claude/commands/mcp-list.md +0 -27
- package/.claude/commands/onboard-squad.md +0 -140
- package/.claude/commands/plan-workspace.md +0 -732
- package/.claude/commands/prd.md +0 -130
- package/.claude/commands/project-status.md +0 -82
- package/.claude/commands/publish.md +0 -138
- package/.claude/commands/recap.md +0 -69
- package/.claude/commands/restore.md +0 -64
- package/.claude/commands/review-squad.md +0 -152
- package/.claude/commands/save.md +0 -24
- package/.claude/commands/stats.md +0 -19
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/commands/ws-pull.md +0 -44
- package/.claude/hooks/_shared-memory.cjs +0 -305
- package/.claude/hooks/_utils.cjs +0 -108
- package/.claude/hooks/agent-failure-detector.cjs +0 -383
- package/.claude/hooks/agent-usage-logger.cjs +0 -204
- package/.claude/hooks/app-edit-guard.cjs +0 -494
- package/.claude/hooks/auto-learn.cjs +0 -304
- package/.claude/hooks/bash-guard.cjs +0 -272
- package/.claude/hooks/builder-mode-manager.cjs +0 -354
- package/.claude/hooks/bulk-activity-guard.cjs +0 -271
- package/.claude/hooks/context-watchdog.cjs +0 -230
- package/.claude/hooks/delegation-reminder.cjs +0 -465
- package/.claude/hooks/design-system-lint.cjs +0 -271
- package/.claude/hooks/post-scaffold-hook.cjs +0 -181
- package/.claude/hooks/prompt-guard.cjs +0 -354
- package/.claude/hooks/publish-template-guard.cjs +0 -147
- package/.claude/hooks/session-start.cjs +0 -35
- package/.claude/hooks/shared-memory-writer.cjs +0 -147
- package/.claude/hooks/skill-injector.cjs +0 -140
- package/.claude/hooks/skill-usage-logger.cjs +0 -258
- package/.claude/hooks/src-edit-guard.cjs +0 -240
- package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
- package/.claude/settings.json +0 -257
- package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
- package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
- package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
- package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
- package/.claude/skills/agent-structure/SKILL.md +0 -98
- package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
- package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
- package/.claude/skills/delegation-routing/SKILL.md +0 -202
- package/.claude/skills/frontend-design/SKILL.md +0 -254
- package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
- package/.claude/skills/hailer-api-client/SKILL.md +0 -518
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
- package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
- package/.claude/skills/hailer-design-system/SKILL.md +0 -235
- package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
- package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
- package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
- package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
- package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
- package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
- package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
- package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
- package/.claude/skills/integration-patterns/SKILL.md +0 -421
- package/.claude/skills/json-only-output/SKILL.md +0 -72
- package/.claude/skills/lsp-setup/SKILL.md +0 -160
- package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
- package/.claude/skills/optional-parameters/SKILL.md +0 -72
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
- package/.claude/skills/testing-patterns/SKILL.md +0 -630
- package/.claude/skills/tool-builder/SKILL.md +0 -250
- package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
- package/.claude/skills/tool-response-verification/SKILL.md +0 -92
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
- package/.mcp.json +0 -13
- package/.opencode/agent/agent-ada-skill-builder.md +0 -35
- package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
- package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
- package/.opencode/agent/agent-builder-agent-creator.md +0 -39
- package/.opencode/agent/agent-code-simplifier.md +0 -31
- package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
- package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
- package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
- package/.opencode/agent/agent-helga-workflow-config.md +0 -203
- package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
- package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
- package/.opencode/agent/agent-ivan-monolith.md +0 -46
- package/.opencode/agent/agent-kenji-data-reader.md +0 -53
- package/.opencode/agent/agent-lars-code-inspector.md +0 -28
- package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
- package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
- package/.opencode/agent/agent-marketplace-publisher.md +0 -44
- package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
- package/.opencode/agent/agent-permissions-handler.md +0 -50
- package/.opencode/agent/agent-simple-writer.md +0 -45
- package/.opencode/agent/agent-svetlana-code-review.md +0 -39
- package/.opencode/agent/agent-tanya-test-runner.md +0 -57
- package/.opencode/agent/agent-ui-designer.md +0 -56
- package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
- package/.opencode/agent/agent-web-search.md +0 -42
- package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
- package/.opencode/agent/agent-zara-zapier.md +0 -53
- package/.opencode/commands/app-squad.md +0 -135
- package/.opencode/commands/audit-squad.md +0 -158
- package/.opencode/commands/autoplan.md +0 -563
- package/.opencode/commands/cleanup-squad.md +0 -98
- package/.opencode/commands/config-squad.md +0 -106
- package/.opencode/commands/crud-squad.md +0 -87
- package/.opencode/commands/data-squad.md +0 -97
- package/.opencode/commands/debug-squad.md +0 -303
- package/.opencode/commands/doc-squad.md +0 -65
- package/.opencode/commands/handoff.md +0 -137
- package/.opencode/commands/health.md +0 -49
- package/.opencode/commands/help-agents.md +0 -151
- package/.opencode/commands/help-commands.md +0 -32
- package/.opencode/commands/help-faq.md +0 -29
- package/.opencode/commands/help-plugins.md +0 -28
- package/.opencode/commands/help-skills.md +0 -7
- package/.opencode/commands/help-tools.md +0 -40
- package/.opencode/commands/help.md +0 -28
- package/.opencode/commands/hotfix-squad.md +0 -112
- package/.opencode/commands/integration-squad.md +0 -82
- package/.opencode/commands/janitor-squad.md +0 -167
- package/.opencode/commands/learn-auto.md +0 -120
- package/.opencode/commands/learn.md +0 -120
- package/.opencode/commands/mcp-list.md +0 -27
- package/.opencode/commands/onboard-squad.md +0 -140
- package/.opencode/commands/plan-workspace.md +0 -732
- package/.opencode/commands/prd.md +0 -131
- package/.opencode/commands/project-status.md +0 -82
- package/.opencode/commands/publish.md +0 -138
- package/.opencode/commands/recap.md +0 -69
- package/.opencode/commands/restore.md +0 -64
- package/.opencode/commands/review-squad.md +0 -152
- package/.opencode/commands/save.md +0 -24
- package/.opencode/commands/stats.md +0 -19
- package/.opencode/commands/swarm.md +0 -210
- package/.opencode/commands/tool-builder.md +0 -39
- package/.opencode/commands/ws-pull.md +0 -44
- package/.opencode/opencode.json +0 -28
- package/SESSION-HANDOFF.md +0 -68
- package/inbox/2026-03-04-bot-config-patterns.md +0 -24
- package/scripts/postinstall.cjs +0 -64
- 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@
|
|
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:
|
|
253
|
+
// Step 4: Find existing dev app or create new one
|
|
254
254
|
if (args.autoCreateDevApp !== false) {
|
|
255
|
-
responseText += `⏳ Step 4/8:
|
|
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
|
-
//
|
|
263
|
-
let imageId;
|
|
262
|
+
// Check for existing dev app (localhost:3000) to reuse
|
|
264
263
|
try {
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
287
|
-
|
|
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
|
|
299
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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: `❌ **
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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('
|
|
574
|
+
logger.error('Manifest not found and no appId provided');
|
|
532
575
|
return {
|
|
533
576
|
content: [{
|
|
534
577
|
type: "text",
|
|
535
|
-
text: `❌
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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('
|
|
591
|
+
logger.error('Failed to parse manifest.json', { error: errorMessage });
|
|
545
592
|
return {
|
|
546
593
|
content: [{
|
|
547
594
|
type: "text",
|
|
548
|
-
text: `❌ **
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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('
|
|
605
|
+
logger.error('No appId found');
|
|
557
606
|
return {
|
|
558
607
|
content: [{
|
|
559
608
|
type: "text",
|
|
560
|
-
text: `❌ **
|
|
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
|
-
//
|
|
565
|
-
|
|
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('
|
|
616
|
+
logger.error('version field missing or empty');
|
|
623
617
|
return {
|
|
624
618
|
content: [{
|
|
625
619
|
type: "text",
|
|
626
|
-
text: `❌ **
|
|
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
|
-
|
|
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('
|
|
626
|
+
logger.error('versionDescription field missing or empty');
|
|
648
627
|
return {
|
|
649
628
|
content: [{
|
|
650
629
|
type: "text",
|
|
651
|
-
text: `❌ **
|
|
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
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
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
|
-
|
|
654
|
+
await context.hailer.request('v3.app.member.add', [prodAppId, `network_${workspaceId}`]);
|
|
749
655
|
}
|
|
750
|
-
catch { }
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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
|
-
|
|
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
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
-
|
|
795
|
-
|
|
751
|
+
statusCode: res.statusCode || 500,
|
|
752
|
+
body: Buffer.concat(chunks).toString('utf-8'),
|
|
796
753
|
});
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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
|
-
|
|
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${
|
|
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
|
-
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
responseText +=
|
|
842
|
-
|
|
843
|
-
|
|
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
|
-
|
|
846
|
-
responseText +=
|
|
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 +=
|
|
851
|
-
responseText += `1. **Open published app in Hailer**
|
|
852
|
-
responseText += `2. **
|
|
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_
|
|
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: `❌ **
|
|
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
|
}
|