hosting_deployJsApplication
Upload a JavaScript application archive to a hosting server for automatic deployment. The archive must contain source files only, excluding node_modules and .gitignore matches.
Instructions
Deploy a JavaScript application from an archive file to a hosting server. IMPORTANT: the archive must ONLY contain application source files, not the build output, skip node_modules directory; also exclude all files matched by .gitignore if the ignore file exists. The build process will be triggered automatically on the server after the archive is uploaded. After deployment, use the hosting_listJsDeployments tool to check deployment status and track build progress.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | Domain name associated with the hosting account (e.g., example.com) | |
| archivePath | Yes | Absolute or relative path to the application archive file. Supported formats: zip, tar, tar.gz, tgz, 7z, gz, gzip. If user provides directory path, create archive from it before proceeding. IMPORTANT: the archive must ONLY contain application source files, not the build output, skip node_modules directory. | |
| removeArchive | No | Whether to remove the archive file after successful deployment (default: false) |
Implementation Reference
- src/core/tools/hosting.ts:122-155 (schema)Tool definition and input schema for hosting_deployJsApplication, including the name, description, and inputSchema with domain (string), archivePath (string), and removeArchive (boolean) parameters. Maps to handlerMethod: handleJavascriptApplicationDeploy.
{ "name": "hosting_deployJsApplication", "topic": "hosting", "description": "Deploy a JavaScript application from an archive file to a hosting server. IMPORTANT: the archive must ONLY contain application source files, not the build output, skip node_modules directory; also exclude all files matched by .gitignore if the ignore file exists. The build process will be triggered automatically on the server after the archive is uploaded. After deployment, use the hosting_listJsDeployments tool to check deployment status and track build progress.", "method": "", "path": "", "inputSchema": { "type": "object", "properties": { "domain": { "type": "string", "description": "Domain name associated with the hosting account (e.g., example.com)" }, "archivePath": { "type": "string", "description": "Absolute or relative path to the application archive file. Supported formats: zip, tar, tar.gz, tgz, 7z, gz, gzip. If user provides directory path, create archive from it before proceeding. IMPORTANT: the archive must ONLY contain application source files, not the build output, skip node_modules directory." }, "removeArchive": { "type": "boolean", "description": "Whether to remove the archive file after successful deployment (default: false)" } }, "required": [ "domain", "archivePath" ] }, "security": [], "custom": true, "templateFile": "deploy-javascript-app.template.js", "templateFileTS": "deploy-javascript-app.template.ts", "handlerMethod": "handleJavascriptApplicationDeploy", "group": "hosting" }, - src/core/runtime.ts:197-216 (registration)Registration/case statement that routes 'hosting_deployJsApplication' tool calls to the handleJavascriptApplicationDeploy method.
private async executeCustomTool(tool: OpenApiTool, params: Record<string, any>): Promise<any> { switch (tool.name) { case 'hosting_importWordpressWebsite': return await this.handleWordpressWebsiteImport(params); case 'hosting_deployWordpressPlugin': return await this.handleWordpressPluginDeploy(params); case 'hosting_deployWordpressTheme': return await this.handleWordpressThemeDeploy(params); case 'hosting_deployJsApplication': return await this.handleJavascriptApplicationDeploy(params); case 'hosting_deployStaticWebsite': return await this.handleStaticWebsiteDeploy(params); case 'hosting_listJsDeployments': return await this.handleListJavascriptDeployments(params); case 'hosting_showJsDeploymentLogs': return await this.handleShowJsDeploymentLogs(params); default: throw new Error(`Unknown custom tool: ${tool.name}`); } } - src/core/runtime.ts:1348-1475 (handler)Main handler function (handleJavascriptApplicationDeploy) that orchestrates the full JS app deployment: validates params, resolves username, uploads archive, fetches build settings, triggers build, and optionally removes archive.
private async handleJavascriptApplicationDeploy(params: Record<string, any>): Promise<any> { const { domain, archivePath, removeArchive = false } = params; this.hosting_deployJsApplication_validateRequiredParams(params); this.hosting_deployJsApplication_validateArchiveFile(archivePath); // Auto-resolve username from domain this.log('info', `Resolving username from domain: ${domain}`); const username = await this.resolveUsername(domain); // Upload archive file this.log('info', `Starting archive upload for ${domain}`); let uploadCredentials: any; try { uploadCredentials = await this.fetchUploadCredentials(username, domain); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to fetch upload credentials: ${errorMessage}`); } const { url: uploadUrl, auth_key: authToken, rest_auth_key: authRestToken } = uploadCredentials; if (!uploadUrl || !authToken || !authRestToken) { throw new Error('Invalid upload credentials received from API'); } const archiveBasename = path.basename(archivePath); let uploadResult: any; try { const stats = fs.statSync(archivePath); uploadResult = await this.uploadFile( archivePath, archiveBasename, uploadUrl, authRestToken, authToken ); this.log('info', `Successfully uploaded archive: ${archiveBasename}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to upload archive: ${errorMessage}`); } // Fetch build settings let buildSettings: any; try { this.log('info', `Fetching build settings for ${domain}`); buildSettings = await this.hosting_deployJsApplication_fetchBuildSettings(username, domain, archivePath); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.log('error', `Failed to fetch build settings: ${errorMessage}`); const archiveRemoved = this.hosting_deployJsApplication_removeArchive(archivePath, removeArchive); return { upload: { status: 'success', data: { filename: uploadResult.filename } }, resolveSettings: { status: 'error', error: errorMessage }, build: { status: 'skipped' }, removeArchive: { status: archiveRemoved ? 'success' : 'skipped' } }; } // Trigger build let buildResult: any; try { this.log('info', `Triggering build for ${domain}`); buildResult = await this.hosting_deployJsApplication_triggerBuild(username, domain, archivePath, buildSettings); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.log('error', `Failed to trigger build: ${errorMessage}`); const archiveRemoved = this.hosting_deployJsApplication_removeArchive(archivePath, removeArchive); return { upload: { status: 'success', data: { filename: uploadResult.filename } }, resolveSettings: { status: 'success', data: buildSettings }, build: { status: 'error', error: errorMessage }, removeArchive: { status: archiveRemoved ? 'success' : 'skipped' } }; } const archiveRemoved = this.hosting_deployJsApplication_removeArchive(archivePath, removeArchive); return { upload: { status: 'success', data: { filename: uploadResult.filename } }, resolveSettings: { status: 'success', data: buildSettings }, build: { status: 'success', data: buildResult }, removeArchive: { status: archiveRemoved ? 'success' : 'skipped' } }; } - src/core/runtime.ts:1175-1186 (helper)Helper to validate archive file format (zip, tar, tar.gz, tgz, 7z, gz, gzip).
private hosting_deployJsApplication_validateArchiveFormat(filePath: string): boolean { const validExtensions = ['zip', 'tar', 'tar.gz', 'tgz', '7z', 'gz', 'gzip']; const fileName = path.basename(filePath).toLowerCase(); for (const ext of validExtensions) { if (fileName.endsWith(`.${ext}`)) { return true; } } return false; } - src/core/runtime.ts:1188-1202 (helper)Helper to validate required parameters (domain, archivePath) for the JS deploy tool.
private hosting_deployJsApplication_validateRequiredParams(params: Record<string, any>): void { const { domain, archivePath, removeArchive } = params; if (!domain || typeof domain !== 'string') { throw new Error('domain is required and must be a string'); } if (!archivePath || typeof archivePath !== 'string') { throw new Error('archivePath is required and must be a string'); } if (removeArchive !== undefined && typeof removeArchive !== 'boolean') { throw new Error('removeArchive must be a boolean if provided'); } }