@hailer/mcp 0.0.6 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/ada.md +127 -0
- package/.claude/agents/agent-builder.md +151 -0
- package/.claude/agents/alejandro.md +66 -0
- package/.claude/agents/bjorn.md +305 -0
- package/.claude/agents/dmitri.md +61 -0
- package/.claude/agents/giuseppe.md +66 -0
- package/.claude/agents/gunther.md +355 -0
- package/.claude/agents/helga.md +68 -0
- package/.claude/agents/kenji.md +58 -0
- package/.claude/agents/svetlana.md +394 -0
- package/.claude/agents/viktor.md +63 -0
- package/.claude/agents/yevgeni.md +60 -0
- package/.claude/hooks/agent-failure-detector.cjs +286 -0
- package/.claude/hooks/app-edit-guard.cjs +462 -0
- package/.claude/hooks/interactive-mode.cjs +59 -0
- package/.claude/hooks/mcp-server-guard.cjs +92 -0
- package/.claude/hooks/post-scaffold-hook.cjs +31 -0
- package/.claude/hooks/src-edit-guard.cjs +208 -0
- package/.claude/settings.json +47 -2
- package/.claude/skills/insight-join-patterns/SKILL.md +209 -0
- package/.env.example +13 -1
- package/CLAUDE.md +134 -0
- package/dist/app.js +4 -3
- package/dist/cli.js +0 -0
- package/dist/client/adaptive-documentation-bot.d.ts +0 -2
- package/dist/client/adaptive-documentation-bot.js +5 -16
- package/dist/client/message-processor.js +5 -0
- package/dist/client/providers/anthropic-provider.js +21 -7
- package/dist/mcp/UserContextCache.d.ts +14 -0
- package/dist/mcp/UserContextCache.js +49 -24
- package/dist/mcp/auth.d.ts +7 -0
- package/dist/mcp/auth.js +13 -5
- package/dist/mcp/hailer-clients.d.ts +5 -2
- package/dist/mcp/signal-handler.d.ts +28 -2
- package/dist/mcp/signal-handler.js +4 -2
- package/dist/mcp/tool-registry.d.ts +55 -2
- package/dist/mcp/tool-registry.js +197 -2
- package/dist/mcp/tools/app-core.d.ts +15 -0
- package/dist/mcp/tools/app-core.js +609 -0
- package/dist/mcp/tools/app-marketplace.d.ts +21 -0
- package/dist/mcp/tools/app-marketplace.js +1284 -0
- package/dist/mcp/tools/app-member.d.ts +11 -0
- package/dist/mcp/tools/app-member.js +258 -0
- package/dist/mcp/tools/app-scaffold.d.ts +11 -0
- package/dist/mcp/tools/app-scaffold.js +743 -0
- package/dist/mcp/tools/app.d.ts +13 -22
- package/dist/mcp/tools/app.js +17 -2466
- package/dist/mcp/tools/file.js +6 -6
- package/dist/mcp/tools/insight.d.ts +1 -0
- package/dist/mcp/tools/insight.js +203 -64
- package/dist/mcp/tools/user.js +3 -9
- package/dist/mcp/tools/workflow.js +49 -38
- package/dist/mcp/utils/hailer-api-client.js +4 -13
- package/dist/mcp/utils/tool-helpers.d.ts +102 -0
- package/dist/mcp/utils/tool-helpers.js +179 -0
- package/dist/mcp/utils/types.d.ts +6 -0
- package/dist/mcp/workspace-cache.d.ts +5 -5
- package/dist/mcp/workspace-cache.js +4 -3
- package/package.json +1 -1
- package/.claude/hooks/PreToolUse.sh +0 -52
- package/.claude/hooks/prompt-skill-loader.cjs +0 -553
- package/.claude/hooks/skill-loader.cjs +0 -142
- package/.claude/settings.local.json +0 -49
- package/.claude/skills/MCP-add-app-member-skill/SKILL.md +0 -977
- package/.claude/skills/MCP-build-data-app-skill/SKILL.md +0 -372
- package/.claude/skills/MCP-create-app-skill/SKILL.md +0 -1101
- package/.claude/skills/MCP-create-insight-skill/SKILL.md +0 -1317
- package/.claude/skills/MCP-get-insight-data-skill/SKILL.md +0 -1053
- package/.claude/skills/MCP-insight-api/SKILL.md +0 -185
- package/.claude/skills/MCP-insight-api/references/insight-endpoints.md +0 -514
- package/.claude/skills/MCP-install-workflow-skill/SKILL.md +0 -1056
- package/.claude/skills/MCP-list-apps-skill/SKILL.md +0 -1010
- package/.claude/skills/MCP-list-workflows-minimal-skill/SKILL.md +0 -992
- package/.claude/skills/MCP-local-first-skill/SKILL.md +0 -570
- package/.claude/skills/MCP-populate-workflow-data-skill/SKILL.md +0 -395
- package/.claude/skills/MCP-preview-insight-skill/SKILL.md +0 -1290
- package/.claude/skills/MCP-publish-hailer-app-skill/SKILL.md +0 -453
- package/.claude/skills/MCP-publish-template-skill/SKILL.md +0 -278
- package/.claude/skills/MCP-remove-app-member-skill/SKILL.md +0 -671
- package/.claude/skills/MCP-remove-app-skill/SKILL.md +0 -985
- package/.claude/skills/MCP-remove-insight-skill/SKILL.md +0 -1011
- package/.claude/skills/MCP-remove-workflow-skill/SKILL.md +0 -920
- package/.claude/skills/MCP-scaffold-hailer-app-skill/SKILL.md +0 -1314
- package/.claude/skills/MCP-update-app-skill/SKILL.md +0 -970
- package/.claude/skills/MCP-update-workflow-field-skill/SKILL.md +0 -1098
- package/.claude/skills/SDK-create-function-field-skill/SKILL.md +0 -313
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -223
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -177
- package/.claude/skills/SDK-workspace-setup-skill/SKILL.md +0 -605
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -435
- package/.claude/skills/activity-api/SKILL.md +0 -96
- package/.claude/skills/activity-api/references/activity-endpoints.md +0 -845
- package/.claude/skills/agent-building/SKILL.md +0 -243
- package/.claude/skills/agent-building/references/architecture-patterns.md +0 -446
- package/.claude/skills/agent-building/references/code-examples.md +0 -587
- package/.claude/skills/agent-building/references/implementation-guide.md +0 -619
- package/.claude/skills/app-api/SKILL.md +0 -219
- package/.claude/skills/app-api/references/app-endpoints.md +0 -759
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +0 -813
- package/.claude/skills/hailer-api/SKILL.md +0 -283
- package/.claude/skills/hailer-api/references/activities.md +0 -620
- package/.claude/skills/hailer-api/references/authentication.md +0 -216
- package/.claude/skills/hailer-api/references/datasets.md +0 -437
- package/.claude/skills/hailer-api/references/files.md +0 -301
- package/.claude/skills/hailer-api/references/insights.md +0 -469
- package/.claude/skills/hailer-api/references/workflows.md +0 -720
- package/.claude/skills/hailer-api/references/workspaces-users.md +0 -445
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -340
- package/.claude/skills/mcp-tools/SKILL.md +0 -419
- package/.claude/skills/mcp-tools/references/api-endpoints.md +0 -499
- package/.claude/skills/mcp-tools/references/data-structures.md +0 -554
- package/.claude/skills/mcp-tools/references/implementation-patterns.md +0 -717
- package/.claude/skills/skill-testing/README.md +0 -137
- package/.claude/skills/skill-testing/SKILL.md +0 -348
- package/.claude/skills/skill-testing/references/test-patterns.md +0 -705
- package/.claude/skills/skill-testing/references/testing-guide.md +0 -603
- package/.claude/skills/skill-testing/references/validation-checklist.md +0 -537
- package/.claude/skills/spawn-app-builder/SKILL.md +0 -366
- package/.claude/skills/tool-builder/SKILL.md +0 -328
- package/tsconfig.json +0 -23
|
@@ -0,0 +1,1284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* App Tools - Marketplace & Templates
|
|
4
|
+
*
|
|
5
|
+
* App marketplace and template management tools:
|
|
6
|
+
* - List, create, get, and install templates
|
|
7
|
+
* - Publish templates to marketplace
|
|
8
|
+
* - Publish apps to marketplace
|
|
9
|
+
* - Install marketplace apps
|
|
10
|
+
* - Get product info and manifests
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.installMarketplaceAppTool = exports.publishAppTool = exports.getProductManifestTool = exports.getProductTool = exports.publishTemplateTool = exports.getTemplateTool = exports.installTemplateTool = exports.createTemplateTool = exports.listTemplatesTool = void 0;
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
const tool_registry_1 = require("../tool-registry");
|
|
16
|
+
const logger_1 = require("../../lib/logger");
|
|
17
|
+
const tool_helpers_1 = require("../utils/tool-helpers");
|
|
18
|
+
const logger = (0, logger_1.createLogger)({ component: 'app-marketplace' });
|
|
19
|
+
const listTemplatesDescription = `🏪 [PLAYGROUND] List Templates - View marketplace templates available
|
|
20
|
+
|
|
21
|
+
**What it does**:
|
|
22
|
+
Lists all workflow templates available in the Hailer marketplace.
|
|
23
|
+
|
|
24
|
+
**Example**:
|
|
25
|
+
\`\`\`javascript
|
|
26
|
+
list_templates()
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
**Shows**:
|
|
30
|
+
- Template name and description
|
|
31
|
+
- Template ID
|
|
32
|
+
- Creator info
|
|
33
|
+
- Installation status
|
|
34
|
+
|
|
35
|
+
**Use Cases**:
|
|
36
|
+
- Discover available templates
|
|
37
|
+
- Find template IDs for installation
|
|
38
|
+
- Browse marketplace offerings
|
|
39
|
+
|
|
40
|
+
**Note**: Products created with \`create_template\` are workspace-specific and won't appear here until published to the public marketplace.`;
|
|
41
|
+
exports.listTemplatesTool = {
|
|
42
|
+
name: 'list_templates',
|
|
43
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
44
|
+
description: listTemplatesDescription,
|
|
45
|
+
schema: zod_1.z.object({
|
|
46
|
+
workspaceId: zod_1.z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Optional workspace ID - defaults to current workspace"),
|
|
50
|
+
publicOnly: zod_1.z
|
|
51
|
+
.boolean()
|
|
52
|
+
.optional()
|
|
53
|
+
.default(false)
|
|
54
|
+
.describe("If true, show only public marketplace templates. If false (default), show private/workspace templates."),
|
|
55
|
+
}),
|
|
56
|
+
async execute(args, context) {
|
|
57
|
+
const showPublic = args.publicOnly ?? false;
|
|
58
|
+
logger.debug('Listing marketplace templates', {
|
|
59
|
+
workspaceId: args.workspaceId,
|
|
60
|
+
publicOnly: showPublic,
|
|
61
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
const workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)(args, context);
|
|
65
|
+
if (!workspaceId) {
|
|
66
|
+
return (0, tool_helpers_1.missingWorkspaceCacheResponse)();
|
|
67
|
+
}
|
|
68
|
+
logger.debug('Calling v3.product.list', { workspaceId, public: showPublic });
|
|
69
|
+
// v3.product.list endpoint with public flag
|
|
70
|
+
const result = await context.hailer.request('v3.product.list', [
|
|
71
|
+
{ cid: workspaceId, public: showPublic },
|
|
72
|
+
{} // pagination options
|
|
73
|
+
]);
|
|
74
|
+
logger.debug('Template list response', {
|
|
75
|
+
result: JSON.stringify(result)
|
|
76
|
+
});
|
|
77
|
+
const templateType = showPublic ? 'Public Marketplace' : 'Private/Workspace';
|
|
78
|
+
let responseText = `✅ **${templateType} Templates**\n\n`;
|
|
79
|
+
responseText += `**Workspace:** ${workspaceId}\n`;
|
|
80
|
+
responseText += `**Filter:** ${showPublic ? 'Public only' : 'Private/workspace'}\n\n`;
|
|
81
|
+
// Handle response: {products: [], totalCount: 0}
|
|
82
|
+
const products = result?.products || [];
|
|
83
|
+
const totalCount = result?.totalCount || 0;
|
|
84
|
+
if (products.length === 0) {
|
|
85
|
+
responseText += `**No ${showPublic ? 'public' : 'private'} templates found.**\n\n`;
|
|
86
|
+
if (showPublic) {
|
|
87
|
+
responseText += `💡 Try \`list_templates()\` without \`publicOnly: true\` to see private workspace templates.\n`;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
responseText += `💡 Use \`create_template\` to create a new template.\n`;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
content: [{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: responseText,
|
|
96
|
+
}],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const templates = products;
|
|
100
|
+
responseText += `**Total Templates:** ${templates.length}\n\n`;
|
|
101
|
+
templates.forEach((template, index) => {
|
|
102
|
+
responseText += `### ${index + 1}. ${template.name || 'Unnamed Template'}\n`;
|
|
103
|
+
responseText += `- **ID:** \`${template._id || template.id || 'N/A'}\`\n`;
|
|
104
|
+
if (template.description) {
|
|
105
|
+
responseText += `- **Description:** ${template.description}\n`;
|
|
106
|
+
}
|
|
107
|
+
if (template.creator) {
|
|
108
|
+
responseText += `- **Creator:** ${template.creator}\n`;
|
|
109
|
+
}
|
|
110
|
+
if (template.version) {
|
|
111
|
+
responseText += `- **Version:** ${template.version}\n`;
|
|
112
|
+
}
|
|
113
|
+
if (template.icon) {
|
|
114
|
+
responseText += `- **Icon:** \`${template.icon}\`\n`;
|
|
115
|
+
}
|
|
116
|
+
if (template.images && template.images.length > 0) {
|
|
117
|
+
responseText += `- **Images:** ${template.images.map((img) => `\`${img}\``).join(', ')}\n`;
|
|
118
|
+
}
|
|
119
|
+
responseText += `\n`;
|
|
120
|
+
});
|
|
121
|
+
responseText += `💡 **Next Steps:**\n`;
|
|
122
|
+
responseText += `- Use \`install_template\` to install a template\n`;
|
|
123
|
+
responseText += `- Use \`get_template\` to see template details`;
|
|
124
|
+
return {
|
|
125
|
+
content: [{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: responseText,
|
|
128
|
+
}],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
logger.error("Error listing templates", error);
|
|
133
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
134
|
+
return {
|
|
135
|
+
content: [{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: `❌ **Error listing templates**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- API endpoint not available\n- Permission issues`,
|
|
138
|
+
}],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// CREATE TEMPLATE TOOL (MARKETPLACE)
|
|
145
|
+
// ============================================================================
|
|
146
|
+
const createTemplateDescription = `🏪 [PLAYGROUND] Create Template - Add template to marketplace
|
|
147
|
+
|
|
148
|
+
**What it does**:
|
|
149
|
+
Creates a new workflow template in the Hailer marketplace.
|
|
150
|
+
|
|
151
|
+
**Example**:
|
|
152
|
+
\`\`\`javascript
|
|
153
|
+
create_template({
|
|
154
|
+
name: 'Project Management',
|
|
155
|
+
description: 'Template for managing projects',
|
|
156
|
+
workflowId: '<source-workflow-id>'
|
|
157
|
+
})
|
|
158
|
+
\`\`\`
|
|
159
|
+
|
|
160
|
+
**Parameters**:
|
|
161
|
+
- \`name\` (required) - Template name
|
|
162
|
+
- \`description\` (optional) - Template description
|
|
163
|
+
- \`workflowId\` (optional) - Source workflow to create template from
|
|
164
|
+
|
|
165
|
+
**Requirements**:
|
|
166
|
+
- User must be workspace administrator
|
|
167
|
+
|
|
168
|
+
**Tips**:
|
|
169
|
+
- Create template from existing workflow
|
|
170
|
+
- Use descriptive names for discoverability`;
|
|
171
|
+
exports.createTemplateTool = {
|
|
172
|
+
name: 'create_template',
|
|
173
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
174
|
+
description: createTemplateDescription,
|
|
175
|
+
schema: zod_1.z.object({
|
|
176
|
+
name: zod_1.z
|
|
177
|
+
.string()
|
|
178
|
+
.min(1)
|
|
179
|
+
.describe("Template name (required)"),
|
|
180
|
+
description: zod_1.z
|
|
181
|
+
.string()
|
|
182
|
+
.optional()
|
|
183
|
+
.describe("Template description"),
|
|
184
|
+
workflowId: zod_1.z
|
|
185
|
+
.string()
|
|
186
|
+
.optional()
|
|
187
|
+
.describe("Source workflow ID to create template from"),
|
|
188
|
+
}),
|
|
189
|
+
async execute(args, context) {
|
|
190
|
+
logger.debug('Creating marketplace template', {
|
|
191
|
+
name: args.name,
|
|
192
|
+
hasWorkflowId: !!args.workflowId,
|
|
193
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
194
|
+
});
|
|
195
|
+
try {
|
|
196
|
+
const workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)({}, context);
|
|
197
|
+
if (!workspaceId) {
|
|
198
|
+
return (0, tool_helpers_1.missingWorkspaceCacheResponse)();
|
|
199
|
+
}
|
|
200
|
+
// v2.network.product.create - creates a product entry (only accepts name)
|
|
201
|
+
const templateData = {
|
|
202
|
+
name: args.name
|
|
203
|
+
};
|
|
204
|
+
logger.debug('Calling v2.network.product.create', {
|
|
205
|
+
workspaceId,
|
|
206
|
+
templateData
|
|
207
|
+
});
|
|
208
|
+
const result = await context.hailer.request('v2.network.product.create', [templateData]);
|
|
209
|
+
logger.debug('Template creation response', {
|
|
210
|
+
result: JSON.stringify(result)
|
|
211
|
+
});
|
|
212
|
+
const templateId = result?.productId || result?._id || result?.id;
|
|
213
|
+
let responseText = `✅ **Template Created Successfully**\n\n`;
|
|
214
|
+
responseText += `**Template Name:** ${args.name}\n`;
|
|
215
|
+
responseText += `**Template ID:** \`${templateId || 'See response'}\`\n`;
|
|
216
|
+
responseText += `**Workspace:** ${workspaceId}\n`;
|
|
217
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
218
|
+
responseText += `- Use Hailer marketplace UI to add workflows and description\n`;
|
|
219
|
+
responseText += `- Share the template ID with others to install\n`;
|
|
220
|
+
if (args.description || args.workflowId) {
|
|
221
|
+
responseText += `\n⚠️ **Note:** The \`v3.app.product.create\` API only accepts \`name\`.\n`;
|
|
222
|
+
responseText += `Description and workflow attachment must be done via Hailer UI.\n`;
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
content: [{
|
|
226
|
+
type: "text",
|
|
227
|
+
text: responseText,
|
|
228
|
+
}],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
logger.error("Error creating template", error);
|
|
233
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
234
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
235
|
+
return {
|
|
236
|
+
content: [{
|
|
237
|
+
type: "text",
|
|
238
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to create templates. Only workspace administrators can create templates.\n\n**Error:** ${errorMessage}`,
|
|
239
|
+
}],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
content: [{
|
|
244
|
+
type: "text",
|
|
245
|
+
text: `❌ **Error creating template**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- User must be workspace administrator\n- Invalid workflow ID`,
|
|
246
|
+
}],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// INSTALL TEMPLATE TOOL (MARKETPLACE)
|
|
253
|
+
// ============================================================================
|
|
254
|
+
const installTemplateDescription = `🏪 [PLAYGROUND] Install Template - Install marketplace template to workspace
|
|
255
|
+
|
|
256
|
+
**What it does**:
|
|
257
|
+
Installs a template from the Hailer marketplace into your workspace.
|
|
258
|
+
|
|
259
|
+
**Example**:
|
|
260
|
+
\`\`\`javascript
|
|
261
|
+
install_template({
|
|
262
|
+
templateId: '<template-id>'
|
|
263
|
+
})
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
**Parameters**:
|
|
267
|
+
- \`templateId\` (required) - Template/Product ID from marketplace
|
|
268
|
+
|
|
269
|
+
**Requirements**:
|
|
270
|
+
- User must be workspace administrator
|
|
271
|
+
|
|
272
|
+
**Tips**:
|
|
273
|
+
- Use \`list_templates\` to find template IDs
|
|
274
|
+
- Template creates new workflow(s) in workspace`;
|
|
275
|
+
exports.installTemplateTool = {
|
|
276
|
+
name: 'install_template',
|
|
277
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
278
|
+
description: installTemplateDescription,
|
|
279
|
+
schema: zod_1.z.object({
|
|
280
|
+
templateId: zod_1.z
|
|
281
|
+
.string()
|
|
282
|
+
.min(1)
|
|
283
|
+
.describe("Template/Product ID to install"),
|
|
284
|
+
workspaceId: zod_1.z
|
|
285
|
+
.string()
|
|
286
|
+
.optional()
|
|
287
|
+
.describe("Target workspace ID (defaults to current)"),
|
|
288
|
+
}),
|
|
289
|
+
async execute(args, context) {
|
|
290
|
+
logger.debug('Installing marketplace template', {
|
|
291
|
+
templateId: args.templateId,
|
|
292
|
+
workspaceId: args.workspaceId,
|
|
293
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
294
|
+
});
|
|
295
|
+
try {
|
|
296
|
+
const workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)(args, context);
|
|
297
|
+
if (!workspaceId) {
|
|
298
|
+
return (0, tool_helpers_1.missingWorkspaceCacheResponse)();
|
|
299
|
+
}
|
|
300
|
+
logger.debug('Calling v2.network.product.install', {
|
|
301
|
+
templateId: args.templateId,
|
|
302
|
+
workspaceId
|
|
303
|
+
});
|
|
304
|
+
const result = await context.hailer.request('v2.network.product.install', [
|
|
305
|
+
args.templateId,
|
|
306
|
+
workspaceId
|
|
307
|
+
]);
|
|
308
|
+
logger.debug('Template installation response', {
|
|
309
|
+
result: JSON.stringify(result)
|
|
310
|
+
});
|
|
311
|
+
const appId = result?.appId || result?._id;
|
|
312
|
+
let responseText = `✅ **Template Installed Successfully**\n\n`;
|
|
313
|
+
responseText += `**Template ID:** \`${args.templateId}\`\n`;
|
|
314
|
+
responseText += `**Workspace:** ${workspaceId}\n`;
|
|
315
|
+
if (appId) {
|
|
316
|
+
responseText += `**App ID:** \`${appId}\`\n`;
|
|
317
|
+
}
|
|
318
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
319
|
+
responseText += `- Use \`list_workflows\` to see new workflow(s)\n`;
|
|
320
|
+
responseText += `- Use \`list_apps\` to see new app(s)\n`;
|
|
321
|
+
return {
|
|
322
|
+
content: [{
|
|
323
|
+
type: "text",
|
|
324
|
+
text: responseText,
|
|
325
|
+
}],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
logger.error("Error installing template", error);
|
|
330
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
331
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
332
|
+
return {
|
|
333
|
+
content: [{
|
|
334
|
+
type: "text",
|
|
335
|
+
text: `❌ **Template Not Found**\n\nTemplate ID \`${args.templateId}\` not found in marketplace.\n\n**Check:**\n- Template ID is correct\n- Template is published and available\n\n💡 Use \`list_templates\` to see available templates.`,
|
|
336
|
+
}],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
340
|
+
return {
|
|
341
|
+
content: [{
|
|
342
|
+
type: "text",
|
|
343
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to install templates. Only workspace administrators can install templates.\n\n**Error:** ${errorMessage}`,
|
|
344
|
+
}],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (errorMessage.toLowerCase().includes('already installed')) {
|
|
348
|
+
return {
|
|
349
|
+
content: [{
|
|
350
|
+
type: "text",
|
|
351
|
+
text: `⚠️ **Template Already Installed**\n\nThis template is already installed in the workspace.\n\n**Template ID:** \`${args.templateId}\`\n\n💡 Use \`list_apps\` to see installed apps.`,
|
|
352
|
+
}],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Include debug info for troubleshooting
|
|
356
|
+
const debugInfo = {
|
|
357
|
+
endpoint: 'v2.network.product.install',
|
|
358
|
+
requestData: [args.templateId, args.workspaceId || 'current-workspace']
|
|
359
|
+
};
|
|
360
|
+
return {
|
|
361
|
+
content: [{
|
|
362
|
+
type: "text",
|
|
363
|
+
text: `❌ **Error installing template**\n\n**Error:** ${errorMessage}\n\n**Debug Info:**\n\`\`\`json\n${JSON.stringify(debugInfo, null, 2)}\n\`\`\`\n\n**Common Issues:**\n- Invalid template ID\n- Template not found\n- Permission denied\n- Already installed`,
|
|
364
|
+
}],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
// ============================================================================
|
|
370
|
+
// GET TEMPLATE TOOL (MARKETPLACE)
|
|
371
|
+
// ============================================================================
|
|
372
|
+
const getTemplateDescription = `🏪 [PLAYGROUND] Get Template - View template details from marketplace
|
|
373
|
+
|
|
374
|
+
**What it does**:
|
|
375
|
+
Retrieves detailed information about a specific marketplace template.
|
|
376
|
+
|
|
377
|
+
**Example**:
|
|
378
|
+
\`\`\`javascript
|
|
379
|
+
get_template({
|
|
380
|
+
templateId: '<template-id>'
|
|
381
|
+
})
|
|
382
|
+
\`\`\`
|
|
383
|
+
|
|
384
|
+
**Shows**:
|
|
385
|
+
- Template name and description
|
|
386
|
+
- Template version
|
|
387
|
+
- Included workflows
|
|
388
|
+
- Manifest information
|
|
389
|
+
|
|
390
|
+
**Use Cases**:
|
|
391
|
+
- Preview template before installing
|
|
392
|
+
- Check template version
|
|
393
|
+
- View template structure`;
|
|
394
|
+
exports.getTemplateTool = {
|
|
395
|
+
name: 'get_template',
|
|
396
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
397
|
+
description: getTemplateDescription,
|
|
398
|
+
schema: zod_1.z.object({
|
|
399
|
+
templateId: zod_1.z
|
|
400
|
+
.string()
|
|
401
|
+
.min(1)
|
|
402
|
+
.describe("Template/Product ID to get details for"),
|
|
403
|
+
}),
|
|
404
|
+
async execute(args, context) {
|
|
405
|
+
logger.debug('Getting marketplace template', {
|
|
406
|
+
templateId: args.templateId,
|
|
407
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
408
|
+
});
|
|
409
|
+
try {
|
|
410
|
+
logger.debug('Calling v2.network.product.get', {
|
|
411
|
+
templateId: args.templateId
|
|
412
|
+
});
|
|
413
|
+
const result = await context.hailer.request('v2.network.product.get', [args.templateId]);
|
|
414
|
+
logger.debug('Template get response', {
|
|
415
|
+
result: JSON.stringify(result)
|
|
416
|
+
});
|
|
417
|
+
const product = result?.product || result;
|
|
418
|
+
let responseText = `✅ **Template Details**\n\n`;
|
|
419
|
+
responseText += `**Template ID:** \`${args.templateId}\`\n`;
|
|
420
|
+
if (product?.name) {
|
|
421
|
+
responseText += `**Name:** ${product.name}\n`;
|
|
422
|
+
}
|
|
423
|
+
if (product?.description) {
|
|
424
|
+
responseText += `**Description:** ${product.description}\n`;
|
|
425
|
+
}
|
|
426
|
+
if (product?.version) {
|
|
427
|
+
responseText += `**Version:** ${product.version}\n`;
|
|
428
|
+
}
|
|
429
|
+
// Also try to get manifest
|
|
430
|
+
try {
|
|
431
|
+
const manifest = await context.hailer.request('v2.network.product.getManifest', [args.templateId]);
|
|
432
|
+
logger.debug('Template manifest response', {
|
|
433
|
+
manifest: JSON.stringify(manifest)
|
|
434
|
+
});
|
|
435
|
+
if (manifest) {
|
|
436
|
+
responseText += `\n**Manifest:**\n\`\`\`json\n${JSON.stringify(manifest, null, 2)}\n\`\`\`\n`;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (manifestError) {
|
|
440
|
+
logger.debug('Could not fetch manifest', { error: manifestError });
|
|
441
|
+
}
|
|
442
|
+
// Check if installed (needs productId and workspaceId)
|
|
443
|
+
try {
|
|
444
|
+
if (context.workspaceCache) {
|
|
445
|
+
const workspaceId = context.workspaceCache.currentWorkspace._id;
|
|
446
|
+
const installed = await context.hailer.request('v2.network.product.isProductInstalled', [
|
|
447
|
+
args.templateId,
|
|
448
|
+
workspaceId
|
|
449
|
+
]);
|
|
450
|
+
logger.debug('Template installation check', {
|
|
451
|
+
installed: JSON.stringify(installed)
|
|
452
|
+
});
|
|
453
|
+
if (installed?.installed) {
|
|
454
|
+
responseText += `\n✅ **Status:** Installed in current workspace\n`;
|
|
455
|
+
if (installed.appId) {
|
|
456
|
+
responseText += `**App ID:** \`${installed.appId}\`\n`;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
responseText += `\n⚪ **Status:** Not installed in current workspace\n`;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
catch (installCheckError) {
|
|
465
|
+
logger.debug('Could not check installation status', { error: installCheckError });
|
|
466
|
+
}
|
|
467
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
468
|
+
responseText += `- Use \`install_template\` to install this template\n`;
|
|
469
|
+
responseText += `- Use \`list_templates\` to see other templates`;
|
|
470
|
+
return {
|
|
471
|
+
content: [{
|
|
472
|
+
type: "text",
|
|
473
|
+
text: responseText,
|
|
474
|
+
}],
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
logger.error("Error getting template", error);
|
|
479
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
480
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
481
|
+
return {
|
|
482
|
+
content: [{
|
|
483
|
+
type: "text",
|
|
484
|
+
text: `❌ **Template Not Found**\n\nTemplate ID \`${args.templateId}\` not found in marketplace.\n\n**Check:**\n- Template ID is correct\n- Template exists and is published\n\n💡 Use \`list_templates\` to see available templates.`,
|
|
485
|
+
}],
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
content: [{
|
|
490
|
+
type: "text",
|
|
491
|
+
text: `❌ **Error getting template**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Invalid template ID\n- Template not found`,
|
|
492
|
+
}],
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
// ============================================================================
|
|
498
|
+
// PUBLISH TEMPLATE TOOL (MARKETPLACE)
|
|
499
|
+
// ============================================================================
|
|
500
|
+
const publishTemplateDescription = `🏪 [PLAYGROUND] Publish Template - Publish workspace to marketplace
|
|
501
|
+
|
|
502
|
+
**What it does**:
|
|
503
|
+
Publishes the current workspace as a template to the Hailer marketplace, making it installable by other workspaces.
|
|
504
|
+
|
|
505
|
+
**Example - New Template**:
|
|
506
|
+
\`\`\`javascript
|
|
507
|
+
publish_template({
|
|
508
|
+
title: 'Football Manager',
|
|
509
|
+
description: 'Complete football club management workspace',
|
|
510
|
+
version: '1.0.0',
|
|
511
|
+
versionDescription: 'Initial release with Players, Matches, Training workflows',
|
|
512
|
+
publisher: 'Hailer Oy',
|
|
513
|
+
iconFileId: '692d3e66e98c7bab4af7f0a1'
|
|
514
|
+
})
|
|
515
|
+
\`\`\`
|
|
516
|
+
|
|
517
|
+
**Example - Update Existing Template**:
|
|
518
|
+
\`\`\`javascript
|
|
519
|
+
publish_template({
|
|
520
|
+
productId: '692d46bfca77093f10b0c09a', // Existing product ID
|
|
521
|
+
title: 'Football Manager',
|
|
522
|
+
description: 'Updated description',
|
|
523
|
+
version: '1.1.0',
|
|
524
|
+
versionDescription: 'Fixed icon and added new features',
|
|
525
|
+
publisher: 'Hailer Oy',
|
|
526
|
+
iconFileId: '692d47adf9383bd1a9e26d78'
|
|
527
|
+
})
|
|
528
|
+
\`\`\`
|
|
529
|
+
|
|
530
|
+
**Required Parameters** (gather ALL before calling):
|
|
531
|
+
- \`title\` - Template name (max 64 chars)
|
|
532
|
+
- \`description\` - Template description (max 4096 chars)
|
|
533
|
+
- \`version\` - Version string (e.g. "1.0.0")
|
|
534
|
+
- \`versionDescription\` - Release notes for this version
|
|
535
|
+
- \`publisher\` - Publishing company/person name
|
|
536
|
+
- \`iconFileId\` - Icon file ID (upload with \`upload_files\` first)
|
|
537
|
+
|
|
538
|
+
**Optional Parameters**:
|
|
539
|
+
- \`productId\` - Existing product ID to UPDATE (omit to create new)
|
|
540
|
+
- \`imageFileIds\` - Array of preview image file IDs
|
|
541
|
+
- \`externalUrl\` - External website URL
|
|
542
|
+
|
|
543
|
+
**IMPORTANT**: A PreToolUse hook will BLOCK this call if required fields are missing.
|
|
544
|
+
Gather ALL information from user BEFORE calling this tool.
|
|
545
|
+
|
|
546
|
+
**Workflow**:
|
|
547
|
+
1. Ask user for template details (name, description, version, etc.)
|
|
548
|
+
2. Ask user for icon (URL, path, or existing fileId)
|
|
549
|
+
3. Upload icon with \`upload_files\`
|
|
550
|
+
4. Call \`publish_template\` with ALL fields
|
|
551
|
+
5. To UPDATE: include \`productId\` from previous publish
|
|
552
|
+
|
|
553
|
+
**What Gets Published**:
|
|
554
|
+
- The ENTIRE current workspace is published as a template
|
|
555
|
+
- All workflows, fields, phases are included
|
|
556
|
+
|
|
557
|
+
**After Publishing**:
|
|
558
|
+
- Template appears in \`list_templates()\`
|
|
559
|
+
- Other workspaces can install with \`install_template()\``;
|
|
560
|
+
exports.publishTemplateTool = {
|
|
561
|
+
name: 'publish_template',
|
|
562
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
563
|
+
description: publishTemplateDescription,
|
|
564
|
+
schema: zod_1.z.object({
|
|
565
|
+
productId: zod_1.z
|
|
566
|
+
.string()
|
|
567
|
+
.length(24)
|
|
568
|
+
.optional()
|
|
569
|
+
.describe("Existing product ID to UPDATE (omit to create new template)"),
|
|
570
|
+
title: zod_1.z
|
|
571
|
+
.string()
|
|
572
|
+
.min(1)
|
|
573
|
+
.max(64)
|
|
574
|
+
.describe("Template name (max 64 chars)"),
|
|
575
|
+
description: zod_1.z
|
|
576
|
+
.string()
|
|
577
|
+
.min(1)
|
|
578
|
+
.max(4096)
|
|
579
|
+
.describe("Template description (max 4096 chars)"),
|
|
580
|
+
version: zod_1.z
|
|
581
|
+
.string()
|
|
582
|
+
.min(1)
|
|
583
|
+
.describe("Version string (e.g. '1.0.0')"),
|
|
584
|
+
versionDescription: zod_1.z
|
|
585
|
+
.string()
|
|
586
|
+
.min(1)
|
|
587
|
+
.describe("Release notes for this version"),
|
|
588
|
+
publisher: zod_1.z
|
|
589
|
+
.string()
|
|
590
|
+
.min(1)
|
|
591
|
+
.describe("Publishing company or person name"),
|
|
592
|
+
iconFileId: zod_1.z
|
|
593
|
+
.string()
|
|
594
|
+
.length(24)
|
|
595
|
+
.describe("Icon file ID (24 chars). Upload with upload_files first"),
|
|
596
|
+
imageFileIds: zod_1.z
|
|
597
|
+
.array(zod_1.z.string().length(24))
|
|
598
|
+
.optional()
|
|
599
|
+
.describe("Optional array of preview image file IDs"),
|
|
600
|
+
externalUrl: zod_1.z
|
|
601
|
+
.string()
|
|
602
|
+
.url()
|
|
603
|
+
.optional()
|
|
604
|
+
.describe("Optional external website URL"),
|
|
605
|
+
}),
|
|
606
|
+
async execute(args, context) {
|
|
607
|
+
const isUpdate = !!args.productId;
|
|
608
|
+
logger.debug(`${isUpdate ? 'Updating' : 'Publishing'} template to marketplace`, {
|
|
609
|
+
title: args.title,
|
|
610
|
+
version: args.version,
|
|
611
|
+
productId: args.productId,
|
|
612
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
613
|
+
});
|
|
614
|
+
try {
|
|
615
|
+
const workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)({}, context);
|
|
616
|
+
if (!workspaceId) {
|
|
617
|
+
return (0, tool_helpers_1.missingWorkspaceCacheResponse)();
|
|
618
|
+
}
|
|
619
|
+
let productId = args.productId;
|
|
620
|
+
// Only create new manifest if not updating existing template
|
|
621
|
+
if (!isUpdate) {
|
|
622
|
+
// Note: versionDescription is required and cannot be empty
|
|
623
|
+
const publishData = {
|
|
624
|
+
workspaceId,
|
|
625
|
+
version: args.version,
|
|
626
|
+
versionDescription: args.versionDescription,
|
|
627
|
+
title: args.title,
|
|
628
|
+
description: args.description
|
|
629
|
+
};
|
|
630
|
+
logger.debug('Calling v2.network.product.publishTemplate', {
|
|
631
|
+
publishData
|
|
632
|
+
});
|
|
633
|
+
// Step 1: Create the template manifest
|
|
634
|
+
const publishResult = await context.hailer.request('v2.network.product.publishTemplate', [publishData]);
|
|
635
|
+
logger.debug('Publish template response', {
|
|
636
|
+
result: JSON.stringify(publishResult)
|
|
637
|
+
});
|
|
638
|
+
productId = publishResult?.productId || publishResult?._id || publishResult?.id;
|
|
639
|
+
if (!productId) {
|
|
640
|
+
return {
|
|
641
|
+
content: [{
|
|
642
|
+
type: "text",
|
|
643
|
+
text: `❌ **Error**: publishTemplate succeeded but no product ID returned.\n\n**Response:**\n\`\`\`json\n${JSON.stringify(publishResult, null, 2)}\n\`\`\``,
|
|
644
|
+
}],
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
// Step 2: Create or Update the product in marketplace
|
|
649
|
+
let productData;
|
|
650
|
+
let apiMethod;
|
|
651
|
+
let apiArgs;
|
|
652
|
+
if (isUpdate) {
|
|
653
|
+
// Update only allows specific fields
|
|
654
|
+
productData = {
|
|
655
|
+
name: args.title,
|
|
656
|
+
description: args.description,
|
|
657
|
+
publisher: args.publisher,
|
|
658
|
+
icon: args.iconFileId,
|
|
659
|
+
images: args.imageFileIds || [args.iconFileId]
|
|
660
|
+
};
|
|
661
|
+
if (args.externalUrl) {
|
|
662
|
+
productData.externalUrl = args.externalUrl;
|
|
663
|
+
}
|
|
664
|
+
apiMethod = 'v3.product.update';
|
|
665
|
+
apiArgs = [productId, productData];
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
// Create requires full data
|
|
669
|
+
productData = {
|
|
670
|
+
cid: workspaceId,
|
|
671
|
+
targetId: productId,
|
|
672
|
+
name: args.title,
|
|
673
|
+
description: args.description,
|
|
674
|
+
type: 'template',
|
|
675
|
+
publisher: args.publisher,
|
|
676
|
+
icon: args.iconFileId,
|
|
677
|
+
images: args.imageFileIds || [args.iconFileId]
|
|
678
|
+
};
|
|
679
|
+
if (args.externalUrl) {
|
|
680
|
+
productData.externalUrl = args.externalUrl;
|
|
681
|
+
}
|
|
682
|
+
apiMethod = 'v3.product.create';
|
|
683
|
+
apiArgs = [productData];
|
|
684
|
+
}
|
|
685
|
+
logger.debug(`Calling ${apiMethod}`, { productId, workspaceId, iconFileId: args.iconFileId });
|
|
686
|
+
const apiResult = await context.hailer.request(apiMethod, apiArgs);
|
|
687
|
+
logger.debug(`${apiMethod} response`, {
|
|
688
|
+
result: JSON.stringify(apiResult)
|
|
689
|
+
});
|
|
690
|
+
// Get the final product ID from the v3 API response (this is the correct marketplace ID)
|
|
691
|
+
const finalProductId = apiResult?._id || apiResult?.id || productId;
|
|
692
|
+
let responseText = `✅ **Template ${isUpdate ? 'Updated' : 'Published'} Successfully**\n\n`;
|
|
693
|
+
responseText += `**Title:** ${args.title}\n`;
|
|
694
|
+
responseText += `**Publisher:** ${args.publisher}\n`;
|
|
695
|
+
responseText += `**Version:** ${args.version}\n`;
|
|
696
|
+
responseText += `**Workspace:** ${workspaceId}\n`;
|
|
697
|
+
responseText += `**Product ID:** \`${finalProductId}\`\n`;
|
|
698
|
+
responseText += `**Release Notes:** ${args.versionDescription}\n`;
|
|
699
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
700
|
+
responseText += `- Use \`list_templates()\` to see it in the marketplace\n`;
|
|
701
|
+
responseText += `- Share the product ID with other workspaces\n`;
|
|
702
|
+
responseText += `- They can install with \`install_template({ templateId: "${finalProductId}" })\`\n`;
|
|
703
|
+
return {
|
|
704
|
+
content: [{
|
|
705
|
+
type: "text",
|
|
706
|
+
text: responseText,
|
|
707
|
+
}],
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
logger.error("Error publishing template", error);
|
|
712
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
713
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
714
|
+
return {
|
|
715
|
+
content: [{
|
|
716
|
+
type: "text",
|
|
717
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to publish templates. Only workspace administrators can publish.\n\n**Error:** ${errorMessage}`,
|
|
718
|
+
}],
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
// Always include debug info for troubleshooting
|
|
722
|
+
const debugWorkspaceId = context.workspaceCache?.currentWorkspace._id || 'unknown';
|
|
723
|
+
const debugInfo = {
|
|
724
|
+
endpoint: 'v2.network.product.publishTemplate',
|
|
725
|
+
requestData: {
|
|
726
|
+
workspaceId: debugWorkspaceId,
|
|
727
|
+
version: args.version,
|
|
728
|
+
versionDescription: args.versionDescription,
|
|
729
|
+
title: args.title,
|
|
730
|
+
description: args.description
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
return {
|
|
734
|
+
content: [{
|
|
735
|
+
type: "text",
|
|
736
|
+
text: `❌ **Error publishing template**\n\n**Error:** ${errorMessage}\n\n**Debug Info:**\n\`\`\`json\n${JSON.stringify(debugInfo, null, 2)}\n\`\`\`\n\n**Common Issues:**\n- Product may need to exist first (use create_template)\n- Version format may be incorrect\n- Workflow may not be attached to product`,
|
|
737
|
+
}],
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
// =============================================================================
|
|
743
|
+
// GET PRODUCT (v3) TOOL
|
|
744
|
+
// =============================================================================
|
|
745
|
+
const getProductDescription = `🏪 [PLAYGROUND] Get Product - Get detailed product info from marketplace (v3 API)
|
|
746
|
+
|
|
747
|
+
**What it does**:
|
|
748
|
+
Retrieves detailed information about a marketplace product using v3 API.
|
|
749
|
+
|
|
750
|
+
**Example**:
|
|
751
|
+
\`\`\`javascript
|
|
752
|
+
get_product({
|
|
753
|
+
productId: '692994df55996e1201963cd7'
|
|
754
|
+
})
|
|
755
|
+
\`\`\`
|
|
756
|
+
|
|
757
|
+
**Returns**: Product details including versions, workflows, and configuration`;
|
|
758
|
+
exports.getProductTool = {
|
|
759
|
+
name: 'get_product',
|
|
760
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
761
|
+
description: getProductDescription,
|
|
762
|
+
schema: zod_1.z.object({
|
|
763
|
+
productId: zod_1.z
|
|
764
|
+
.string()
|
|
765
|
+
.length(24)
|
|
766
|
+
.describe("Product ID to get details for (24 characters)"),
|
|
767
|
+
}),
|
|
768
|
+
async execute(args, context) {
|
|
769
|
+
logger.debug('Getting product details (v3)', {
|
|
770
|
+
productId: args.productId,
|
|
771
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
772
|
+
});
|
|
773
|
+
try {
|
|
774
|
+
logger.debug('Calling v3.app.product.get', {
|
|
775
|
+
productId: args.productId
|
|
776
|
+
});
|
|
777
|
+
const result = await context.hailer.request('v3.app.product.get', [args.productId]);
|
|
778
|
+
logger.debug('Product get response', {
|
|
779
|
+
result: JSON.stringify(result)
|
|
780
|
+
});
|
|
781
|
+
const product = result;
|
|
782
|
+
let responseText = `✅ **Product Details (v3)**\n\n`;
|
|
783
|
+
responseText += `**Product ID:** \`${args.productId}\`\n`;
|
|
784
|
+
if (product?.name) {
|
|
785
|
+
responseText += `**Name:** ${product.name}\n`;
|
|
786
|
+
}
|
|
787
|
+
if (product?.description) {
|
|
788
|
+
responseText += `**Description:** ${product.description}\n`;
|
|
789
|
+
}
|
|
790
|
+
if (product?.type) {
|
|
791
|
+
responseText += `**Type:** ${product.type}\n`;
|
|
792
|
+
}
|
|
793
|
+
if (product?.versions?.length) {
|
|
794
|
+
responseText += `**Versions:** ${product.versions.length}\n`;
|
|
795
|
+
}
|
|
796
|
+
return {
|
|
797
|
+
content: [{
|
|
798
|
+
type: "text",
|
|
799
|
+
text: responseText,
|
|
800
|
+
}],
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
catch (error) {
|
|
804
|
+
logger.error("Error getting product", error);
|
|
805
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
806
|
+
return {
|
|
807
|
+
content: [{
|
|
808
|
+
type: "text",
|
|
809
|
+
text: `❌ **Error getting product**\n\n**Error:** ${errorMessage}`,
|
|
810
|
+
}],
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
// =============================================================================
|
|
816
|
+
// GET PRODUCT MANIFEST (v3) TOOL
|
|
817
|
+
// =============================================================================
|
|
818
|
+
const getProductManifestDescription = `🏪 [PLAYGROUND] Get Product Manifest - Get manifest/version info from marketplace product
|
|
819
|
+
|
|
820
|
+
**What it does**:
|
|
821
|
+
Retrieves the manifest for a marketplace product, which may include version and workflow information.
|
|
822
|
+
|
|
823
|
+
**Example**:
|
|
824
|
+
\`\`\`javascript
|
|
825
|
+
get_product_manifest({
|
|
826
|
+
productId: '692994df55996e1201963cd7'
|
|
827
|
+
})
|
|
828
|
+
\`\`\`
|
|
829
|
+
|
|
830
|
+
**Returns**: Product manifest with version details`;
|
|
831
|
+
exports.getProductManifestTool = {
|
|
832
|
+
name: 'get_product_manifest',
|
|
833
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
834
|
+
description: getProductManifestDescription,
|
|
835
|
+
schema: zod_1.z.object({
|
|
836
|
+
productId: zod_1.z
|
|
837
|
+
.string()
|
|
838
|
+
.length(24)
|
|
839
|
+
.describe("Product ID to get manifest for (24 characters)"),
|
|
840
|
+
}),
|
|
841
|
+
async execute(args, context) {
|
|
842
|
+
logger.debug('Getting product manifest (v3)', {
|
|
843
|
+
productId: args.productId,
|
|
844
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
845
|
+
});
|
|
846
|
+
try {
|
|
847
|
+
logger.debug('Calling v3.app.product.getManifest', {
|
|
848
|
+
productId: args.productId
|
|
849
|
+
});
|
|
850
|
+
const result = await context.hailer.request('v3.app.product.getManifest', [args.productId]);
|
|
851
|
+
logger.debug('Product manifest response', {
|
|
852
|
+
result: JSON.stringify(result)
|
|
853
|
+
});
|
|
854
|
+
const manifest = result;
|
|
855
|
+
let responseText = `✅ **Product Manifest (v3)**\n\n`;
|
|
856
|
+
responseText += `**Product ID:** \`${args.productId}\`\n`;
|
|
857
|
+
if (manifest?.version) {
|
|
858
|
+
responseText += `**Version:** ${manifest.version}\n`;
|
|
859
|
+
}
|
|
860
|
+
if (manifest?.workflows?.length) {
|
|
861
|
+
responseText += `**Workflows:** ${manifest.workflows.length}\n`;
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
content: [{
|
|
865
|
+
type: "text",
|
|
866
|
+
text: responseText,
|
|
867
|
+
}],
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
catch (error) {
|
|
871
|
+
logger.error("Error getting product manifest", error);
|
|
872
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
873
|
+
return {
|
|
874
|
+
content: [{
|
|
875
|
+
type: "text",
|
|
876
|
+
text: `❌ **Error getting product manifest**\n\n**Error:** ${errorMessage}`,
|
|
877
|
+
}],
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
// =============================================================================
|
|
883
|
+
// PUBLISH APP TO MARKETPLACE TOOL
|
|
884
|
+
// =============================================================================
|
|
885
|
+
const publishAppDescription = `🏪 [PLAYGROUND] Publish App - Publish app to Hailer marketplace
|
|
886
|
+
|
|
887
|
+
**What it does**:
|
|
888
|
+
Publishes an existing app to the Hailer marketplace, making it installable by other workspaces.
|
|
889
|
+
|
|
890
|
+
**IMPORTANT**: This is different from \`publish_hailer_app\` which deploys code to Hailer hosting.
|
|
891
|
+
This tool creates a **marketplace listing** so other workspaces can discover and install your app.
|
|
892
|
+
|
|
893
|
+
**Example - New Marketplace Listing**:
|
|
894
|
+
\`\`\`javascript
|
|
895
|
+
publish_app({
|
|
896
|
+
appId: '692d3e66e98c7bab4af7f0a1',
|
|
897
|
+
versionId: '692d3e66e98c7bab4af7f0a3', // From publish_hailer_app response
|
|
898
|
+
title: 'Task Manager Pro',
|
|
899
|
+
description: 'Advanced task management with Kanban boards',
|
|
900
|
+
version: '1.0.0',
|
|
901
|
+
versionDescription: 'Initial release with drag-drop support',
|
|
902
|
+
publisher: 'Hailer Oy',
|
|
903
|
+
iconFileId: '692d3e66e98c7bab4af7f0a2'
|
|
904
|
+
})
|
|
905
|
+
\`\`\`
|
|
906
|
+
|
|
907
|
+
**Example - Update Existing Listing**:
|
|
908
|
+
\`\`\`javascript
|
|
909
|
+
publish_app({
|
|
910
|
+
appId: '692d3e66e98c7bab4af7f0a1',
|
|
911
|
+
versionId: '692d3e66e98c7bab4af7f0a3',
|
|
912
|
+
productId: '692d46bfca77093f10b0c09a', // Existing product ID
|
|
913
|
+
title: 'Task Manager Pro',
|
|
914
|
+
description: 'Updated description',
|
|
915
|
+
version: '1.1.0',
|
|
916
|
+
versionDescription: 'Added dark mode support',
|
|
917
|
+
publisher: 'Hailer Oy',
|
|
918
|
+
iconFileId: '692d47adf9383bd1a9e26d78'
|
|
919
|
+
})
|
|
920
|
+
\`\`\`
|
|
921
|
+
|
|
922
|
+
**Required Parameters** (gather ALL before calling):
|
|
923
|
+
- \`appId\` - App ID to publish (must be a published app, not dev)
|
|
924
|
+
- \`versionId\` - Version ID from \`publish_hailer_app\` response (the targetId for marketplace)
|
|
925
|
+
- \`title\` - Marketplace listing name (max 64 chars)
|
|
926
|
+
- \`description\` - Full description (max 4096 chars)
|
|
927
|
+
- \`version\` - Version string (e.g. "1.0.0")
|
|
928
|
+
- \`versionDescription\` - Release notes for this version
|
|
929
|
+
- \`publisher\` - Publishing company/person name
|
|
930
|
+
- \`iconFileId\` - Icon file ID (upload with \`upload_files\` first)
|
|
931
|
+
|
|
932
|
+
**Optional Parameters**:
|
|
933
|
+
- \`productId\` - Existing product ID to UPDATE (omit to create new)
|
|
934
|
+
- \`imageFileIds\` - Array of preview image file IDs
|
|
935
|
+
- \`externalUrl\` - External documentation URL
|
|
936
|
+
|
|
937
|
+
**Workflow**:
|
|
938
|
+
1. Create and publish app with \`publish_hailer_app\` (deploys code, returns versionId)
|
|
939
|
+
2. Upload icon with \`upload_files\`
|
|
940
|
+
3. Call \`publish_app\` with versionId to create marketplace listing
|
|
941
|
+
4. Other workspaces can install with \`install_marketplace_app\`
|
|
942
|
+
|
|
943
|
+
**After Publishing**:
|
|
944
|
+
- App appears in marketplace
|
|
945
|
+
- Share the product ID with other workspaces
|
|
946
|
+
- They install with \`install_marketplace_app({ productId: "..." })\``;
|
|
947
|
+
exports.publishAppTool = {
|
|
948
|
+
name: 'publish_app',
|
|
949
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
950
|
+
description: publishAppDescription,
|
|
951
|
+
schema: zod_1.z.object({
|
|
952
|
+
appId: zod_1.z
|
|
953
|
+
.string()
|
|
954
|
+
.length(24)
|
|
955
|
+
.describe("App ID to publish (24 characters)"),
|
|
956
|
+
versionId: zod_1.z
|
|
957
|
+
.string()
|
|
958
|
+
.length(24)
|
|
959
|
+
.optional()
|
|
960
|
+
.describe("Optional version ID. If not provided, uses appId as targetId."),
|
|
961
|
+
productId: zod_1.z
|
|
962
|
+
.string()
|
|
963
|
+
.length(24)
|
|
964
|
+
.optional()
|
|
965
|
+
.describe("Existing product ID to UPDATE (omit to create new listing)"),
|
|
966
|
+
title: zod_1.z
|
|
967
|
+
.string()
|
|
968
|
+
.min(1)
|
|
969
|
+
.max(64)
|
|
970
|
+
.describe("Marketplace listing name (max 64 chars)"),
|
|
971
|
+
description: zod_1.z
|
|
972
|
+
.string()
|
|
973
|
+
.min(1)
|
|
974
|
+
.max(4096)
|
|
975
|
+
.describe("Full description (max 4096 chars)"),
|
|
976
|
+
version: zod_1.z
|
|
977
|
+
.string()
|
|
978
|
+
.min(1)
|
|
979
|
+
.describe("Version string (e.g. '1.0.0')"),
|
|
980
|
+
versionDescription: zod_1.z
|
|
981
|
+
.string()
|
|
982
|
+
.min(1)
|
|
983
|
+
.describe("Release notes for this version"),
|
|
984
|
+
publisher: zod_1.z
|
|
985
|
+
.string()
|
|
986
|
+
.min(1)
|
|
987
|
+
.describe("Publishing company or person name"),
|
|
988
|
+
iconFileId: zod_1.z
|
|
989
|
+
.string()
|
|
990
|
+
.length(24)
|
|
991
|
+
.describe("Icon file ID (24 chars). Upload with upload_files first"),
|
|
992
|
+
imageFileIds: zod_1.z
|
|
993
|
+
.array(zod_1.z.string().length(24))
|
|
994
|
+
.optional()
|
|
995
|
+
.describe("Optional array of preview image file IDs"),
|
|
996
|
+
externalUrl: zod_1.z
|
|
997
|
+
.string()
|
|
998
|
+
.url()
|
|
999
|
+
.optional()
|
|
1000
|
+
.describe("Optional external documentation URL"),
|
|
1001
|
+
}),
|
|
1002
|
+
async execute(args, context) {
|
|
1003
|
+
const isUpdate = !!args.productId;
|
|
1004
|
+
logger.debug(`${isUpdate ? 'Updating' : 'Publishing'} app to marketplace`, {
|
|
1005
|
+
appId: args.appId,
|
|
1006
|
+
title: args.title,
|
|
1007
|
+
version: args.version,
|
|
1008
|
+
productId: args.productId,
|
|
1009
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
1010
|
+
});
|
|
1011
|
+
try {
|
|
1012
|
+
const workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)({}, context);
|
|
1013
|
+
if (!workspaceId) {
|
|
1014
|
+
return (0, tool_helpers_1.missingWorkspaceCacheResponse)();
|
|
1015
|
+
}
|
|
1016
|
+
// Verify app exists and get details
|
|
1017
|
+
logger.debug('Fetching app details', { appId: args.appId });
|
|
1018
|
+
const appResult = await context.hailer.request('v3.app.list', [{}]);
|
|
1019
|
+
const apps = appResult?.apps || appResult?.details?.apps || (Array.isArray(appResult) ? appResult : []);
|
|
1020
|
+
const app = Array.isArray(apps) ? apps.find((a) => a._id === args.appId) : null;
|
|
1021
|
+
if (!app) {
|
|
1022
|
+
return {
|
|
1023
|
+
content: [{
|
|
1024
|
+
type: "text",
|
|
1025
|
+
text: `❌ **App Not Found**\n\nApp ID \`${args.appId}\` was not found in the current workspace.\n\n**Tips:**\n- Use \`list_apps()\` to see available apps\n- Ensure the app exists and you have access`,
|
|
1026
|
+
}],
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
// Warn if app is a dev app (has localhost URL)
|
|
1030
|
+
if (app.url && app.url.includes('localhost')) {
|
|
1031
|
+
return {
|
|
1032
|
+
content: [{
|
|
1033
|
+
type: "text",
|
|
1034
|
+
text: `⚠️ **Cannot Publish Development App**\n\nApp "${app.name}" is a development app (URL: ${app.url}).\n\n**To publish to marketplace:**\n1. First publish the app code with \`publish_hailer_app\`\n2. Or create a production app with empty URL\n3. Then call \`publish_app\` again`,
|
|
1035
|
+
}],
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
let productId = args.productId;
|
|
1039
|
+
let targetId = args.versionId;
|
|
1040
|
+
// Step 1: If no versionId provided and not updating, create app version manifest
|
|
1041
|
+
if (!targetId && !productId) {
|
|
1042
|
+
const publishData = {
|
|
1043
|
+
appId: args.appId,
|
|
1044
|
+
workspaceId,
|
|
1045
|
+
version: args.version,
|
|
1046
|
+
versionDescription: args.versionDescription,
|
|
1047
|
+
title: args.title,
|
|
1048
|
+
description: args.description
|
|
1049
|
+
};
|
|
1050
|
+
logger.debug('Calling v2.network.product.publishAppVersion', { publishData });
|
|
1051
|
+
try {
|
|
1052
|
+
const publishResult = await context.hailer.request('v2.network.product.publishAppVersion', [publishData]);
|
|
1053
|
+
logger.debug('publishAppVersion response', { result: JSON.stringify(publishResult) });
|
|
1054
|
+
targetId = publishResult?.productId || publishResult?._id || publishResult?.id;
|
|
1055
|
+
}
|
|
1056
|
+
catch (err) {
|
|
1057
|
+
logger.debug('v2.network.product.publishAppVersion failed', {
|
|
1058
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1059
|
+
});
|
|
1060
|
+
// If publishAppVersion doesn't exist, the error will be caught below
|
|
1061
|
+
}
|
|
1062
|
+
if (!targetId) {
|
|
1063
|
+
return {
|
|
1064
|
+
content: [{
|
|
1065
|
+
type: "text",
|
|
1066
|
+
text: `❌ **Error**: Could not create app version for marketplace.\n\n**Note:** The app marketplace publishing API may not be available. Make sure the app has been published with \`publish_hailer_app\` first.\n\nYou can share the app directly with \`add_app_member\` instead.`,
|
|
1067
|
+
}],
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
// Step 2: Create or Update the product in marketplace
|
|
1072
|
+
let productData;
|
|
1073
|
+
let apiMethod;
|
|
1074
|
+
let apiArgs;
|
|
1075
|
+
if (isUpdate && productId) {
|
|
1076
|
+
// Update only allows specific fields
|
|
1077
|
+
productData = {
|
|
1078
|
+
name: args.title,
|
|
1079
|
+
description: args.description,
|
|
1080
|
+
publisher: args.publisher,
|
|
1081
|
+
icon: args.iconFileId,
|
|
1082
|
+
images: args.imageFileIds || [args.iconFileId]
|
|
1083
|
+
};
|
|
1084
|
+
if (args.externalUrl) {
|
|
1085
|
+
productData.externalUrl = args.externalUrl;
|
|
1086
|
+
}
|
|
1087
|
+
apiMethod = 'v3.product.update';
|
|
1088
|
+
apiArgs = [productId, productData];
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
// Create requires full data - targetId comes from versionId param or publishAppVersion call
|
|
1092
|
+
productData = {
|
|
1093
|
+
cid: workspaceId,
|
|
1094
|
+
targetId, // Version ID from publishAppVersion or provided versionId
|
|
1095
|
+
name: args.title,
|
|
1096
|
+
description: args.description,
|
|
1097
|
+
type: 'app',
|
|
1098
|
+
publisher: args.publisher,
|
|
1099
|
+
icon: args.iconFileId,
|
|
1100
|
+
images: args.imageFileIds || [args.iconFileId]
|
|
1101
|
+
};
|
|
1102
|
+
if (args.externalUrl) {
|
|
1103
|
+
productData.externalUrl = args.externalUrl;
|
|
1104
|
+
}
|
|
1105
|
+
apiMethod = 'v3.product.create';
|
|
1106
|
+
apiArgs = [productData];
|
|
1107
|
+
}
|
|
1108
|
+
logger.debug(`Calling ${apiMethod}`, { productId, workspaceId, appId: args.appId, targetId, iconFileId: args.iconFileId });
|
|
1109
|
+
const apiResult = await context.hailer.request(apiMethod, apiArgs);
|
|
1110
|
+
logger.debug(`${apiMethod} response`, {
|
|
1111
|
+
result: JSON.stringify(apiResult)
|
|
1112
|
+
});
|
|
1113
|
+
// Get the final product ID from the v3 API response
|
|
1114
|
+
const finalProductId = apiResult?._id || apiResult?.id || productId;
|
|
1115
|
+
let responseText = `✅ **App ${isUpdate ? 'Updated' : 'Published'} to Marketplace**\n\n`;
|
|
1116
|
+
responseText += `**App:** ${app.name} (\`${args.appId}\`)\n`;
|
|
1117
|
+
responseText += `**Title:** ${args.title}\n`;
|
|
1118
|
+
responseText += `**Publisher:** ${args.publisher}\n`;
|
|
1119
|
+
responseText += `**Version:** ${args.version}\n`;
|
|
1120
|
+
responseText += `**Product ID:** \`${finalProductId}\`\n`;
|
|
1121
|
+
responseText += `**Release Notes:** ${args.versionDescription}\n`;
|
|
1122
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
1123
|
+
responseText += `- Share the product ID with other workspaces\n`;
|
|
1124
|
+
responseText += `- They can install with \`install_marketplace_app({ productId: "${finalProductId}" })\`\n`;
|
|
1125
|
+
responseText += `- To update, include \`productId: "${finalProductId}"\` in next publish\n`;
|
|
1126
|
+
return {
|
|
1127
|
+
content: [{
|
|
1128
|
+
type: "text",
|
|
1129
|
+
text: responseText,
|
|
1130
|
+
}],
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
catch (error) {
|
|
1134
|
+
logger.error("Error publishing app to marketplace", error);
|
|
1135
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
1136
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
1137
|
+
return {
|
|
1138
|
+
content: [{
|
|
1139
|
+
type: "text",
|
|
1140
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to publish apps to marketplace. Only workspace administrators can publish.\n\n**Error:** ${errorMessage}`,
|
|
1141
|
+
}],
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
return {
|
|
1145
|
+
content: [{
|
|
1146
|
+
type: "text",
|
|
1147
|
+
text: `❌ **Error publishing app to marketplace**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- App must be a production app (not dev/localhost)\n- Must be workspace administrator\n- Icon file must be uploaded first\n- Invalid version format`,
|
|
1148
|
+
}],
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
// =============================================================================
|
|
1154
|
+
// INSTALL MARKETPLACE APP TOOL
|
|
1155
|
+
// =============================================================================
|
|
1156
|
+
const installMarketplaceAppDescription = `🏪 [PLAYGROUND] Install Marketplace App - Install app from Hailer marketplace
|
|
1157
|
+
|
|
1158
|
+
**What it does**:
|
|
1159
|
+
Installs an app from the Hailer marketplace into your workspace.
|
|
1160
|
+
|
|
1161
|
+
**Example**:
|
|
1162
|
+
\`\`\`javascript
|
|
1163
|
+
install_marketplace_app({
|
|
1164
|
+
productId: '692d46bfca77093f10b0c09a'
|
|
1165
|
+
})
|
|
1166
|
+
\`\`\`
|
|
1167
|
+
|
|
1168
|
+
**Parameters**:
|
|
1169
|
+
- \`productId\` (required) - Marketplace product ID
|
|
1170
|
+
|
|
1171
|
+
**Requirements**:
|
|
1172
|
+
- User must be workspace administrator
|
|
1173
|
+
|
|
1174
|
+
**Tips**:
|
|
1175
|
+
- Get product ID from app publisher or marketplace
|
|
1176
|
+
- Use \`get_product\` to preview app details before installing
|
|
1177
|
+
- Installed app appears in workspace app list`;
|
|
1178
|
+
exports.installMarketplaceAppTool = {
|
|
1179
|
+
name: 'install_marketplace_app',
|
|
1180
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
1181
|
+
description: installMarketplaceAppDescription,
|
|
1182
|
+
schema: zod_1.z.object({
|
|
1183
|
+
productId: zod_1.z
|
|
1184
|
+
.string()
|
|
1185
|
+
.length(24)
|
|
1186
|
+
.describe("Marketplace product ID to install (24 characters)"),
|
|
1187
|
+
workspaceId: zod_1.z
|
|
1188
|
+
.string()
|
|
1189
|
+
.optional()
|
|
1190
|
+
.describe("Target workspace ID (defaults to current)"),
|
|
1191
|
+
}),
|
|
1192
|
+
async execute(args, context) {
|
|
1193
|
+
logger.debug('Installing marketplace app', {
|
|
1194
|
+
productId: args.productId,
|
|
1195
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
1196
|
+
});
|
|
1197
|
+
try {
|
|
1198
|
+
const workspaceId = (0, tool_helpers_1.getResolvedWorkspaceId)(args, context);
|
|
1199
|
+
if (!workspaceId) {
|
|
1200
|
+
return (0, tool_helpers_1.missingWorkspaceCacheResponse)();
|
|
1201
|
+
}
|
|
1202
|
+
// Check if already installed
|
|
1203
|
+
logger.debug('Checking if product is already installed', { productId: args.productId, workspaceId });
|
|
1204
|
+
try {
|
|
1205
|
+
const isInstalledResult = await context.hailer.request('v3.app.product.isProductInstalled', [args.productId, workspaceId]);
|
|
1206
|
+
if (isInstalledResult?.installed) {
|
|
1207
|
+
return {
|
|
1208
|
+
content: [{
|
|
1209
|
+
type: "text",
|
|
1210
|
+
text: `ℹ️ **App Already Installed**\n\nThis app is already installed in your workspace.\n\n**App ID:** \`${isInstalledResult.appId || 'unknown'}\`\n\n**Tips:**\n- Use \`list_apps()\` to see installed apps\n- Use \`get_product({ productId: "${args.productId}" })\` to view product details`,
|
|
1211
|
+
}],
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
catch (checkError) {
|
|
1216
|
+
// Ignore check errors and proceed with installation
|
|
1217
|
+
logger.debug('Could not check installation status, proceeding', {
|
|
1218
|
+
error: checkError instanceof Error ? checkError.message : String(checkError)
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
// Get product details first
|
|
1222
|
+
logger.debug('Fetching product details', { productId: args.productId });
|
|
1223
|
+
let productName = 'Unknown';
|
|
1224
|
+
try {
|
|
1225
|
+
const productResult = await context.hailer.request('v3.app.product.get', [args.productId]);
|
|
1226
|
+
productName = productResult?.name || productResult?.title || 'Unknown';
|
|
1227
|
+
}
|
|
1228
|
+
catch (productError) {
|
|
1229
|
+
logger.debug('Could not fetch product details', {
|
|
1230
|
+
error: productError instanceof Error ? productError.message : String(productError)
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
// Install the app
|
|
1234
|
+
logger.debug('Calling v3.app.product.install', { productId: args.productId, workspaceId });
|
|
1235
|
+
const installResult = await context.hailer.request('v3.app.product.install', [args.productId, workspaceId]);
|
|
1236
|
+
logger.debug('Install result', { result: JSON.stringify(installResult) });
|
|
1237
|
+
const installedAppId = installResult?.appId || installResult?.details?.appId || installResult?._id;
|
|
1238
|
+
let responseText = `✅ **App Installed Successfully**\n\n`;
|
|
1239
|
+
responseText += `**Product:** ${productName}\n`;
|
|
1240
|
+
responseText += `**Product ID:** \`${args.productId}\`\n`;
|
|
1241
|
+
if (installedAppId) {
|
|
1242
|
+
responseText += `**Installed App ID:** \`${installedAppId}\`\n`;
|
|
1243
|
+
}
|
|
1244
|
+
responseText += `**Workspace:** \`${workspaceId}\`\n`;
|
|
1245
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
1246
|
+
responseText += `- Use \`list_apps()\` to see the installed app\n`;
|
|
1247
|
+
responseText += `- Use \`add_app_member\` to share with workspace members\n`;
|
|
1248
|
+
responseText += `- Open the app from Hailer workspace menu\n`;
|
|
1249
|
+
return {
|
|
1250
|
+
content: [{
|
|
1251
|
+
type: "text",
|
|
1252
|
+
text: responseText,
|
|
1253
|
+
}],
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
catch (error) {
|
|
1257
|
+
logger.error("Error installing marketplace app", error);
|
|
1258
|
+
const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
|
|
1259
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
1260
|
+
return {
|
|
1261
|
+
content: [{
|
|
1262
|
+
type: "text",
|
|
1263
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to install apps. Only workspace administrators can install marketplace apps.\n\n**Error:** ${errorMessage}`,
|
|
1264
|
+
}],
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
1268
|
+
return {
|
|
1269
|
+
content: [{
|
|
1270
|
+
type: "text",
|
|
1271
|
+
text: `❌ **Product Not Found**\n\nThe marketplace product \`${args.productId}\` was not found.\n\n**Tips:**\n- Verify the product ID is correct\n- Use \`get_product\` to check if the product exists\n\n**Error:** ${errorMessage}`,
|
|
1272
|
+
}],
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
content: [{
|
|
1277
|
+
type: "text",
|
|
1278
|
+
text: `❌ **Error installing marketplace app**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Invalid product ID\n- Product not found in marketplace\n- Insufficient permissions`,
|
|
1279
|
+
}],
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
//# sourceMappingURL=app-marketplace.js.map
|