@hailer/mcp 0.0.1
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/commands/tool-builder.md +37 -0
- package/.claude/commands/ws-pull.md +44 -0
- package/.claude/settings.json +8 -0
- package/.claude/settings.local.json +49 -0
- package/.claude/skills/activity-api/SKILL.md +96 -0
- package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
- package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
- package/.claude/skills/agent-building/SKILL.md +243 -0
- package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
- package/.claude/skills/agent-building/references/code-examples.md +587 -0
- package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
- package/.claude/skills/app-api/SKILL.md +219 -0
- package/.claude/skills/app-api/references/app-endpoints.md +759 -0
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
- package/.claude/skills/create-app-skill/SKILL.md +1101 -0
- package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
- package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
- package/.claude/skills/hailer-api/SKILL.md +283 -0
- package/.claude/skills/hailer-api/references/activities.md +620 -0
- package/.claude/skills/hailer-api/references/authentication.md +216 -0
- package/.claude/skills/hailer-api/references/datasets.md +437 -0
- package/.claude/skills/hailer-api/references/files.md +301 -0
- package/.claude/skills/hailer-api/references/insights.md +469 -0
- package/.claude/skills/hailer-api/references/workflows.md +720 -0
- package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
- package/.claude/skills/insight-api/SKILL.md +185 -0
- package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
- package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
- package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
- package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
- package/.claude/skills/local-first-skill/SKILL.md +570 -0
- package/.claude/skills/mcp-tools/SKILL.md +419 -0
- package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
- package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
- package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
- package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
- package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
- package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
- package/.claude/skills/remove-app-skill/SKILL.md +985 -0
- package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
- package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
- package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
- package/.claude/skills/skill-testing/README.md +137 -0
- package/.claude/skills/skill-testing/SKILL.md +348 -0
- package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
- package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
- package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
- package/.claude/skills/tool-builder/SKILL.md +328 -0
- package/.claude/skills/update-app-skill/SKILL.md +970 -0
- package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
- package/.env.example +81 -0
- package/.mcp.json +13 -0
- package/README.md +297 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +5 -0
- package/dist/client/adaptive-documentation-bot.d.ts +108 -0
- package/dist/client/adaptive-documentation-bot.js +475 -0
- package/dist/client/adaptive-documentation-types.d.ts +66 -0
- package/dist/client/adaptive-documentation-types.js +9 -0
- package/dist/client/agent-activity-bot.d.ts +51 -0
- package/dist/client/agent-activity-bot.js +166 -0
- package/dist/client/agent-tracker.d.ts +499 -0
- package/dist/client/agent-tracker.js +659 -0
- package/dist/client/description-updater.d.ts +56 -0
- package/dist/client/description-updater.js +259 -0
- package/dist/client/log-parser.d.ts +72 -0
- package/dist/client/log-parser.js +387 -0
- package/dist/client/mcp-client.d.ts +50 -0
- package/dist/client/mcp-client.js +532 -0
- package/dist/client/message-processor.d.ts +35 -0
- package/dist/client/message-processor.js +352 -0
- package/dist/client/multi-bot-manager.d.ts +24 -0
- package/dist/client/multi-bot-manager.js +74 -0
- package/dist/client/providers/anthropic-provider.d.ts +19 -0
- package/dist/client/providers/anthropic-provider.js +631 -0
- package/dist/client/providers/llm-provider.d.ts +47 -0
- package/dist/client/providers/llm-provider.js +367 -0
- package/dist/client/providers/openai-provider.d.ts +23 -0
- package/dist/client/providers/openai-provider.js +621 -0
- package/dist/client/simple-llm-caller.d.ts +19 -0
- package/dist/client/simple-llm-caller.js +100 -0
- package/dist/client/skill-generator.d.ts +81 -0
- package/dist/client/skill-generator.js +386 -0
- package/dist/client/test-adaptive-bot.d.ts +9 -0
- package/dist/client/test-adaptive-bot.js +82 -0
- package/dist/client/token-pricing.d.ts +38 -0
- package/dist/client/token-pricing.js +127 -0
- package/dist/client/token-tracker.d.ts +232 -0
- package/dist/client/token-tracker.js +457 -0
- package/dist/client/token-usage-bot.d.ts +53 -0
- package/dist/client/token-usage-bot.js +153 -0
- package/dist/client/tool-executor.d.ts +69 -0
- package/dist/client/tool-executor.js +159 -0
- package/dist/client/tool-schema-loader.d.ts +60 -0
- package/dist/client/tool-schema-loader.js +178 -0
- package/dist/client/types.d.ts +69 -0
- package/dist/client/types.js +7 -0
- package/dist/config.d.ts +162 -0
- package/dist/config.js +296 -0
- package/dist/core.d.ts +26 -0
- package/dist/core.js +147 -0
- package/dist/lib/context-manager.d.ts +111 -0
- package/dist/lib/context-manager.js +431 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +277 -0
- package/dist/lib/materialize.d.ts +3 -0
- package/dist/lib/materialize.js +101 -0
- package/dist/lib/normalizedName.d.ts +7 -0
- package/dist/lib/normalizedName.js +48 -0
- package/dist/lib/prompt-length-manager.d.ts +81 -0
- package/dist/lib/prompt-length-manager.js +457 -0
- package/dist/lib/terminal-prompt.d.ts +9 -0
- package/dist/lib/terminal-prompt.js +108 -0
- package/dist/mcp/UserContextCache.d.ts +56 -0
- package/dist/mcp/UserContextCache.js +163 -0
- package/dist/mcp/auth.d.ts +2 -0
- package/dist/mcp/auth.js +29 -0
- package/dist/mcp/hailer-clients.d.ts +42 -0
- package/dist/mcp/hailer-clients.js +246 -0
- package/dist/mcp/signal-handler.d.ts +45 -0
- package/dist/mcp/signal-handler.js +317 -0
- package/dist/mcp/tool-registry.d.ts +100 -0
- package/dist/mcp/tool-registry.js +306 -0
- package/dist/mcp/tools/activity.d.ts +15 -0
- package/dist/mcp/tools/activity.js +955 -0
- package/dist/mcp/tools/app.d.ts +20 -0
- package/dist/mcp/tools/app.js +1488 -0
- package/dist/mcp/tools/discussion.d.ts +19 -0
- package/dist/mcp/tools/discussion.js +950 -0
- package/dist/mcp/tools/file.d.ts +15 -0
- package/dist/mcp/tools/file.js +119 -0
- package/dist/mcp/tools/insight.d.ts +17 -0
- package/dist/mcp/tools/insight.js +806 -0
- package/dist/mcp/tools/skill.d.ts +10 -0
- package/dist/mcp/tools/skill.js +279 -0
- package/dist/mcp/tools/user.d.ts +10 -0
- package/dist/mcp/tools/user.js +108 -0
- package/dist/mcp/tools/workflow-template.d.ts +19 -0
- package/dist/mcp/tools/workflow-template.js +822 -0
- package/dist/mcp/tools/workflow.d.ts +18 -0
- package/dist/mcp/tools/workflow.js +1362 -0
- package/dist/mcp/utils/api-errors.d.ts +45 -0
- package/dist/mcp/utils/api-errors.js +160 -0
- package/dist/mcp/utils/data-transformers.d.ts +102 -0
- package/dist/mcp/utils/data-transformers.js +194 -0
- package/dist/mcp/utils/file-upload.d.ts +33 -0
- package/dist/mcp/utils/file-upload.js +148 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
- package/dist/mcp/utils/hailer-api-client.js +323 -0
- package/dist/mcp/utils/index.d.ts +13 -0
- package/dist/mcp/utils/index.js +39 -0
- package/dist/mcp/utils/logger.d.ts +42 -0
- package/dist/mcp/utils/logger.js +103 -0
- package/dist/mcp/utils/types.d.ts +286 -0
- package/dist/mcp/utils/types.js +7 -0
- package/dist/mcp/workspace-cache.d.ts +42 -0
- package/dist/mcp/workspace-cache.js +97 -0
- package/dist/mcp-server.d.ts +42 -0
- package/dist/mcp-server.js +280 -0
- package/package.json +56 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,1488 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* App Tools - Clean Architecture Implementation
|
|
4
|
+
*
|
|
5
|
+
* Hailer app management tools:
|
|
6
|
+
* - Apps extend workspace functionality
|
|
7
|
+
* - Development apps (localhost URLs)
|
|
8
|
+
* - Published apps (auto-generated URLs)
|
|
9
|
+
*
|
|
10
|
+
* All 8 app management tools in one file for cohesion.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.publishHailerAppTool = exports.scaffoldHailerAppTool = exports.removeAppMemberTool = exports.addAppMemberTool = exports.removeAppTool = exports.updateAppTool = exports.listAppsTool = exports.createAppTool = void 0;
|
|
47
|
+
const zod_1 = require("zod");
|
|
48
|
+
const tool_registry_1 = require("../tool-registry");
|
|
49
|
+
const workspace_cache_1 = require("../workspace-cache");
|
|
50
|
+
const logger_1 = require("../../lib/logger");
|
|
51
|
+
const config_1 = require("../../config");
|
|
52
|
+
const logger = (0, logger_1.createLogger)({ component: 'app-tools' });
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// CREATE APP TOOL
|
|
55
|
+
// ============================================================================
|
|
56
|
+
const createAppDescription = `🧪 [PLAYGROUND] Create App - Create Hailer app entry in workspace
|
|
57
|
+
|
|
58
|
+
**What are Apps?**
|
|
59
|
+
Apps are custom web applications that extend Hailer workspace functionality. Think React/vanilla JS apps running within Hailer.
|
|
60
|
+
|
|
61
|
+
**App Types**:
|
|
62
|
+
- **Development Apps**: URL points to localhost (e.g., http://localhost:3000)
|
|
63
|
+
- **Published Apps**: URL empty or points to production
|
|
64
|
+
|
|
65
|
+
**Example 1 - Development App**:
|
|
66
|
+
\`\`\`javascript
|
|
67
|
+
create_app({
|
|
68
|
+
name: 'Local Development',
|
|
69
|
+
description: 'App pointing to local server',
|
|
70
|
+
url: 'http://localhost:3000'
|
|
71
|
+
})
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
**Example 2 - Published App**:
|
|
75
|
+
\`\`\`javascript
|
|
76
|
+
create_app({
|
|
77
|
+
name: 'Task Manager',
|
|
78
|
+
description: 'Production task management app',
|
|
79
|
+
url: '' // Empty for auto URL
|
|
80
|
+
})
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
**Properties**:
|
|
84
|
+
- \`name\` (required) - App display name
|
|
85
|
+
- \`description\` (optional) - App description
|
|
86
|
+
- \`url\` (optional) - App URL or empty for auto-generated
|
|
87
|
+
- \`image\` (optional) - Image/icon ID
|
|
88
|
+
|
|
89
|
+
**Requirements**:
|
|
90
|
+
- User must be workspace administrator
|
|
91
|
+
- For published apps, leave URL empty
|
|
92
|
+
|
|
93
|
+
**Tips**:
|
|
94
|
+
- Use development apps for local testing
|
|
95
|
+
- Published apps get automatic URLs from Hailer
|
|
96
|
+
- Use \`add_app_member\` to share with others`;
|
|
97
|
+
exports.createAppTool = {
|
|
98
|
+
name: 'create_app',
|
|
99
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
100
|
+
description: createAppDescription,
|
|
101
|
+
schema: zod_1.z.object({
|
|
102
|
+
workspaceId: zod_1.z
|
|
103
|
+
.string()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe("Optional workspace ID - defaults to current workspace"),
|
|
106
|
+
name: zod_1.z
|
|
107
|
+
.string()
|
|
108
|
+
.min(1)
|
|
109
|
+
.describe("App name (required)"),
|
|
110
|
+
description: zod_1.z
|
|
111
|
+
.string()
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("App description"),
|
|
114
|
+
url: zod_1.z
|
|
115
|
+
.string()
|
|
116
|
+
.optional()
|
|
117
|
+
.describe("App URL (empty for published apps, localhost for dev)"),
|
|
118
|
+
image: zod_1.z
|
|
119
|
+
.string()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("Image/icon ID (24 characters)"),
|
|
122
|
+
config: zod_1.z
|
|
123
|
+
.record(zod_1.z.unknown())
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Optional app configuration"),
|
|
126
|
+
}),
|
|
127
|
+
async execute(args, context) {
|
|
128
|
+
logger.debug('Creating app', {
|
|
129
|
+
name: args.name,
|
|
130
|
+
workspaceId: args.workspaceId,
|
|
131
|
+
hasUrl: !!args.url,
|
|
132
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
if (!context.workspaceCache) {
|
|
136
|
+
return {
|
|
137
|
+
content: [{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: "❌ Workspace cache not available",
|
|
140
|
+
}],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const workspaceId = args.workspaceId
|
|
144
|
+
? (0, workspace_cache_1.resolveWorkspaceId)(context.workspaceCache, args.workspaceId)
|
|
145
|
+
: context.workspaceCache.currentWorkspace._id;
|
|
146
|
+
if (!workspaceId) {
|
|
147
|
+
return {
|
|
148
|
+
content: [{
|
|
149
|
+
type: "text",
|
|
150
|
+
text: `❌ Could not resolve workspace: ${args.workspaceId}`,
|
|
151
|
+
}],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
logger.debug('Calling v3.app.create', {
|
|
155
|
+
workspaceId,
|
|
156
|
+
name: args.name
|
|
157
|
+
});
|
|
158
|
+
const appData = {
|
|
159
|
+
cid: workspaceId,
|
|
160
|
+
name: args.name
|
|
161
|
+
};
|
|
162
|
+
if (args.description)
|
|
163
|
+
appData.description = args.description;
|
|
164
|
+
if (args.url !== undefined)
|
|
165
|
+
appData.url = args.url;
|
|
166
|
+
if (args.image)
|
|
167
|
+
appData.image = args.image;
|
|
168
|
+
if (args.config)
|
|
169
|
+
appData.config = args.config;
|
|
170
|
+
const result = await context.hailer.request('v3.app.create', [appData]);
|
|
171
|
+
logger.debug('App creation successful', {
|
|
172
|
+
appId: result.appId || result._id
|
|
173
|
+
});
|
|
174
|
+
const appId = result.appId || result._id;
|
|
175
|
+
let responseText = `✅ **App Created Successfully**\n\n`;
|
|
176
|
+
responseText += `**App Name:** ${args.name}\n`;
|
|
177
|
+
responseText += `**App ID:** \`${appId}\`\n`;
|
|
178
|
+
responseText += `**Workspace:** ${workspaceId}\n`;
|
|
179
|
+
if (args.description) {
|
|
180
|
+
responseText += `**Description:** ${args.description}\n`;
|
|
181
|
+
}
|
|
182
|
+
if (args.url) {
|
|
183
|
+
responseText += `**URL:** ${args.url}\n`;
|
|
184
|
+
responseText += `**Type:** Development App (points to ${args.url})\n`;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
responseText += `**URL:** Auto-generated by Hailer\n`;
|
|
188
|
+
responseText += `**Type:** Published App\n`;
|
|
189
|
+
}
|
|
190
|
+
responseText += `\n💡 **Next Steps:**\n`;
|
|
191
|
+
responseText += `- Use \`list_apps\` to see all apps\n`;
|
|
192
|
+
responseText += `- Use \`add_app_member\` to share with users/teams\n`;
|
|
193
|
+
responseText += `- Use \`update_app\` to modify properties\n`;
|
|
194
|
+
if (!args.url) {
|
|
195
|
+
responseText += `\n📦 **Publishing:**\n`;
|
|
196
|
+
responseText += `1. Configure manifest.json with this appId: \`${appId}\`\n`;
|
|
197
|
+
responseText += `2. Run: \`EMAIL=your@email.com npm run publish-production\`\n`;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
content: [{
|
|
201
|
+
type: "text",
|
|
202
|
+
text: responseText,
|
|
203
|
+
}],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
logger.error("Error creating app", error);
|
|
208
|
+
let errorMessage = 'Unknown error occurred';
|
|
209
|
+
if (error instanceof Error) {
|
|
210
|
+
errorMessage = error.message;
|
|
211
|
+
}
|
|
212
|
+
else if (typeof error === 'object' && error !== null) {
|
|
213
|
+
try {
|
|
214
|
+
errorMessage = JSON.stringify(error, null, 2);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
errorMessage = String(error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
errorMessage = String(error);
|
|
222
|
+
}
|
|
223
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
224
|
+
return {
|
|
225
|
+
content: [{
|
|
226
|
+
type: "text",
|
|
227
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to create apps. Only workspace administrators can create apps.\n\n**Error:** ${errorMessage}`,
|
|
228
|
+
}],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
content: [{
|
|
233
|
+
type: "text",
|
|
234
|
+
text: `❌ **Error creating app**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- User must be workspace administrator\n- Invalid workspace ID\n- Image ID must be 24 characters if provided`,
|
|
235
|
+
}],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// LIST APPS TOOL
|
|
242
|
+
// ============================================================================
|
|
243
|
+
const listAppsDescription = `🧪 [PLAYGROUND] List Apps - View all apps in workspace
|
|
244
|
+
|
|
245
|
+
**What it does**:
|
|
246
|
+
Lists all Hailer apps in the current workspace that you have access to.
|
|
247
|
+
|
|
248
|
+
**Example**:
|
|
249
|
+
\`\`\`javascript
|
|
250
|
+
list_apps()
|
|
251
|
+
\`\`\`
|
|
252
|
+
|
|
253
|
+
**Shows**:
|
|
254
|
+
- App name and description
|
|
255
|
+
- App ID
|
|
256
|
+
- URL (localhost for dev, auto for published)
|
|
257
|
+
- Creator and timestamps
|
|
258
|
+
- Configuration
|
|
259
|
+
|
|
260
|
+
**Use Cases**:
|
|
261
|
+
- See all workspace apps
|
|
262
|
+
- Find app IDs for updates
|
|
263
|
+
- Check which apps are development vs published
|
|
264
|
+
- Audit app configuration`;
|
|
265
|
+
exports.listAppsTool = {
|
|
266
|
+
name: 'list_apps',
|
|
267
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
268
|
+
description: listAppsDescription,
|
|
269
|
+
schema: zod_1.z.object({
|
|
270
|
+
workspaceId: zod_1.z
|
|
271
|
+
.string()
|
|
272
|
+
.optional()
|
|
273
|
+
.describe("Optional workspace ID - defaults to current workspace"),
|
|
274
|
+
}),
|
|
275
|
+
async execute(args, context) {
|
|
276
|
+
logger.debug('Listing apps', {
|
|
277
|
+
workspaceId: args.workspaceId,
|
|
278
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
279
|
+
});
|
|
280
|
+
try {
|
|
281
|
+
if (!context.workspaceCache) {
|
|
282
|
+
return {
|
|
283
|
+
content: [{
|
|
284
|
+
type: "text",
|
|
285
|
+
text: "❌ Workspace cache not available",
|
|
286
|
+
}],
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const workspaceId = args.workspaceId
|
|
290
|
+
? (0, workspace_cache_1.resolveWorkspaceId)(context.workspaceCache, args.workspaceId)
|
|
291
|
+
: context.workspaceCache.currentWorkspace._id;
|
|
292
|
+
if (!workspaceId) {
|
|
293
|
+
return {
|
|
294
|
+
content: [{
|
|
295
|
+
type: "text",
|
|
296
|
+
text: `❌ Could not resolve workspace: ${args.workspaceId}`,
|
|
297
|
+
}],
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
logger.debug('Calling v3.app.list');
|
|
301
|
+
const result = await context.hailer.request('v3.app.list', [{}]);
|
|
302
|
+
logger.debug('List apps successful', {
|
|
303
|
+
appCount: result.apps?.length || 0
|
|
304
|
+
});
|
|
305
|
+
let responseText = `✅ **Apps Retrieved**\n\n`;
|
|
306
|
+
responseText += `**Workspace:** ${workspaceId}\n\n`;
|
|
307
|
+
if (!result.apps || result.apps.length === 0) {
|
|
308
|
+
responseText += `**No apps found** in this workspace.\n\n`;
|
|
309
|
+
responseText += `💡 Use \`create_app\` to create your first app.`;
|
|
310
|
+
return {
|
|
311
|
+
content: [{
|
|
312
|
+
type: "text",
|
|
313
|
+
text: responseText,
|
|
314
|
+
}],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const apps = result.apps;
|
|
318
|
+
responseText += `**Total Apps:** ${apps.length}\n\n`;
|
|
319
|
+
apps.forEach((app) => {
|
|
320
|
+
responseText += `### ${app.name || 'Unnamed App'}\n`;
|
|
321
|
+
responseText += `- **App ID:** \`${app._id}\`\n`;
|
|
322
|
+
if (app.description) {
|
|
323
|
+
responseText += `- **Description:** ${app.description}\n`;
|
|
324
|
+
}
|
|
325
|
+
if (app.url) {
|
|
326
|
+
responseText += `- **URL:** ${app.url}\n`;
|
|
327
|
+
responseText += `- **Type:** ${app.url.includes('localhost') ? 'Development' : 'Custom URL'}\n`;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
responseText += `- **URL:** Auto-generated (Published App)\n`;
|
|
331
|
+
responseText += `- **Type:** Published\n`;
|
|
332
|
+
}
|
|
333
|
+
if (app.created) {
|
|
334
|
+
responseText += `- **Created:** ${new Date(app.created * 1000).toLocaleString()}\n`;
|
|
335
|
+
}
|
|
336
|
+
if (app.config && Object.keys(app.config).length > 0) {
|
|
337
|
+
responseText += `- **Config:** Present\n`;
|
|
338
|
+
}
|
|
339
|
+
responseText += `\n`;
|
|
340
|
+
});
|
|
341
|
+
responseText += `💡 **Next Steps:**\n`;
|
|
342
|
+
responseText += `- Use \`update_app\` to modify an app\n`;
|
|
343
|
+
responseText += `- Use \`add_app_member\` to share apps\n`;
|
|
344
|
+
responseText += `- Use \`remove_app\` to delete apps`;
|
|
345
|
+
return {
|
|
346
|
+
content: [{
|
|
347
|
+
type: "text",
|
|
348
|
+
text: responseText,
|
|
349
|
+
}],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
logger.error("Error listing apps", error);
|
|
354
|
+
let errorMessage = 'Unknown error occurred';
|
|
355
|
+
if (error instanceof Error) {
|
|
356
|
+
errorMessage = error.message;
|
|
357
|
+
}
|
|
358
|
+
else if (typeof error === 'object' && error !== null) {
|
|
359
|
+
try {
|
|
360
|
+
errorMessage = JSON.stringify(error, null, 2);
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
errorMessage = String(error);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
errorMessage = String(error);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
content: [{
|
|
371
|
+
type: "text",
|
|
372
|
+
text: `❌ **Error listing apps**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Workspace not accessible\n- Permission issues`,
|
|
373
|
+
}],
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
// ============================================================================
|
|
379
|
+
// UPDATE APP TOOL
|
|
380
|
+
// ============================================================================
|
|
381
|
+
const updateAppDescription = `🧪 [PLAYGROUND] Update App - Modify app properties
|
|
382
|
+
|
|
383
|
+
**What it does**:
|
|
384
|
+
Updates an existing app's properties (name, description, URL, etc.).
|
|
385
|
+
|
|
386
|
+
**Example - Update name and description**:
|
|
387
|
+
\`\`\`javascript
|
|
388
|
+
update_app({
|
|
389
|
+
appId: '<app-id>',
|
|
390
|
+
name: 'Updated App Name',
|
|
391
|
+
description: 'New description'
|
|
392
|
+
})
|
|
393
|
+
\`\`\`
|
|
394
|
+
|
|
395
|
+
**Example - Change URL (dev to prod)**:
|
|
396
|
+
\`\`\`javascript
|
|
397
|
+
update_app({
|
|
398
|
+
appId: '<app-id>',
|
|
399
|
+
url: '' // Empty for published
|
|
400
|
+
})
|
|
401
|
+
\`\`\`
|
|
402
|
+
|
|
403
|
+
**Updatable Properties**:
|
|
404
|
+
- \`name\` - App display name
|
|
405
|
+
- \`description\` - App description
|
|
406
|
+
- \`url\` - App URL
|
|
407
|
+
- \`image\` - App icon ID
|
|
408
|
+
- \`config\` - App configuration
|
|
409
|
+
|
|
410
|
+
**Requirements**:
|
|
411
|
+
- User must be app creator or workspace admin
|
|
412
|
+
|
|
413
|
+
**Tips**:
|
|
414
|
+
- Only specified properties are updated
|
|
415
|
+
- Use \`list_apps\` to get app IDs`;
|
|
416
|
+
exports.updateAppTool = {
|
|
417
|
+
name: 'update_app',
|
|
418
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
419
|
+
description: updateAppDescription,
|
|
420
|
+
schema: zod_1.z.object({
|
|
421
|
+
appId: zod_1.z
|
|
422
|
+
.string()
|
|
423
|
+
.min(1)
|
|
424
|
+
.describe("App ID to update"),
|
|
425
|
+
name: zod_1.z
|
|
426
|
+
.string()
|
|
427
|
+
.optional()
|
|
428
|
+
.describe("New app name"),
|
|
429
|
+
description: zod_1.z
|
|
430
|
+
.string()
|
|
431
|
+
.optional()
|
|
432
|
+
.describe("New description"),
|
|
433
|
+
url: zod_1.z
|
|
434
|
+
.string()
|
|
435
|
+
.optional()
|
|
436
|
+
.describe("New URL"),
|
|
437
|
+
image: zod_1.z
|
|
438
|
+
.string()
|
|
439
|
+
.optional()
|
|
440
|
+
.describe("New image ID"),
|
|
441
|
+
config: zod_1.z
|
|
442
|
+
.record(zod_1.z.unknown())
|
|
443
|
+
.optional()
|
|
444
|
+
.describe("New configuration"),
|
|
445
|
+
}),
|
|
446
|
+
async execute(args, context) {
|
|
447
|
+
logger.debug('Updating app', {
|
|
448
|
+
appId: args.appId,
|
|
449
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
450
|
+
});
|
|
451
|
+
try {
|
|
452
|
+
const updateData = {};
|
|
453
|
+
if (args.name !== undefined)
|
|
454
|
+
updateData.name = args.name;
|
|
455
|
+
if (args.description !== undefined)
|
|
456
|
+
updateData.description = args.description;
|
|
457
|
+
if (args.url !== undefined)
|
|
458
|
+
updateData.url = args.url;
|
|
459
|
+
if (args.image !== undefined)
|
|
460
|
+
updateData.image = args.image;
|
|
461
|
+
if (args.config !== undefined)
|
|
462
|
+
updateData.config = args.config;
|
|
463
|
+
logger.debug('Calling v3.app.update', {
|
|
464
|
+
appId: args.appId,
|
|
465
|
+
updates: Object.keys(updateData)
|
|
466
|
+
});
|
|
467
|
+
await context.hailer.request('v3.app.update', [
|
|
468
|
+
args.appId,
|
|
469
|
+
updateData
|
|
470
|
+
]);
|
|
471
|
+
logger.debug('App update successful');
|
|
472
|
+
let responseText = `✅ **App Updated Successfully**\n\n`;
|
|
473
|
+
responseText += `**App ID:** \`${args.appId}\`\n\n`;
|
|
474
|
+
responseText += `**Updated Properties:**\n`;
|
|
475
|
+
const updatedFields = [];
|
|
476
|
+
if (args.name)
|
|
477
|
+
updatedFields.push(`name → "${args.name}"`);
|
|
478
|
+
if (args.description)
|
|
479
|
+
updatedFields.push(`description → "${args.description}"`);
|
|
480
|
+
if (args.url !== undefined)
|
|
481
|
+
updatedFields.push(`url → "${args.url || 'auto-generated'}"`);
|
|
482
|
+
if (args.image)
|
|
483
|
+
updatedFields.push(`image → "${args.image}"`);
|
|
484
|
+
if (args.config)
|
|
485
|
+
updatedFields.push(`config (updated)`);
|
|
486
|
+
updatedFields.forEach(field => {
|
|
487
|
+
responseText += `- ${field}\n`;
|
|
488
|
+
});
|
|
489
|
+
responseText += `\n💡 Use \`list_apps\` to verify changes.`;
|
|
490
|
+
return {
|
|
491
|
+
content: [{
|
|
492
|
+
type: "text",
|
|
493
|
+
text: responseText,
|
|
494
|
+
}],
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
logger.error("Error updating app", error);
|
|
499
|
+
let errorMessage = 'Unknown error occurred';
|
|
500
|
+
if (error instanceof Error) {
|
|
501
|
+
errorMessage = error.message;
|
|
502
|
+
}
|
|
503
|
+
else if (typeof error === 'object' && error !== null) {
|
|
504
|
+
try {
|
|
505
|
+
errorMessage = JSON.stringify(error, null, 2);
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
errorMessage = String(error);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
errorMessage = String(error);
|
|
513
|
+
}
|
|
514
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
515
|
+
return {
|
|
516
|
+
content: [{
|
|
517
|
+
type: "text",
|
|
518
|
+
text: `❌ **App Not Found**\n\nThe specified app doesn't exist.\n\n**App ID:** \`${args.appId}\`\n\n💡 Use \`list_apps\` to see available apps.`,
|
|
519
|
+
}],
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
523
|
+
return {
|
|
524
|
+
content: [{
|
|
525
|
+
type: "text",
|
|
526
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to update this app. Only app creator or workspace admin can update apps.\n\n**Error:** ${errorMessage}`,
|
|
527
|
+
}],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
content: [{
|
|
532
|
+
type: "text",
|
|
533
|
+
text: `❌ **Error updating app**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- App ID invalid\n- Not app creator or admin\n- Invalid property values`,
|
|
534
|
+
}],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
// ============================================================================
|
|
540
|
+
// REMOVE APP TOOL
|
|
541
|
+
// ============================================================================
|
|
542
|
+
const removeAppDescription = `🧪 [PLAYGROUND] Remove App - ⚠️ DANGER: Permanently deletes app entry from Hailer
|
|
543
|
+
|
|
544
|
+
⚠️ **MANDATORY: GATHER COMPLETE CONTEXT BEFORE CALLING THIS TOOL**
|
|
545
|
+
**BEFORE calling this tool, you are REQUIRED to:**
|
|
546
|
+
1. Load the skill: \`get_skill({ skillName: "remove-app-skill" })\`
|
|
547
|
+
2. Fetch app details with \`list_apps\` to get:
|
|
548
|
+
- App name and ID
|
|
549
|
+
- Workspace ID and name
|
|
550
|
+
- App type (development/published)
|
|
551
|
+
- App configuration
|
|
552
|
+
3. Show comprehensive confirmation message including:
|
|
553
|
+
- Workspace ID and name
|
|
554
|
+
- App ID and name
|
|
555
|
+
- What will be deleted (entry, permissions, configuration)
|
|
556
|
+
- What won't be deleted (published code remains)
|
|
557
|
+
- Clear irreversibility warning
|
|
558
|
+
4. Wait for explicit user confirmation
|
|
559
|
+
**FAILURE TO GATHER AND SHOW THIS CONTEXT IS AN ERROR**
|
|
560
|
+
|
|
561
|
+
**Required**: appId
|
|
562
|
+
**Permission**: App creator or workspace administrator
|
|
563
|
+
|
|
564
|
+
**What gets deleted**:
|
|
565
|
+
- App entry (name, description, URL, metadata)
|
|
566
|
+
- App permissions
|
|
567
|
+
- App configuration
|
|
568
|
+
|
|
569
|
+
**What doesn't get deleted**:
|
|
570
|
+
- Published app code (remains on server)
|
|
571
|
+
- App data (if any)
|
|
572
|
+
|
|
573
|
+
**Example**:
|
|
574
|
+
\`\`\`javascript
|
|
575
|
+
remove_app({
|
|
576
|
+
appId: '<app-id>'
|
|
577
|
+
})
|
|
578
|
+
\`\`\`
|
|
579
|
+
|
|
580
|
+
**Tips**:
|
|
581
|
+
- Use \`list_apps\` to get app IDs
|
|
582
|
+
- Published app code must be removed separately
|
|
583
|
+
- Operation cannot be undone`;
|
|
584
|
+
exports.removeAppTool = {
|
|
585
|
+
name: 'remove_app',
|
|
586
|
+
group: tool_registry_1.ToolGroup.NUCLEAR,
|
|
587
|
+
description: removeAppDescription,
|
|
588
|
+
schema: zod_1.z.object({
|
|
589
|
+
appId: zod_1.z
|
|
590
|
+
.string()
|
|
591
|
+
.min(1)
|
|
592
|
+
.describe("App ID to remove"),
|
|
593
|
+
confirmed: zod_1.z
|
|
594
|
+
.boolean()
|
|
595
|
+
.optional()
|
|
596
|
+
.describe("First confirmation - must be true to proceed"),
|
|
597
|
+
secondConfirmed: zod_1.z
|
|
598
|
+
.boolean()
|
|
599
|
+
.optional()
|
|
600
|
+
.describe("Second confirmation - must be true to proceed (required for double-check safety)"),
|
|
601
|
+
}),
|
|
602
|
+
async execute(args, context) {
|
|
603
|
+
logger.debug('Removing app', {
|
|
604
|
+
appId: args.appId,
|
|
605
|
+
confirmed: args.confirmed,
|
|
606
|
+
secondConfirmed: args.secondConfirmed,
|
|
607
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
608
|
+
});
|
|
609
|
+
try {
|
|
610
|
+
// Fetch app info and workspace info
|
|
611
|
+
const [appListResult, initData] = await Promise.all([
|
|
612
|
+
context.hailer.request('v3.app.list', [{}]),
|
|
613
|
+
context.hailer.request('v2.core.init', [["network"]])
|
|
614
|
+
]);
|
|
615
|
+
const app = appListResult.apps?.find(a => a._id === args.appId);
|
|
616
|
+
const appName = app?.name || 'Unknown';
|
|
617
|
+
const workspaceId = initData.network?._id || 'Unknown';
|
|
618
|
+
const workspaceName = initData.network?.name || 'Unknown';
|
|
619
|
+
// SAFETY CHECK: Require double confirmation
|
|
620
|
+
if (!args.confirmed || !args.secondConfirmed) {
|
|
621
|
+
let warningText = `⚠️ **DESTRUCTIVE OPERATION - CONFIRMATION REQUIRED**\n\n`;
|
|
622
|
+
warningText += `You are about to **permanently delete** the following:\n\n`;
|
|
623
|
+
warningText += `**App:** ${appName}\n`;
|
|
624
|
+
warningText += `**App ID:** \`${args.appId}\`\n`;
|
|
625
|
+
warningText += `**Workspace:** ${workspaceName} (\`${workspaceId}\`)\n\n`;
|
|
626
|
+
warningText += `**⚠️ This will permanently delete:**\n`;
|
|
627
|
+
warningText += `- App entry (name, description, URL)\n`;
|
|
628
|
+
warningText += `- App permissions\n`;
|
|
629
|
+
warningText += `- App configuration\n\n`;
|
|
630
|
+
warningText += `**🚨 THIS CANNOT BE UNDONE! 🚨**\n\n`;
|
|
631
|
+
warningText += `**To proceed, you must:**\n`;
|
|
632
|
+
warningText += `1. Review the \`remove-app-skill\` (REQUIRED)\n`;
|
|
633
|
+
warningText += `2. Call this tool again with BOTH confirmations:\n\n`;
|
|
634
|
+
warningText += `\`\`\`javascript\n`;
|
|
635
|
+
warningText += `remove_app({\n`;
|
|
636
|
+
warningText += ` appId: "${args.appId}",\n`;
|
|
637
|
+
warningText += ` confirmed: true,\n`;
|
|
638
|
+
warningText += ` secondConfirmed: true\n`;
|
|
639
|
+
warningText += `})\n`;
|
|
640
|
+
warningText += `\`\`\`\n\n`;
|
|
641
|
+
warningText += `💡 **Before proceeding:**\n`;
|
|
642
|
+
warningText += `- Load \`remove-app-skill\` to review safety checklist\n`;
|
|
643
|
+
warningText += `- Verify with user that this is intentional\n`;
|
|
644
|
+
warningText += `- Note: Published app code remains on server`;
|
|
645
|
+
return {
|
|
646
|
+
content: [{
|
|
647
|
+
type: "text",
|
|
648
|
+
text: warningText,
|
|
649
|
+
}],
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
logger.debug('Calling v3.app.remove', {
|
|
653
|
+
appId: args.appId,
|
|
654
|
+
appName,
|
|
655
|
+
workspaceId,
|
|
656
|
+
workspaceName
|
|
657
|
+
});
|
|
658
|
+
await context.hailer.request('v3.app.remove', [args.appId]);
|
|
659
|
+
logger.debug('App removal successful', {
|
|
660
|
+
appId: args.appId,
|
|
661
|
+
appName
|
|
662
|
+
});
|
|
663
|
+
let responseText = `✅ **App Removed Successfully**\n\n`;
|
|
664
|
+
responseText += `**App:** ${appName}\n`;
|
|
665
|
+
responseText += `**App ID:** \`${args.appId}\`\n`;
|
|
666
|
+
responseText += `**Workspace:** ${workspaceName} (\`${workspaceId}\`)\n\n`;
|
|
667
|
+
responseText += `⚠️ **The app entry has been permanently deleted.**\n\n`;
|
|
668
|
+
responseText += `**What was deleted:**\n`;
|
|
669
|
+
responseText += `- App entry (name, description, URL)\n`;
|
|
670
|
+
responseText += `- App permissions\n`;
|
|
671
|
+
responseText += `- App configuration\n\n`;
|
|
672
|
+
responseText += `**Note:** Published app code remains on server (if applicable).\n\n`;
|
|
673
|
+
responseText += `💡 Use \`list_apps\` to see remaining apps.`;
|
|
674
|
+
return {
|
|
675
|
+
content: [{
|
|
676
|
+
type: "text",
|
|
677
|
+
text: responseText,
|
|
678
|
+
}],
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
logger.error("Error removing app", error);
|
|
683
|
+
let errorMessage = 'Unknown error occurred';
|
|
684
|
+
if (error instanceof Error) {
|
|
685
|
+
errorMessage = error.message;
|
|
686
|
+
}
|
|
687
|
+
else if (typeof error === 'object' && error !== null) {
|
|
688
|
+
try {
|
|
689
|
+
errorMessage = JSON.stringify(error, null, 2);
|
|
690
|
+
}
|
|
691
|
+
catch {
|
|
692
|
+
errorMessage = String(error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
errorMessage = String(error);
|
|
697
|
+
}
|
|
698
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
699
|
+
return {
|
|
700
|
+
content: [{
|
|
701
|
+
type: "text",
|
|
702
|
+
text: `❌ **App Not Found**\n\nThe specified app doesn't exist or has already been deleted.\n\n**App ID:** \`${args.appId}\`\n\n💡 Use \`list_apps\` to see available apps.`,
|
|
703
|
+
}],
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
707
|
+
return {
|
|
708
|
+
content: [{
|
|
709
|
+
type: "text",
|
|
710
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to remove this app. Only app creator or workspace admin can delete apps.\n\n**Error:** ${errorMessage}`,
|
|
711
|
+
}],
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
content: [{
|
|
716
|
+
type: "text",
|
|
717
|
+
text: `❌ **Error removing app**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- App ID invalid\n- App already deleted\n- Not app creator or admin`,
|
|
718
|
+
}],
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
// ============================================================================
|
|
724
|
+
// ADD APP MEMBER TOOL
|
|
725
|
+
// ============================================================================
|
|
726
|
+
const addAppMemberDescription = `🧪 [PLAYGROUND] Add App Member - Share app with users/teams
|
|
727
|
+
|
|
728
|
+
**What it does**:
|
|
729
|
+
Grants permission for users, teams, or entire workspace to access an app.
|
|
730
|
+
|
|
731
|
+
**Member Formats**:
|
|
732
|
+
- \`network_<workspaceId>\` - Entire workspace
|
|
733
|
+
- \`team_<teamId>\` - Specific team
|
|
734
|
+
- \`user_<userId>\` - Individual user
|
|
735
|
+
- \`group_<groupId>\` - Custom group
|
|
736
|
+
|
|
737
|
+
**Example - Share with workspace**:
|
|
738
|
+
\`\`\`javascript
|
|
739
|
+
add_app_member({
|
|
740
|
+
appId: '<app-id>',
|
|
741
|
+
member: 'network_68446c045b30685f67c6fc8c'
|
|
742
|
+
})
|
|
743
|
+
\`\`\`
|
|
744
|
+
|
|
745
|
+
**Example - Share with team**:
|
|
746
|
+
\`\`\`javascript
|
|
747
|
+
add_app_member({
|
|
748
|
+
appId: '<app-id>',
|
|
749
|
+
member: 'team_<team-id>'
|
|
750
|
+
})
|
|
751
|
+
\`\`\`
|
|
752
|
+
|
|
753
|
+
**Example - Share with user**:
|
|
754
|
+
\`\`\`javascript
|
|
755
|
+
add_app_member({
|
|
756
|
+
appId: '<app-id>',
|
|
757
|
+
member: 'user_<user-id>'
|
|
758
|
+
})
|
|
759
|
+
\`\`\`
|
|
760
|
+
|
|
761
|
+
**Requirements**:
|
|
762
|
+
- User must be app creator or workspace admin
|
|
763
|
+
|
|
764
|
+
**Tips**:
|
|
765
|
+
- Default: Only creator and admins see app
|
|
766
|
+
- Use network_ to make app available to everyone
|
|
767
|
+
- Use team_ for team-specific apps`;
|
|
768
|
+
exports.addAppMemberTool = {
|
|
769
|
+
name: 'add_app_member',
|
|
770
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
771
|
+
description: addAppMemberDescription,
|
|
772
|
+
schema: zod_1.z.object({
|
|
773
|
+
appId: zod_1.z
|
|
774
|
+
.string()
|
|
775
|
+
.min(1)
|
|
776
|
+
.describe("App ID"),
|
|
777
|
+
member: zod_1.z
|
|
778
|
+
.string()
|
|
779
|
+
.regex(/^(network_|team_|user_|group_)[a-zA-Z0-9]+$/, "Member must be network_*, team_*, user_*, or group_*")
|
|
780
|
+
.describe("Member identifier (network_<id>, team_<id>, user_<id>, or group_<id>)"),
|
|
781
|
+
}),
|
|
782
|
+
async execute(args, context) {
|
|
783
|
+
logger.debug('Adding app member', {
|
|
784
|
+
appId: args.appId,
|
|
785
|
+
member: args.member,
|
|
786
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
787
|
+
});
|
|
788
|
+
try {
|
|
789
|
+
logger.debug('Calling v3.app.member.add', {
|
|
790
|
+
appId: args.appId,
|
|
791
|
+
member: args.member
|
|
792
|
+
});
|
|
793
|
+
await context.hailer.request('v3.app.member.add', [
|
|
794
|
+
args.appId,
|
|
795
|
+
args.member
|
|
796
|
+
]);
|
|
797
|
+
logger.debug('App member added successfully');
|
|
798
|
+
const memberType = args.member.split('_')[0];
|
|
799
|
+
const memberTypeLabel = {
|
|
800
|
+
'network': 'Workspace',
|
|
801
|
+
'team': 'Team',
|
|
802
|
+
'user': 'User',
|
|
803
|
+
'group': 'Group'
|
|
804
|
+
};
|
|
805
|
+
let responseText = `✅ **App Member Added Successfully**\n\n`;
|
|
806
|
+
responseText += `**App ID:** \`${args.appId}\`\n`;
|
|
807
|
+
responseText += `**Member Type:** ${memberTypeLabel[memberType] || 'Member'}\n`;
|
|
808
|
+
responseText += `**Member ID:** \`${args.member}\`\n\n`;
|
|
809
|
+
if (memberType === 'network') {
|
|
810
|
+
responseText += `🌐 **App is now accessible to entire workspace!**\n`;
|
|
811
|
+
}
|
|
812
|
+
else if (memberType === 'team') {
|
|
813
|
+
responseText += `👥 **App is now accessible to all team members.**\n`;
|
|
814
|
+
}
|
|
815
|
+
else if (memberType === 'user') {
|
|
816
|
+
responseText += `👤 **App is now accessible to this user.**\n`;
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
responseText += `🔑 **App is now accessible to this group.**\n`;
|
|
820
|
+
}
|
|
821
|
+
return {
|
|
822
|
+
content: [{
|
|
823
|
+
type: "text",
|
|
824
|
+
text: responseText,
|
|
825
|
+
}],
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
catch (error) {
|
|
829
|
+
logger.error("Error adding app member", error);
|
|
830
|
+
let errorMessage = 'Unknown error occurred';
|
|
831
|
+
if (error instanceof Error) {
|
|
832
|
+
errorMessage = error.message;
|
|
833
|
+
}
|
|
834
|
+
else if (typeof error === 'object' && error !== null) {
|
|
835
|
+
try {
|
|
836
|
+
errorMessage = JSON.stringify(error, null, 2);
|
|
837
|
+
}
|
|
838
|
+
catch {
|
|
839
|
+
errorMessage = String(error);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
errorMessage = String(error);
|
|
844
|
+
}
|
|
845
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
846
|
+
return {
|
|
847
|
+
content: [{
|
|
848
|
+
type: "text",
|
|
849
|
+
text: `❌ **App Not Found**\n\nThe specified app doesn't exist.\n\n**App ID:** \`${args.appId}\`\n\n💡 Use \`list_apps\` to see available apps.`,
|
|
850
|
+
}],
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
854
|
+
return {
|
|
855
|
+
content: [{
|
|
856
|
+
type: "text",
|
|
857
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to manage this app's members. Only app creator or workspace admin can add members.\n\n**Error:** ${errorMessage}`,
|
|
858
|
+
}],
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
return {
|
|
862
|
+
content: [{
|
|
863
|
+
type: "text",
|
|
864
|
+
text: `❌ **Error adding app member**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- App ID invalid\n- Member ID invalid\n- Not app creator or admin\n- Member format must be network_*, team_*, user_*, or group_*`,
|
|
865
|
+
}],
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
// ============================================================================
|
|
871
|
+
// REMOVE APP MEMBER TOOL
|
|
872
|
+
// ============================================================================
|
|
873
|
+
const removeAppMemberDescription = `🧪 [PLAYGROUND] Remove App Member - Revoke app access
|
|
874
|
+
|
|
875
|
+
**What it does**:
|
|
876
|
+
Revokes permission for users, teams, or workspace to access an app.
|
|
877
|
+
|
|
878
|
+
**Example - Remove workspace access**:
|
|
879
|
+
\`\`\`javascript
|
|
880
|
+
remove_app_member({
|
|
881
|
+
appId: '<app-id>',
|
|
882
|
+
member: 'network_68446c045b30685f67c6fc8c'
|
|
883
|
+
})
|
|
884
|
+
\`\`\`
|
|
885
|
+
|
|
886
|
+
**Example - Remove team access**:
|
|
887
|
+
\`\`\`javascript
|
|
888
|
+
remove_app_member({
|
|
889
|
+
appId: '<app-id>',
|
|
890
|
+
member: 'team_<team-id>'
|
|
891
|
+
})
|
|
892
|
+
\`\`\`
|
|
893
|
+
|
|
894
|
+
**Requirements**:
|
|
895
|
+
- User must be app creator or workspace admin
|
|
896
|
+
|
|
897
|
+
**Notes**:
|
|
898
|
+
- Creator and admins always retain access
|
|
899
|
+
- Removing workspace revokes access from all members
|
|
900
|
+
|
|
901
|
+
**Tips**:
|
|
902
|
+
- Use \`list_apps\` to get app IDs
|
|
903
|
+
- Member format same as add_app_member`;
|
|
904
|
+
exports.removeAppMemberTool = {
|
|
905
|
+
name: 'remove_app_member',
|
|
906
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
907
|
+
description: removeAppMemberDescription,
|
|
908
|
+
schema: zod_1.z.object({
|
|
909
|
+
appId: zod_1.z
|
|
910
|
+
.string()
|
|
911
|
+
.min(1)
|
|
912
|
+
.describe("App ID"),
|
|
913
|
+
member: zod_1.z
|
|
914
|
+
.string()
|
|
915
|
+
.regex(/^(network_|team_|user_|group_)[a-zA-Z0-9]+$/, "Member must be network_*, team_*, user_*, or group_*")
|
|
916
|
+
.describe("Member identifier to remove"),
|
|
917
|
+
}),
|
|
918
|
+
async execute(args, context) {
|
|
919
|
+
logger.debug('Removing app member', {
|
|
920
|
+
appId: args.appId,
|
|
921
|
+
member: args.member,
|
|
922
|
+
apiKey: context.apiKey.substring(0, 8) + '...'
|
|
923
|
+
});
|
|
924
|
+
try {
|
|
925
|
+
logger.debug('Calling v3.app.member.remove', {
|
|
926
|
+
appId: args.appId,
|
|
927
|
+
member: args.member
|
|
928
|
+
});
|
|
929
|
+
await context.hailer.request('v3.app.member.remove', [
|
|
930
|
+
args.appId,
|
|
931
|
+
args.member
|
|
932
|
+
]);
|
|
933
|
+
logger.debug('App member removed successfully');
|
|
934
|
+
const memberType = args.member.split('_')[0];
|
|
935
|
+
const memberTypeLabel = {
|
|
936
|
+
'network': 'Workspace',
|
|
937
|
+
'team': 'Team',
|
|
938
|
+
'user': 'User',
|
|
939
|
+
'group': 'Group'
|
|
940
|
+
};
|
|
941
|
+
let responseText = `✅ **App Member Removed Successfully**\n\n`;
|
|
942
|
+
responseText += `**App ID:** \`${args.appId}\`\n`;
|
|
943
|
+
responseText += `**Member Type:** ${memberTypeLabel[memberType] || 'Member'}\n`;
|
|
944
|
+
responseText += `**Member ID:** \`${args.member}\`\n\n`;
|
|
945
|
+
responseText += `🔒 **Access revoked for this ${(memberTypeLabel[memberType] || 'Member').toLowerCase()}.**\n\n`;
|
|
946
|
+
responseText += `**Note:** Creator and admins always retain access.`;
|
|
947
|
+
return {
|
|
948
|
+
content: [{
|
|
949
|
+
type: "text",
|
|
950
|
+
text: responseText,
|
|
951
|
+
}],
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
catch (error) {
|
|
955
|
+
logger.error("Error removing app member", error);
|
|
956
|
+
let errorMessage = 'Unknown error occurred';
|
|
957
|
+
if (error instanceof Error) {
|
|
958
|
+
errorMessage = error.message;
|
|
959
|
+
}
|
|
960
|
+
else if (typeof error === 'object' && error !== null) {
|
|
961
|
+
try {
|
|
962
|
+
errorMessage = JSON.stringify(error, null, 2);
|
|
963
|
+
}
|
|
964
|
+
catch {
|
|
965
|
+
errorMessage = String(error);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
errorMessage = String(error);
|
|
970
|
+
}
|
|
971
|
+
if (errorMessage.toLowerCase().includes('not found')) {
|
|
972
|
+
return {
|
|
973
|
+
content: [{
|
|
974
|
+
type: "text",
|
|
975
|
+
text: `❌ **App or Member Not Found**\n\nThe specified app or member doesn't exist.\n\n**App ID:** \`${args.appId}\`\n**Member:** \`${args.member}\`\n\n💡 Use \`list_apps\` to see available apps.`,
|
|
976
|
+
}],
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
if (errorMessage.toLowerCase().includes('permission')) {
|
|
980
|
+
return {
|
|
981
|
+
content: [{
|
|
982
|
+
type: "text",
|
|
983
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to manage this app's members. Only app creator or workspace admin can remove members.\n\n**Error:** ${errorMessage}`,
|
|
984
|
+
}],
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
content: [{
|
|
989
|
+
type: "text",
|
|
990
|
+
text: `❌ **Error removing app member**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- App ID invalid\n- Member doesn't have access\n- Not app creator or admin`,
|
|
991
|
+
}],
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
// ============================================================================
|
|
997
|
+
// SCAFFOLD HAILER APP TOOL
|
|
998
|
+
// ============================================================================
|
|
999
|
+
const scaffoldHailerAppDescription = `Scaffold new Hailer app from template - ONE SHOT SETUP
|
|
1000
|
+
|
|
1001
|
+
⚠️ **MANDATORY: YOU MUST LOAD SKILL FIRST**
|
|
1002
|
+
**BEFORE calling this tool, you are REQUIRED to:**
|
|
1003
|
+
1. Load the skill: \`get_skill({ skillName: "scaffold-hailer-app-skill" })\`
|
|
1004
|
+
2. Read the setup guide and understand the process
|
|
1005
|
+
3. Review template options and troubleshooting tips
|
|
1006
|
+
**FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
|
|
1007
|
+
|
|
1008
|
+
**Required Parameters**:
|
|
1009
|
+
- projectName: Project folder name
|
|
1010
|
+
- template: react-ts (recommended), react-swc-ts, or vanilla
|
|
1011
|
+
|
|
1012
|
+
**Optional Parameters**:
|
|
1013
|
+
- description: App description for Hailer
|
|
1014
|
+
- autoCreateDevApp: (default: true) Automatically create dev app entry in Hailer
|
|
1015
|
+
- autoShareWithWorkspace: (default: true) Share app with entire workspace
|
|
1016
|
+
- autoStartDevServer: (default: true) Start dev server in background
|
|
1017
|
+
|
|
1018
|
+
**What This Does (One Shot)**:
|
|
1019
|
+
1. ✅ Scaffolds project from template
|
|
1020
|
+
2. ✅ Installs dependencies
|
|
1021
|
+
3. ✅ Creates dev app entry in Hailer with correct URL
|
|
1022
|
+
4. ✅ Shares app with workspace
|
|
1023
|
+
5. ✅ Updates manifest.json with app ID
|
|
1024
|
+
6. ✅ Starts dev server on port 3000
|
|
1025
|
+
7. ✅ App ready to open in Hailer!
|
|
1026
|
+
|
|
1027
|
+
**Target Directory**:
|
|
1028
|
+
- Uses DEV_APPS_PATH from .env.local if set
|
|
1029
|
+
- Falls back to targetDirectory parameter
|
|
1030
|
+
- Falls back to current directory if neither set`;
|
|
1031
|
+
exports.scaffoldHailerAppTool = {
|
|
1032
|
+
name: 'scaffold_hailer_app',
|
|
1033
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
1034
|
+
description: scaffoldHailerAppDescription,
|
|
1035
|
+
schema: zod_1.z.object({
|
|
1036
|
+
projectName: zod_1.z.string().min(1).describe("Project folder name"),
|
|
1037
|
+
template: zod_1.z.enum(['react-ts', 'react-swc-ts', 'vanilla']).describe("Template to use"),
|
|
1038
|
+
description: zod_1.z.string().optional().describe("App description for Hailer"),
|
|
1039
|
+
targetDirectory: zod_1.z.string().optional().describe("Target directory (defaults to DEV_APPS_PATH or current)"),
|
|
1040
|
+
installDependencies: zod_1.z.coerce.boolean().optional().default(true).describe("Run npm install after scaffolding"),
|
|
1041
|
+
autoCreateDevApp: zod_1.z.coerce.boolean().optional().default(true).describe("Automatically create dev app entry in Hailer"),
|
|
1042
|
+
autoShareWithWorkspace: zod_1.z.coerce.boolean().optional().default(true).describe("Share app with entire workspace"),
|
|
1043
|
+
autoStartDevServer: zod_1.z.coerce.boolean().optional().default(true).describe("Start dev server in background on port 3000")
|
|
1044
|
+
}),
|
|
1045
|
+
async execute(args, context) {
|
|
1046
|
+
const { execSync, spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1047
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1048
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
1049
|
+
try {
|
|
1050
|
+
const targetDir = args.targetDirectory || config_1.environment.DEV_APPS_PATH || process.cwd();
|
|
1051
|
+
const projectPath = path.join(targetDir, args.projectName);
|
|
1052
|
+
// Check if directory already exists
|
|
1053
|
+
if (fs.existsSync(projectPath)) {
|
|
1054
|
+
return {
|
|
1055
|
+
content: [{
|
|
1056
|
+
type: "text",
|
|
1057
|
+
text: `❌ **Directory Already Exists**\n\nThe directory \`${projectPath}\` already exists.\n\n**Options:**\n- Choose a different project name\n- Remove the existing directory\n- Use a different target directory`,
|
|
1058
|
+
}],
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
let responseText = `🚀 **ONE-SHOT HAILER APP SETUP**\n\n`;
|
|
1062
|
+
responseText += `**Project:** ${args.projectName}\n`;
|
|
1063
|
+
responseText += `**Template:** ${args.template}\n`;
|
|
1064
|
+
responseText += `**Location:** ${projectPath}\n\n`;
|
|
1065
|
+
// Step 1: Scaffold project
|
|
1066
|
+
responseText += `⏳ Step 1/8: Creating project from template...\n`;
|
|
1067
|
+
try {
|
|
1068
|
+
const createCmd = `npm create @hailer/app@latest ${args.projectName} -- --template ${args.template}`;
|
|
1069
|
+
execSync(createCmd, {
|
|
1070
|
+
cwd: targetDir,
|
|
1071
|
+
stdio: 'pipe',
|
|
1072
|
+
encoding: 'utf-8',
|
|
1073
|
+
shell: '/bin/bash'
|
|
1074
|
+
});
|
|
1075
|
+
responseText += `✅ Project scaffolded\n\n`;
|
|
1076
|
+
}
|
|
1077
|
+
catch (error) {
|
|
1078
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1079
|
+
return {
|
|
1080
|
+
content: [{
|
|
1081
|
+
type: "text",
|
|
1082
|
+
text: `❌ **Error scaffolding project**\n\n**Error:** ${errorMessage}\n\n**Possible Issues:**\n- npm not available\n- Network issues\n- Insufficient permissions`,
|
|
1083
|
+
}],
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
// Step 2: Install dependencies
|
|
1087
|
+
if (args.installDependencies !== false) {
|
|
1088
|
+
responseText += `⏳ Step 2/8: Installing dependencies...\n`;
|
|
1089
|
+
try {
|
|
1090
|
+
execSync('npm install', {
|
|
1091
|
+
cwd: projectPath,
|
|
1092
|
+
stdio: 'pipe',
|
|
1093
|
+
encoding: 'utf-8',
|
|
1094
|
+
shell: '/bin/bash'
|
|
1095
|
+
});
|
|
1096
|
+
responseText += `✅ Dependencies installed\n\n`;
|
|
1097
|
+
}
|
|
1098
|
+
catch (error) {
|
|
1099
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1100
|
+
responseText += `⚠️ Failed to install dependencies: ${errorMessage}\n\n`;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
responseText += `⏭️ Step 2/8: Skipped (installDependencies = false)\n\n`;
|
|
1105
|
+
}
|
|
1106
|
+
// Step 3: Configure CORS in vite.config.ts
|
|
1107
|
+
responseText += `⏳ Step 3/8: Configuring CORS for Hailer access...\n`;
|
|
1108
|
+
try {
|
|
1109
|
+
const viteConfigPath = path.join(projectPath, 'vite.config.ts');
|
|
1110
|
+
if (fs.existsSync(viteConfigPath)) {
|
|
1111
|
+
let viteConfig = fs.readFileSync(viteConfigPath, 'utf-8');
|
|
1112
|
+
// Check if CORS is already configured
|
|
1113
|
+
if (!viteConfig.includes('cors:')) {
|
|
1114
|
+
// Add CORS configuration to the server block
|
|
1115
|
+
// Matches: server: { port: 3000 } or server: { port: 3000, } with optional whitespace
|
|
1116
|
+
viteConfig = viteConfig.replace(/server:\s*{\s*port:\s*3000\s*,?\s*}/, `server: {
|
|
1117
|
+
port: 3000,
|
|
1118
|
+
cors: {
|
|
1119
|
+
origin: '*',
|
|
1120
|
+
credentials: true
|
|
1121
|
+
}
|
|
1122
|
+
}`);
|
|
1123
|
+
fs.writeFileSync(viteConfigPath, viteConfig);
|
|
1124
|
+
responseText += `✅ CORS configured in vite.config.ts\n\n`;
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
responseText += `✅ CORS already configured\n\n`;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
responseText += `⚠️ vite.config.ts not found, skipping CORS configuration\n\n`;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
catch (error) {
|
|
1135
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1136
|
+
responseText += `⚠️ Failed to configure CORS: ${errorMessage}\n\n`;
|
|
1137
|
+
}
|
|
1138
|
+
let appId;
|
|
1139
|
+
let workspaceId;
|
|
1140
|
+
// Step 4: Create dev app in Hailer
|
|
1141
|
+
if (args.autoCreateDevApp !== false) {
|
|
1142
|
+
responseText += `⏳ Step 4/8: Creating dev app entry in Hailer...\n`;
|
|
1143
|
+
try {
|
|
1144
|
+
if (!context.workspaceCache) {
|
|
1145
|
+
responseText += `⚠️ Workspace cache not available, skipping app creation\n\n`;
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
workspaceId = context.workspaceCache.currentWorkspace._id;
|
|
1149
|
+
const appData = {
|
|
1150
|
+
cid: workspaceId,
|
|
1151
|
+
name: args.projectName,
|
|
1152
|
+
url: 'http://localhost:3000'
|
|
1153
|
+
};
|
|
1154
|
+
if (args.description) {
|
|
1155
|
+
appData.description = args.description;
|
|
1156
|
+
}
|
|
1157
|
+
const result = await context.hailer.request('v3.app.create', [appData]);
|
|
1158
|
+
appId = result.appId || result._id;
|
|
1159
|
+
responseText += `✅ App created: ${appId}\n`;
|
|
1160
|
+
responseText += ` URL: http://localhost:3000\n\n`;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
catch (error) {
|
|
1164
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1165
|
+
responseText += `⚠️ Failed to create app in Hailer: ${errorMessage}\n\n`;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
else {
|
|
1169
|
+
responseText += `⏭️ Step 4/8: Skipped (autoCreateDevApp = false)\n\n`;
|
|
1170
|
+
}
|
|
1171
|
+
// Step 5: Share with workspace
|
|
1172
|
+
if (args.autoShareWithWorkspace !== false && appId && workspaceId) {
|
|
1173
|
+
responseText += `⏳ Step 5/8: Sharing app with workspace...\n`;
|
|
1174
|
+
try {
|
|
1175
|
+
await context.hailer.request('v3.app.member.add', [
|
|
1176
|
+
appId,
|
|
1177
|
+
`network_${workspaceId}`
|
|
1178
|
+
]);
|
|
1179
|
+
responseText += `✅ App shared with entire workspace\n\n`;
|
|
1180
|
+
}
|
|
1181
|
+
catch (error) {
|
|
1182
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1183
|
+
responseText += `⚠️ Failed to share app: ${errorMessage}\n\n`;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
responseText += `⏭️ Step 5/8: Skipped (no app ID or autoShareWithWorkspace = false)\n\n`;
|
|
1188
|
+
}
|
|
1189
|
+
// Step 6: Update manifest.json with app ID
|
|
1190
|
+
if (appId) {
|
|
1191
|
+
responseText += `⏳ Step 6/8: Updating manifest.json with app ID...\n`;
|
|
1192
|
+
try {
|
|
1193
|
+
const manifestPath = path.join(projectPath, 'public', 'manifest.json');
|
|
1194
|
+
if (fs.existsSync(manifestPath)) {
|
|
1195
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
1196
|
+
const manifest = JSON.parse(manifestContent);
|
|
1197
|
+
manifest.appId = appId;
|
|
1198
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
1199
|
+
responseText += `✅ manifest.json updated with appId\n\n`;
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
responseText += `⚠️ manifest.json not found at ${manifestPath}\n\n`;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1207
|
+
responseText += `⚠️ Failed to update manifest: ${errorMessage}\n\n`;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
else {
|
|
1211
|
+
responseText += `⏭️ Step 6/8: Skipped (no app ID)\n\n`;
|
|
1212
|
+
}
|
|
1213
|
+
// Step 7: Start dev server
|
|
1214
|
+
if (args.autoStartDevServer !== false) {
|
|
1215
|
+
responseText += `⏳ Step 7/8: Starting dev server on port 3000...\n`;
|
|
1216
|
+
try {
|
|
1217
|
+
// Start dev server in background
|
|
1218
|
+
const devServer = spawn('npm', ['run', 'dev'], {
|
|
1219
|
+
cwd: projectPath,
|
|
1220
|
+
detached: true,
|
|
1221
|
+
stdio: 'ignore',
|
|
1222
|
+
shell: true
|
|
1223
|
+
});
|
|
1224
|
+
devServer.unref();
|
|
1225
|
+
responseText += `✅ Dev server started in background\n`;
|
|
1226
|
+
responseText += ` PID: ${devServer.pid}\n`;
|
|
1227
|
+
responseText += ` URL: http://localhost:3000\n\n`;
|
|
1228
|
+
}
|
|
1229
|
+
catch (error) {
|
|
1230
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1231
|
+
responseText += `⚠️ Failed to start dev server: ${errorMessage}\n`;
|
|
1232
|
+
responseText += ` Start manually: cd ${args.projectName} && npm run dev\n\n`;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
responseText += `⏭️ Step 7/8: Skipped (autoStartDevServer = false)\n\n`;
|
|
1237
|
+
}
|
|
1238
|
+
// Step 8: Complete
|
|
1239
|
+
responseText += `✅ **Step 8/8: Setup Complete!**\n\n`;
|
|
1240
|
+
// Final summary
|
|
1241
|
+
responseText += `## 🎉 Your Hailer App is Ready!\n\n`;
|
|
1242
|
+
responseText += `**What was done:**\n`;
|
|
1243
|
+
responseText += `- ✅ Project scaffolded from ${args.template} template\n`;
|
|
1244
|
+
if (args.installDependencies !== false)
|
|
1245
|
+
responseText += `- ✅ Dependencies installed\n`;
|
|
1246
|
+
responseText += `- ✅ CORS configured for Hailer access\n`;
|
|
1247
|
+
if (appId)
|
|
1248
|
+
responseText += `- ✅ Dev app entry created in Hailer (ID: \`${appId}\`)\n`;
|
|
1249
|
+
if (args.autoShareWithWorkspace !== false && appId)
|
|
1250
|
+
responseText += `- ✅ App shared with entire workspace\n`;
|
|
1251
|
+
if (appId)
|
|
1252
|
+
responseText += `- ✅ manifest.json configured with app ID\n`;
|
|
1253
|
+
if (args.autoStartDevServer !== false)
|
|
1254
|
+
responseText += `- ✅ Dev server started on http://localhost:3000\n`;
|
|
1255
|
+
responseText += `\n## 🚀 Next Steps\n\n`;
|
|
1256
|
+
responseText += `1. **Open the app in Hailer** - Look for "${args.projectName}" in your apps menu\n`;
|
|
1257
|
+
responseText += `2. **Start coding** - Files in \`${projectPath}/src\` will hot-reload\n`;
|
|
1258
|
+
responseText += `3. **View logs** - Check dev server output if needed\n\n`;
|
|
1259
|
+
responseText += `## 📂 Project Location\n\n`;
|
|
1260
|
+
responseText += `\`\`\`\n${projectPath}\n\`\`\`\n\n`;
|
|
1261
|
+
if (appId) {
|
|
1262
|
+
responseText += `## 🔗 App Info\n\n`;
|
|
1263
|
+
responseText += `- **App ID:** \`${appId}\`\n`;
|
|
1264
|
+
responseText += `- **Dev URL:** http://localhost:3000\n`;
|
|
1265
|
+
responseText += `- **Shared with:** Entire workspace\n`;
|
|
1266
|
+
}
|
|
1267
|
+
return {
|
|
1268
|
+
content: [{
|
|
1269
|
+
type: "text",
|
|
1270
|
+
text: responseText,
|
|
1271
|
+
}],
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
catch (error) {
|
|
1275
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1276
|
+
return {
|
|
1277
|
+
content: [{
|
|
1278
|
+
type: "text",
|
|
1279
|
+
text: `❌ **Error in one-shot setup**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Node.js not installed or version < 18\n- npm not available\n- Insufficient permissions\n- Hailer API connection issues`,
|
|
1280
|
+
}],
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
// ============================================================================
|
|
1286
|
+
// PUBLISH HAILER APP TOOL
|
|
1287
|
+
// ============================================================================
|
|
1288
|
+
const publishHailerAppDescription = `Publish Hailer app to production - Builds and uploads to Hailer hosting
|
|
1289
|
+
|
|
1290
|
+
⚠️ **MANDATORY: YOU MUST LOAD SKILL FIRST**
|
|
1291
|
+
**BEFORE calling this tool, you are REQUIRED to:**
|
|
1292
|
+
1. Load the skill: \`get_skill({ skillName: "publish-hailer-app-skill" })\`
|
|
1293
|
+
2. Read the complete workflow and prerequisites
|
|
1294
|
+
3. Review troubleshooting tips and common issues
|
|
1295
|
+
**FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
|
|
1296
|
+
|
|
1297
|
+
**Required Parameters**:
|
|
1298
|
+
- email: Hailer user email
|
|
1299
|
+
- password: User password
|
|
1300
|
+
|
|
1301
|
+
**Project Directory**:
|
|
1302
|
+
- Uses DEV_APPS_PATH from .env.local if set
|
|
1303
|
+
- Falls back to projectDirectory parameter
|
|
1304
|
+
- Falls back to current directory if neither set
|
|
1305
|
+
|
|
1306
|
+
**Prerequisites**: Published app entry exists (create_app with empty URL), manifest.json has appId
|
|
1307
|
+
**Process**: Builds bundle → authenticates → uploads files`;
|
|
1308
|
+
exports.publishHailerAppTool = {
|
|
1309
|
+
name: 'publish_hailer_app',
|
|
1310
|
+
group: tool_registry_1.ToolGroup.PLAYGROUND,
|
|
1311
|
+
description: publishHailerAppDescription,
|
|
1312
|
+
schema: zod_1.z.object({
|
|
1313
|
+
email: zod_1.z.string().email().describe("Hailer user email for authentication"),
|
|
1314
|
+
password: zod_1.z.string().min(1).describe("Hailer user password"),
|
|
1315
|
+
projectDirectory: zod_1.z.string().optional().describe("Path to app project (defaults to current directory)"),
|
|
1316
|
+
appId: zod_1.z.string().optional().describe("App ID to publish to (reads from manifest.json if not provided)")
|
|
1317
|
+
}),
|
|
1318
|
+
async execute(args, _context) {
|
|
1319
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1320
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1321
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
1322
|
+
try {
|
|
1323
|
+
const projectDir = args.projectDirectory || config_1.environment.DEV_APPS_PATH || process.cwd();
|
|
1324
|
+
// Check if directory exists
|
|
1325
|
+
if (!fs.existsSync(projectDir)) {
|
|
1326
|
+
return {
|
|
1327
|
+
content: [{
|
|
1328
|
+
type: "text",
|
|
1329
|
+
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`,
|
|
1330
|
+
}],
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
// Check if package.json exists
|
|
1334
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
1335
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1336
|
+
return {
|
|
1337
|
+
content: [{
|
|
1338
|
+
type: "text",
|
|
1339
|
+
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.`,
|
|
1340
|
+
}],
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
// Read manifest.json to get/verify appId
|
|
1344
|
+
const manifestPath = path.join(projectDir, 'public', 'manifest.json');
|
|
1345
|
+
let appId = args.appId;
|
|
1346
|
+
let appName = 'Unknown';
|
|
1347
|
+
if (fs.existsSync(manifestPath)) {
|
|
1348
|
+
try {
|
|
1349
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
1350
|
+
const manifest = JSON.parse(manifestContent);
|
|
1351
|
+
if (!appId) {
|
|
1352
|
+
appId = manifest.appId;
|
|
1353
|
+
}
|
|
1354
|
+
appName = manifest.name || appName;
|
|
1355
|
+
if (!appId) {
|
|
1356
|
+
return {
|
|
1357
|
+
content: [{
|
|
1358
|
+
type: "text",
|
|
1359
|
+
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.`,
|
|
1360
|
+
}],
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
catch (error) {
|
|
1365
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1366
|
+
return {
|
|
1367
|
+
content: [{
|
|
1368
|
+
type: "text",
|
|
1369
|
+
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`,
|
|
1370
|
+
}],
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
else if (!appId) {
|
|
1375
|
+
return {
|
|
1376
|
+
content: [{
|
|
1377
|
+
type: "text",
|
|
1378
|
+
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.`,
|
|
1379
|
+
}],
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
let responseText = `🚀 **Publishing Hailer App**\n\n`;
|
|
1383
|
+
responseText += `**App Name:** ${appName}\n`;
|
|
1384
|
+
responseText += `**App ID:** \`${appId}\`\n`;
|
|
1385
|
+
responseText += `**Project:** ${projectDir}\n\n`;
|
|
1386
|
+
// Check if publish-production script exists
|
|
1387
|
+
try {
|
|
1388
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
1389
|
+
if (!packageJson.scripts?.['publish-production']) {
|
|
1390
|
+
return {
|
|
1391
|
+
content: [{
|
|
1392
|
+
type: "text",
|
|
1393
|
+
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\`\`\``,
|
|
1394
|
+
}],
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
catch (error) {
|
|
1399
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1400
|
+
return {
|
|
1401
|
+
content: [{
|
|
1402
|
+
type: "text",
|
|
1403
|
+
text: `❌ **Invalid package.json**\n\n${errorMessage}`,
|
|
1404
|
+
}],
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
// Run publish command with email and password
|
|
1408
|
+
responseText += `⏳ Building and publishing app...\n\n`;
|
|
1409
|
+
try {
|
|
1410
|
+
const publishCmd = `EMAIL=${args.email} npm run publish-production`;
|
|
1411
|
+
// Execute with password via stdin
|
|
1412
|
+
const result = execSync(publishCmd, {
|
|
1413
|
+
cwd: projectDir,
|
|
1414
|
+
input: args.password + '\n',
|
|
1415
|
+
encoding: 'utf-8',
|
|
1416
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
1417
|
+
});
|
|
1418
|
+
responseText += `✅ **App Published Successfully!**\n\n`;
|
|
1419
|
+
// Try to parse output for useful info
|
|
1420
|
+
if (result) {
|
|
1421
|
+
responseText += `\`\`\`\n${result.trim()}\n\`\`\`\n\n`;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
const err = error;
|
|
1426
|
+
const errorOutput = err.stderr || err.stdout || err.message || String(error);
|
|
1427
|
+
// Check for common errors
|
|
1428
|
+
if (errorOutput.includes('Authentication failed') || errorOutput.includes('Invalid credentials')) {
|
|
1429
|
+
return {
|
|
1430
|
+
content: [{
|
|
1431
|
+
type: "text",
|
|
1432
|
+
text: `❌ **Authentication Failed**\n\nInvalid email or password.\n\n**Check:**\n- Email is correct\n- Password is correct\n- Account has access to this workspace`,
|
|
1433
|
+
}],
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
if (errorOutput.includes('Permission denied') || errorOutput.includes('not authorized')) {
|
|
1437
|
+
return {
|
|
1438
|
+
content: [{
|
|
1439
|
+
type: "text",
|
|
1440
|
+
text: `❌ **Permission Denied**\n\nYou don't have permission to publish this app.\n\n**Requirements:**\n- Must be app creator or workspace admin\n- Must have access to the workspace`,
|
|
1441
|
+
}],
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
if (errorOutput.includes('App not found') || errorOutput.includes('Invalid app')) {
|
|
1445
|
+
return {
|
|
1446
|
+
content: [{
|
|
1447
|
+
type: "text",
|
|
1448
|
+
text: `❌ **App Not Found**\n\nApp ID \`${appId}\` not found in Hailer.\n\n**Steps to fix:**\n1. Verify app exists: use \`list_apps\` tool\n2. Check appId in manifest.json is correct\n3. Ensure you created a published app entry (with empty URL)`,
|
|
1449
|
+
}],
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
return {
|
|
1453
|
+
content: [{
|
|
1454
|
+
type: "text",
|
|
1455
|
+
text: `❌ **Publish Failed**\n\n\`\`\`\n${errorOutput}\n\`\`\`\n\n**Common Issues:**\n- Build errors (check app code)\n- Network connectivity issues\n- Invalid appId in manifest.json\n- Missing dependencies (run npm install)`,
|
|
1456
|
+
}],
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
responseText += `## 📋 Next Steps\n\n`;
|
|
1460
|
+
responseText += `1. **Open published app in Hailer** (not the dev version)\n`;
|
|
1461
|
+
responseText += `2. **Verify functionality** - test all features work\n`;
|
|
1462
|
+
responseText += `3. **Share with users** - use \`add_app_member\` tool:\n`;
|
|
1463
|
+
responseText += ` \`\`\`javascript\n`;
|
|
1464
|
+
responseText += ` add_app_member({\n`;
|
|
1465
|
+
responseText += ` appId: "${appId}",\n`;
|
|
1466
|
+
responseText += ` member: "network_<workspace-id>" // or team_*, user_*\n`;
|
|
1467
|
+
responseText += ` })\n`;
|
|
1468
|
+
responseText += ` \`\`\`\n\n`;
|
|
1469
|
+
responseText += `**Note:** Published files have replaced the previous version of this app.`;
|
|
1470
|
+
return {
|
|
1471
|
+
content: [{
|
|
1472
|
+
type: "text",
|
|
1473
|
+
text: responseText,
|
|
1474
|
+
}],
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
catch (error) {
|
|
1478
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1479
|
+
return {
|
|
1480
|
+
content: [{
|
|
1481
|
+
type: "text",
|
|
1482
|
+
text: `❌ **Error publishing Hailer app**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- Not in a Hailer app directory\n- Missing package.json or manifest.json\n- Network connectivity issues\n- Build failures`,
|
|
1483
|
+
}],
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
//# sourceMappingURL=app.js.map
|