openkitt 0.1.2
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/README.md +236 -0
- package/dist/ast/engine.d.ts +26 -0
- package/dist/ast/engine.js +130 -0
- package/dist/ast/engine.js.map +1 -0
- package/dist/ast/operations.d.ts +13 -0
- package/dist/ast/operations.js +329 -0
- package/dist/ast/operations.js.map +1 -0
- package/dist/ast/validator.d.ts +19 -0
- package/dist/ast/validator.js +281 -0
- package/dist/ast/validator.js.map +1 -0
- package/dist/audit/logger.d.ts +14 -0
- package/dist/audit/logger.js +102 -0
- package/dist/audit/logger.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +401 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/create-confirm.d.ts +13 -0
- package/dist/commands/create-confirm.js +38 -0
- package/dist/commands/create-confirm.js.map +1 -0
- package/dist/commands/create-infra.d.ts +20 -0
- package/dist/commands/create-infra.js +63 -0
- package/dist/commands/create-infra.js.map +1 -0
- package/dist/commands/create-manifest.d.ts +10 -0
- package/dist/commands/create-manifest.js +21 -0
- package/dist/commands/create-manifest.js.map +1 -0
- package/dist/commands/create-packages.d.ts +17 -0
- package/dist/commands/create-packages.js +69 -0
- package/dist/commands/create-packages.js.map +1 -0
- package/dist/commands/create-pipeline.d.ts +15 -0
- package/dist/commands/create-pipeline.js +57 -0
- package/dist/commands/create-pipeline.js.map +1 -0
- package/dist/commands/create-scaffolding.d.ts +12 -0
- package/dist/commands/create-scaffolding.js +137 -0
- package/dist/commands/create-scaffolding.js.map +1 -0
- package/dist/commands/create-staging.d.ts +13 -0
- package/dist/commands/create-staging.js +17 -0
- package/dist/commands/create-staging.js.map +1 -0
- package/dist/commands/create-transforms.d.ts +10 -0
- package/dist/commands/create-transforms.js +33 -0
- package/dist/commands/create-transforms.js.map +1 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.js +155 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/delete.d.ts +2 -0
- package/dist/commands/delete.js +83 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/deploy.d.ts +5 -0
- package/dist/commands/deploy.js +371 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/domain.d.ts +2 -0
- package/dist/commands/domain.js +90 -0
- package/dist/commands/domain.js.map +1 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +153 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.js +107 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +217 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +142 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +235 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +90 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +113 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/run.d.ts +14 -0
- package/dist/commands/run.js +196 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/settings.d.ts +3 -0
- package/dist/commands/settings.js +278 -0
- package/dist/commands/settings.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +88 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +242 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/credentials/config.d.ts +12 -0
- package/dist/credentials/config.js +196 -0
- package/dist/credentials/config.js.map +1 -0
- package/dist/credentials/encryption.d.ts +9 -0
- package/dist/credentials/encryption.js +100 -0
- package/dist/credentials/encryption.js.map +1 -0
- package/dist/credentials/keychain.d.ts +8 -0
- package/dist/credentials/keychain.js +236 -0
- package/dist/credentials/keychain.js.map +1 -0
- package/dist/credentials/railway.d.ts +9 -0
- package/dist/credentials/railway.js +69 -0
- package/dist/credentials/railway.js.map +1 -0
- package/dist/llm/client.d.ts +59 -0
- package/dist/llm/client.js +530 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/operations.d.ts +39 -0
- package/dist/llm/operations.js +131 -0
- package/dist/llm/operations.js.map +1 -0
- package/dist/llm/rate-limiter.d.ts +20 -0
- package/dist/llm/rate-limiter.js +57 -0
- package/dist/llm/rate-limiter.js.map +1 -0
- package/dist/llm/scaffolding.d.ts +51 -0
- package/dist/llm/scaffolding.js +118 -0
- package/dist/llm/scaffolding.js.map +1 -0
- package/dist/manifest/drift.d.ts +6 -0
- package/dist/manifest/drift.js +45 -0
- package/dist/manifest/drift.js.map +1 -0
- package/dist/manifest/reader.d.ts +12 -0
- package/dist/manifest/reader.js +99 -0
- package/dist/manifest/reader.js.map +1 -0
- package/dist/manifest/types.d.ts +31 -0
- package/dist/manifest/types.js +2 -0
- package/dist/manifest/types.js.map +1 -0
- package/dist/manifest/writer.d.ts +9 -0
- package/dist/manifest/writer.js +142 -0
- package/dist/manifest/writer.js.map +1 -0
- package/dist/mcp/client.d.ts +21 -0
- package/dist/mcp/client.js +213 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/lifecycle.d.ts +12 -0
- package/dist/mcp/lifecycle.js +149 -0
- package/dist/mcp/lifecycle.js.map +1 -0
- package/dist/mcp/project-guard.d.ts +14 -0
- package/dist/mcp/project-guard.js +27 -0
- package/dist/mcp/project-guard.js.map +1 -0
- package/dist/prompts/operations.d.ts +1 -0
- package/dist/prompts/operations.js +160 -0
- package/dist/prompts/operations.js.map +1 -0
- package/dist/prompts/scaffolding.d.ts +1 -0
- package/dist/prompts/scaffolding.js +331 -0
- package/dist/prompts/scaffolding.js.map +1 -0
- package/dist/prompts/version.d.ts +2 -0
- package/dist/prompts/version.js +3 -0
- package/dist/prompts/version.js.map +1 -0
- package/dist/sandbox/command-allowlist.d.ts +6 -0
- package/dist/sandbox/command-allowlist.js +103 -0
- package/dist/sandbox/command-allowlist.js.map +1 -0
- package/dist/sandbox/package-allowlist.d.ts +6 -0
- package/dist/sandbox/package-allowlist.js +49 -0
- package/dist/sandbox/package-allowlist.js.map +1 -0
- package/dist/sandbox/scanner.d.ts +25 -0
- package/dist/sandbox/scanner.js +196 -0
- package/dist/sandbox/scanner.js.map +1 -0
- package/dist/sandbox/staging.d.ts +12 -0
- package/dist/sandbox/staging.js +107 -0
- package/dist/sandbox/staging.js.map +1 -0
- package/dist/scaffold/frameworks/express.d.ts +3 -0
- package/dist/scaffold/frameworks/express.js +242 -0
- package/dist/scaffold/frameworks/express.js.map +1 -0
- package/dist/scaffold/frameworks/hono.d.ts +3 -0
- package/dist/scaffold/frameworks/hono.js +238 -0
- package/dist/scaffold/frameworks/hono.js.map +1 -0
- package/dist/scaffold/frameworks/nextjs.d.ts +3 -0
- package/dist/scaffold/frameworks/nextjs.js +447 -0
- package/dist/scaffold/frameworks/nextjs.js.map +1 -0
- package/dist/scaffold/frameworks/tanstack-start.d.ts +3 -0
- package/dist/scaffold/frameworks/tanstack-start.js +280 -0
- package/dist/scaffold/frameworks/tanstack-start.js.map +1 -0
- package/dist/scaffold/index.d.ts +4 -0
- package/dist/scaffold/index.js +138 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/integrations/backend.d.ts +3 -0
- package/dist/scaffold/integrations/backend.js +403 -0
- package/dist/scaffold/integrations/backend.js.map +1 -0
- package/dist/scaffold/integrations/posthog.d.ts +2 -0
- package/dist/scaffold/integrations/posthog.js +21 -0
- package/dist/scaffold/integrations/posthog.js.map +1 -0
- package/dist/scaffold/integrations/sentry.d.ts +2 -0
- package/dist/scaffold/integrations/sentry.js +16 -0
- package/dist/scaffold/integrations/sentry.js.map +1 -0
- package/dist/scaffold/integrations/storybook.d.ts +2 -0
- package/dist/scaffold/integrations/storybook.js +79 -0
- package/dist/scaffold/integrations/storybook.js.map +1 -0
- package/dist/scaffold/integrations/vitest.d.ts +2 -0
- package/dist/scaffold/integrations/vitest.js +37 -0
- package/dist/scaffold/integrations/vitest.js.map +1 -0
- package/dist/scaffold/package-builder.d.ts +3 -0
- package/dist/scaffold/package-builder.js +136 -0
- package/dist/scaffold/package-builder.js.map +1 -0
- package/dist/scaffold/packages.d.ts +4 -0
- package/dist/scaffold/packages.js +144 -0
- package/dist/scaffold/packages.js.map +1 -0
- package/dist/scaffold/types.d.ts +20 -0
- package/dist/scaffold/types.js +2 -0
- package/dist/scaffold/types.js.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/auth-guard.d.ts +5 -0
- package/dist/utils/auth-guard.js +52 -0
- package/dist/utils/auth-guard.js.map +1 -0
- package/dist/utils/cleanup.d.ts +2 -0
- package/dist/utils/cleanup.js +37 -0
- package/dist/utils/cleanup.js.map +1 -0
- package/dist/utils/constraints.d.ts +11 -0
- package/dist/utils/constraints.js +162 -0
- package/dist/utils/constraints.js.map +1 -0
- package/dist/utils/display.d.ts +32 -0
- package/dist/utils/display.js +103 -0
- package/dist/utils/display.js.map +1 -0
- package/dist/utils/global-config.d.ts +8 -0
- package/dist/utils/global-config.js +39 -0
- package/dist/utils/global-config.js.map +1 -0
- package/dist/utils/pm.d.ts +4 -0
- package/dist/utils/pm.js +40 -0
- package/dist/utils/pm.js.map +1 -0
- package/dist/utils/prerequisites.d.ts +9 -0
- package/dist/utils/prerequisites.js +96 -0
- package/dist/utils/prerequisites.js.map +1 -0
- package/dist/utils/prompts.d.ts +25 -0
- package/dist/utils/prompts.js +148 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/validation.d.ts +9 -0
- package/dist/utils/validation.js +38 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/versions/compat.d.ts +13 -0
- package/dist/versions/compat.js +127 -0
- package/dist/versions/compat.js.map +1 -0
- package/dist/versions/integrity.d.ts +9 -0
- package/dist/versions/integrity.js +60 -0
- package/dist/versions/integrity.js.map +1 -0
- package/dist/versions/parser.d.ts +13 -0
- package/dist/versions/parser.js +106 -0
- package/dist/versions/parser.js.map +1 -0
- package/dist/versions/registry.d.ts +30 -0
- package/dist/versions/registry.js +165 -0
- package/dist/versions/registry.js.map +1 -0
- package/package.json +58 -0
- package/scripts/publish.sh +254 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { getManifestPath } from "./reader.js";
|
|
5
|
+
const DIR_MODE = 0o700;
|
|
6
|
+
const FILE_MODE = 0o600;
|
|
7
|
+
const MANIFEST_VERSION = "0.1.0";
|
|
8
|
+
function isWindows() {
|
|
9
|
+
return platform() === "win32";
|
|
10
|
+
}
|
|
11
|
+
function ensureKittDir(manifestPath) {
|
|
12
|
+
const kittDir = dirname(manifestPath);
|
|
13
|
+
if (!existsSync(kittDir)) {
|
|
14
|
+
mkdirSync(kittDir, { recursive: true, mode: DIR_MODE });
|
|
15
|
+
}
|
|
16
|
+
if (!isWindows()) {
|
|
17
|
+
chmodSync(kittDir, DIR_MODE);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function writeManifest(workspaceDir, manifest) {
|
|
21
|
+
const manifestPath = getManifestPath(workspaceDir);
|
|
22
|
+
const manifestDir = dirname(manifestPath);
|
|
23
|
+
const tempFile = join(manifestDir, `manifest.json.tmp-${process.pid}-${Date.now()}`);
|
|
24
|
+
const payload = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
25
|
+
ensureKittDir(manifestPath);
|
|
26
|
+
try {
|
|
27
|
+
writeFileSync(tempFile, payload, { encoding: "utf-8", mode: FILE_MODE });
|
|
28
|
+
if (!isWindows()) {
|
|
29
|
+
chmodSync(tempFile, FILE_MODE);
|
|
30
|
+
}
|
|
31
|
+
renameSync(tempFile, manifestPath);
|
|
32
|
+
if (!isWindows()) {
|
|
33
|
+
chmodSync(manifestPath, FILE_MODE);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (existsSync(tempFile)) {
|
|
38
|
+
unlinkSync(tempFile);
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function createInitialManifest(workspaceName, packageManager, railway) {
|
|
44
|
+
return {
|
|
45
|
+
version: MANIFEST_VERSION,
|
|
46
|
+
workspace: {
|
|
47
|
+
name: workspaceName,
|
|
48
|
+
packageManager,
|
|
49
|
+
...(railway ? { railway } : {}),
|
|
50
|
+
},
|
|
51
|
+
settings: {},
|
|
52
|
+
apps: {},
|
|
53
|
+
packages: {},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function addApp(manifest, name, app) {
|
|
57
|
+
return {
|
|
58
|
+
...manifest,
|
|
59
|
+
apps: {
|
|
60
|
+
...manifest.apps,
|
|
61
|
+
[name]: app,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function removeApp(manifest, name) {
|
|
66
|
+
const nextApps = { ...manifest.apps };
|
|
67
|
+
delete nextApps[name];
|
|
68
|
+
const nextPackages = {};
|
|
69
|
+
for (const [packageName, pkg] of Object.entries(manifest.packages)) {
|
|
70
|
+
nextPackages[packageName] = {
|
|
71
|
+
...pkg,
|
|
72
|
+
consumers: pkg.consumers.filter((consumer) => consumer !== name),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
...manifest,
|
|
77
|
+
apps: nextApps,
|
|
78
|
+
packages: nextPackages,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function addPackage(manifest, name, pkg) {
|
|
82
|
+
return {
|
|
83
|
+
...manifest,
|
|
84
|
+
packages: {
|
|
85
|
+
...manifest.packages,
|
|
86
|
+
[name]: pkg,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function addConsumer(manifest, packageName, appName) {
|
|
91
|
+
const pkg = manifest.packages[packageName];
|
|
92
|
+
if (!pkg) {
|
|
93
|
+
return {
|
|
94
|
+
...manifest,
|
|
95
|
+
packages: {
|
|
96
|
+
...manifest.packages,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const consumers = pkg.consumers.includes(appName) ? [...pkg.consumers] : [...pkg.consumers, appName];
|
|
101
|
+
return {
|
|
102
|
+
...manifest,
|
|
103
|
+
packages: {
|
|
104
|
+
...manifest.packages,
|
|
105
|
+
[packageName]: {
|
|
106
|
+
...pkg,
|
|
107
|
+
consumers,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function removeConsumer(manifest, packageName, appName) {
|
|
113
|
+
const pkg = manifest.packages[packageName];
|
|
114
|
+
if (!pkg) {
|
|
115
|
+
return {
|
|
116
|
+
...manifest,
|
|
117
|
+
packages: {
|
|
118
|
+
...manifest.packages,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
...manifest,
|
|
124
|
+
packages: {
|
|
125
|
+
...manifest.packages,
|
|
126
|
+
[packageName]: {
|
|
127
|
+
...pkg,
|
|
128
|
+
consumers: pkg.consumers.filter((consumer) => consumer !== appName),
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export function updateSettings(manifest, key, value) {
|
|
134
|
+
return {
|
|
135
|
+
...manifest,
|
|
136
|
+
settings: {
|
|
137
|
+
...manifest.settings,
|
|
138
|
+
[key]: value,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/manifest/writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServerProcess } from './lifecycle.js';
|
|
2
|
+
export interface McpTool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface McpToolResult {
|
|
8
|
+
content: Array<{
|
|
9
|
+
type: string;
|
|
10
|
+
text: string;
|
|
11
|
+
}>;
|
|
12
|
+
isError?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface McpClient {
|
|
15
|
+
initialize(): Promise<void>;
|
|
16
|
+
listTools(): Promise<McpTool[]>;
|
|
17
|
+
callTool(name: string, args: Record<string, unknown>): Promise<McpToolResult>;
|
|
18
|
+
close(): void;
|
|
19
|
+
isReady(): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function createMcpClient(server: McpServerProcess): Promise<McpClient>;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createAuditLogger } from '../audit/logger.js';
|
|
2
|
+
const JSON_RPC_VERSION = '2.0';
|
|
3
|
+
const MCP_PROTOCOL_VERSION = '2025-11-25';
|
|
4
|
+
const CLIENT_INFO = {
|
|
5
|
+
name: 'openkitt',
|
|
6
|
+
version: '0.1.0',
|
|
7
|
+
};
|
|
8
|
+
const auditLogger = createAuditLogger('.');
|
|
9
|
+
function createJsonRpcError(code, message) {
|
|
10
|
+
const error = new Error(message);
|
|
11
|
+
error.code = code;
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return typeof value === 'object' && value !== null;
|
|
16
|
+
}
|
|
17
|
+
function isJsonRpcResponse(value) {
|
|
18
|
+
if (!isRecord(value)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return value.jsonrpc === JSON_RPC_VERSION && typeof value.id === 'number';
|
|
22
|
+
}
|
|
23
|
+
function isInitializeResult(value) {
|
|
24
|
+
if (!isRecord(value)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return (typeof value.protocolVersion === 'string'
|
|
28
|
+
&& isRecord(value.capabilities)
|
|
29
|
+
&& isRecord(value.serverInfo)
|
|
30
|
+
&& typeof value.serverInfo.name === 'string'
|
|
31
|
+
&& typeof value.serverInfo.version === 'string');
|
|
32
|
+
}
|
|
33
|
+
function isToolsListResult(value) {
|
|
34
|
+
if (!isRecord(value) || !Array.isArray(value.tools)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return value.tools.every((tool) => {
|
|
38
|
+
if (!isRecord(tool)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return (typeof tool.name === 'string'
|
|
42
|
+
&& typeof tool.description === 'string'
|
|
43
|
+
&& isRecord(tool.inputSchema));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function isToolsCallResult(value) {
|
|
47
|
+
if (!isRecord(value) || !Array.isArray(value.content)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const hasValidContent = value.content.every((item) => {
|
|
51
|
+
if (!isRecord(item)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return typeof item.type === 'string' && typeof item.text === 'string';
|
|
55
|
+
});
|
|
56
|
+
if (!hasValidContent) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (value.isError !== undefined && typeof value.isError !== 'boolean') {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
export async function createMcpClient(server) {
|
|
65
|
+
let ready = false;
|
|
66
|
+
let closed = false;
|
|
67
|
+
let nextRequestId = 1;
|
|
68
|
+
let stdoutBuffer = '';
|
|
69
|
+
const pendingRequests = new Map();
|
|
70
|
+
const rejectPending = (error) => {
|
|
71
|
+
for (const pending of pendingRequests.values()) {
|
|
72
|
+
pending.reject(error);
|
|
73
|
+
}
|
|
74
|
+
pendingRequests.clear();
|
|
75
|
+
};
|
|
76
|
+
const handleStdoutData = (chunk) => {
|
|
77
|
+
stdoutBuffer += String(chunk);
|
|
78
|
+
let newlineIndex = stdoutBuffer.indexOf('\n');
|
|
79
|
+
while (newlineIndex >= 0) {
|
|
80
|
+
const line = stdoutBuffer.slice(0, newlineIndex).trim();
|
|
81
|
+
stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1);
|
|
82
|
+
if (line) {
|
|
83
|
+
let message;
|
|
84
|
+
try {
|
|
85
|
+
message = JSON.parse(line);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
newlineIndex = stdoutBuffer.indexOf('\n');
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (isJsonRpcResponse(message)) {
|
|
92
|
+
const pending = pendingRequests.get(message.id);
|
|
93
|
+
if (pending) {
|
|
94
|
+
pendingRequests.delete(message.id);
|
|
95
|
+
if (message.error) {
|
|
96
|
+
pending.reject(createJsonRpcError(message.error.code, message.error.message));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
pending.resolve(message.result);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
newlineIndex = stdoutBuffer.indexOf('\n');
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const handleServerExit = () => {
|
|
108
|
+
if (closed) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
ready = false;
|
|
112
|
+
rejectPending(new Error('MCP server connection closed'));
|
|
113
|
+
};
|
|
114
|
+
server.stdout.on('data', handleStdoutData);
|
|
115
|
+
server.process.on('exit', handleServerExit);
|
|
116
|
+
server.process.on('error', handleServerExit);
|
|
117
|
+
const writeMessage = (message) => {
|
|
118
|
+
if (closed) {
|
|
119
|
+
throw new Error('MCP client is closed');
|
|
120
|
+
}
|
|
121
|
+
server.stdin.write(`${JSON.stringify(message)}\n`);
|
|
122
|
+
};
|
|
123
|
+
const request = async (method, params) => {
|
|
124
|
+
if (closed) {
|
|
125
|
+
throw new Error('MCP client is closed');
|
|
126
|
+
}
|
|
127
|
+
const id = nextRequestId;
|
|
128
|
+
nextRequestId += 1;
|
|
129
|
+
const message = {
|
|
130
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
131
|
+
id,
|
|
132
|
+
method,
|
|
133
|
+
...(params ? { params } : {}),
|
|
134
|
+
};
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
pendingRequests.set(id, { resolve, reject });
|
|
137
|
+
writeMessage(message);
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
const notify = (method, params) => {
|
|
141
|
+
writeMessage({
|
|
142
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
143
|
+
method,
|
|
144
|
+
...(params ? { params } : {}),
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const close = () => {
|
|
148
|
+
if (closed) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
closed = true;
|
|
152
|
+
ready = false;
|
|
153
|
+
server.stdout.off('data', handleStdoutData);
|
|
154
|
+
server.process.off('exit', handleServerExit);
|
|
155
|
+
server.process.off('error', handleServerExit);
|
|
156
|
+
rejectPending(new Error('MCP client closed'));
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
async initialize() {
|
|
160
|
+
if (ready) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const result = await request('initialize', {
|
|
164
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
165
|
+
capabilities: {},
|
|
166
|
+
clientInfo: CLIENT_INFO,
|
|
167
|
+
});
|
|
168
|
+
if (!isInitializeResult(result)) {
|
|
169
|
+
throw new Error('Invalid initialize response from MCP server');
|
|
170
|
+
}
|
|
171
|
+
notify('notifications/initialized');
|
|
172
|
+
ready = true;
|
|
173
|
+
},
|
|
174
|
+
async listTools() {
|
|
175
|
+
if (!ready) {
|
|
176
|
+
throw new Error('MCP client is not initialized');
|
|
177
|
+
}
|
|
178
|
+
const result = await request('tools/list');
|
|
179
|
+
if (!isToolsListResult(result)) {
|
|
180
|
+
throw new Error('Invalid tools/list response from MCP server');
|
|
181
|
+
}
|
|
182
|
+
auditLogger.mcp('tools/list', '{}', 'SUCCESS');
|
|
183
|
+
return result.tools;
|
|
184
|
+
},
|
|
185
|
+
async callTool(name, args) {
|
|
186
|
+
if (!ready) {
|
|
187
|
+
throw new Error('MCP client is not initialized');
|
|
188
|
+
}
|
|
189
|
+
const params = { name, arguments: args };
|
|
190
|
+
const paramsText = JSON.stringify(params);
|
|
191
|
+
try {
|
|
192
|
+
const result = await request('tools/call', params);
|
|
193
|
+
if (!isToolsCallResult(result)) {
|
|
194
|
+
throw new Error('Invalid tools/call response from MCP server');
|
|
195
|
+
}
|
|
196
|
+
const status = result.isError ? 'FAILED' : 'SUCCESS';
|
|
197
|
+
auditLogger.mcp(name, paramsText, status, result.isError ? 'tool returned isError=true' : undefined);
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
catch (requestError) {
|
|
201
|
+
const error = requestError instanceof Error ? requestError : new Error(String(requestError));
|
|
202
|
+
const detail = 'code' in error ? `${String(error.code)}: ${error.message}` : error.message;
|
|
203
|
+
auditLogger.mcp(name, paramsText, 'FAILED', detail);
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
close,
|
|
208
|
+
isReady() {
|
|
209
|
+
return ready;
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mcp/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { Readable, Writable } from 'node:stream';
|
|
3
|
+
export interface McpServerProcess {
|
|
4
|
+
process: ChildProcess;
|
|
5
|
+
stdin: Writable;
|
|
6
|
+
stdout: Readable;
|
|
7
|
+
pid: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function spawnMcpServer(): Promise<McpServerProcess>;
|
|
10
|
+
export declare function getMcpServerProcess(): McpServerProcess | null;
|
|
11
|
+
export declare function isMcpServerRunning(): boolean;
|
|
12
|
+
export declare function shutdownMcpServer(): Promise<void>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { createAuditLogger } from '../audit/logger.js';
|
|
5
|
+
import { error } from '../utils/display.js';
|
|
6
|
+
const MCP_ENTRY_POINT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'node_modules', '@railway', 'mcp-server', 'dist', 'index.js');
|
|
7
|
+
const MCP_START_FAILURE_MESSAGE = 'Railway MCP Server failed to start. Try reinstalling: npm install -g openkitt@latest';
|
|
8
|
+
const SHUTDOWN_TIMEOUT_MS = 5000;
|
|
9
|
+
const auditLogger = createAuditLogger('.');
|
|
10
|
+
let activeServer = null;
|
|
11
|
+
let pendingSpawn = null;
|
|
12
|
+
let shouldLogRespawn = false;
|
|
13
|
+
const intentionallyShuttingDown = new WeakSet();
|
|
14
|
+
function formatExitDetail(code, signal) {
|
|
15
|
+
const codeLabel = code === null ? 'null' : String(code);
|
|
16
|
+
const signalLabel = signal ?? 'null';
|
|
17
|
+
return `code=${codeLabel}, signal=${signalLabel}`;
|
|
18
|
+
}
|
|
19
|
+
function isChildRunning(child) {
|
|
20
|
+
return child.exitCode === null && !child.killed;
|
|
21
|
+
}
|
|
22
|
+
function buildServerProcess(child) {
|
|
23
|
+
if (!child.stdin || !child.stdout) {
|
|
24
|
+
throw new Error('MCP server stdio was not created');
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
process: child,
|
|
28
|
+
stdin: child.stdin,
|
|
29
|
+
stdout: child.stdout,
|
|
30
|
+
pid: child.pid ?? -1,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function attachLifecycleListeners(child) {
|
|
34
|
+
child.stderr?.on('data', (chunk) => {
|
|
35
|
+
const message = String(chunk).trim();
|
|
36
|
+
if (!message) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
auditLogger.mcp('server-lifecycle', 'stderr', 'FAILED', message);
|
|
40
|
+
});
|
|
41
|
+
child.on('exit', (code, signal) => {
|
|
42
|
+
const detail = formatExitDetail(code, signal);
|
|
43
|
+
if (activeServer?.process === child) {
|
|
44
|
+
activeServer = null;
|
|
45
|
+
}
|
|
46
|
+
if (intentionallyShuttingDown.has(child)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
shouldLogRespawn = true;
|
|
50
|
+
auditLogger.mcp('server-lifecycle', 'crash', 'FAILED', detail);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function displaySpawnFailure(reason) {
|
|
54
|
+
error(MCP_START_FAILURE_MESSAGE);
|
|
55
|
+
auditLogger.mcp('server-lifecycle', 'spawn', 'FAILED', reason);
|
|
56
|
+
}
|
|
57
|
+
export async function spawnMcpServer() {
|
|
58
|
+
if (activeServer && isChildRunning(activeServer.process)) {
|
|
59
|
+
return activeServer;
|
|
60
|
+
}
|
|
61
|
+
if (pendingSpawn) {
|
|
62
|
+
return pendingSpawn;
|
|
63
|
+
}
|
|
64
|
+
const respawn = shouldLogRespawn;
|
|
65
|
+
const child = spawn('node', [MCP_ENTRY_POINT], {
|
|
66
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
67
|
+
});
|
|
68
|
+
attachLifecycleListeners(child);
|
|
69
|
+
pendingSpawn = new Promise((resolve, reject) => {
|
|
70
|
+
const handleError = (spawnError) => {
|
|
71
|
+
child.off('spawn', handleSpawn);
|
|
72
|
+
if (activeServer?.process === child) {
|
|
73
|
+
activeServer = null;
|
|
74
|
+
}
|
|
75
|
+
displaySpawnFailure(spawnError.message);
|
|
76
|
+
reject(spawnError);
|
|
77
|
+
};
|
|
78
|
+
const handleSpawn = () => {
|
|
79
|
+
child.off('error', handleError);
|
|
80
|
+
let serverProcess;
|
|
81
|
+
try {
|
|
82
|
+
serverProcess = buildServerProcess(child);
|
|
83
|
+
}
|
|
84
|
+
catch (buildError) {
|
|
85
|
+
const err = buildError instanceof Error ? buildError : new Error(String(buildError));
|
|
86
|
+
displaySpawnFailure(err.message);
|
|
87
|
+
reject(err);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
activeServer = serverProcess;
|
|
91
|
+
shouldLogRespawn = false;
|
|
92
|
+
if (respawn) {
|
|
93
|
+
auditLogger.mcp('server-lifecycle', 'respawn', 'SUCCESS', `pid=${serverProcess.pid}`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
auditLogger.mcp('server-lifecycle', 'spawn', 'SUCCESS', `pid=${serverProcess.pid}`);
|
|
97
|
+
}
|
|
98
|
+
resolve(serverProcess);
|
|
99
|
+
};
|
|
100
|
+
child.once('error', handleError);
|
|
101
|
+
child.once('spawn', handleSpawn);
|
|
102
|
+
}).finally(() => {
|
|
103
|
+
pendingSpawn = null;
|
|
104
|
+
});
|
|
105
|
+
return pendingSpawn;
|
|
106
|
+
}
|
|
107
|
+
export function getMcpServerProcess() {
|
|
108
|
+
if (!activeServer) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!isChildRunning(activeServer.process)) {
|
|
112
|
+
activeServer = null;
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return activeServer;
|
|
116
|
+
}
|
|
117
|
+
export function isMcpServerRunning() {
|
|
118
|
+
return getMcpServerProcess() !== null;
|
|
119
|
+
}
|
|
120
|
+
export async function shutdownMcpServer() {
|
|
121
|
+
if (!activeServer) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const child = activeServer.process;
|
|
125
|
+
intentionallyShuttingDown.add(child);
|
|
126
|
+
if (!isChildRunning(child)) {
|
|
127
|
+
activeServer = null;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const exitPromise = new Promise((resolve) => {
|
|
131
|
+
child.once('exit', (code, signal) => {
|
|
132
|
+
resolve({ code, signal });
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
child.kill('SIGTERM');
|
|
136
|
+
let forcedKill = false;
|
|
137
|
+
const timeoutHandle = setTimeout(() => {
|
|
138
|
+
forcedKill = true;
|
|
139
|
+
child.kill('SIGKILL');
|
|
140
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
141
|
+
const exitResult = await exitPromise;
|
|
142
|
+
clearTimeout(timeoutHandle);
|
|
143
|
+
activeServer = null;
|
|
144
|
+
const detail = forcedKill
|
|
145
|
+
? `forced after timeout (${formatExitDetail(exitResult.code, exitResult.signal)})`
|
|
146
|
+
: formatExitDetail(exitResult.code, exitResult.signal);
|
|
147
|
+
auditLogger.mcp('server-lifecycle', 'shutdown', 'SUCCESS', detail);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/mcp/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { McpClient, McpTool, McpToolResult } from './client.js';
|
|
2
|
+
export interface GuardedMcpClient {
|
|
3
|
+
initialize(): Promise<void>;
|
|
4
|
+
listTools(): Promise<McpTool[]>;
|
|
5
|
+
callTool(name: string, args: Record<string, unknown>): Promise<McpToolResult>;
|
|
6
|
+
close(): void;
|
|
7
|
+
isReady(): boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ProjectGuard {
|
|
10
|
+
guardedCallTool(client: McpClient, name: string, args: Record<string, unknown>): Promise<McpToolResult>;
|
|
11
|
+
validateProjectId(args: Record<string, unknown>): boolean;
|
|
12
|
+
getProjectId(): string;
|
|
13
|
+
}
|
|
14
|
+
export declare function createProjectGuard(projectId: string): ProjectGuard;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createAuditLogger } from '../audit/logger.js';
|
|
2
|
+
export function createProjectGuard(projectId) {
|
|
3
|
+
const auditLogger = createAuditLogger('.');
|
|
4
|
+
const validateProjectId = (args) => {
|
|
5
|
+
if (!Object.prototype.hasOwnProperty.call(args, 'projectId')) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
return args.projectId === projectId;
|
|
9
|
+
};
|
|
10
|
+
const guardedCallTool = async (client, name, args) => {
|
|
11
|
+
if (!validateProjectId(args)) {
|
|
12
|
+
const providedProjectId = String(args.projectId);
|
|
13
|
+
const message = `MCP call blocked: tool "${name}" targets project "${providedProjectId}" but workspace is linked to "${projectId}"`;
|
|
14
|
+
auditLogger.mcp(name, JSON.stringify(args), 'BLOCKED', 'cross-project call rejected');
|
|
15
|
+
throw new Error(message);
|
|
16
|
+
}
|
|
17
|
+
return client.callTool(name, args);
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
guardedCallTool,
|
|
21
|
+
validateProjectId,
|
|
22
|
+
getProjectId() {
|
|
23
|
+
return projectId;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=project-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-guard.js","sourceRoot":"","sources":["../../src/mcp/project-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAqBvD,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAA8B,CAAC;IAExE,MAAM,iBAAiB,GAAG,CAAC,IAA6B,EAAW,EAAE;QACnE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC;IACtC,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,KAAK,EAC3B,MAAiB,EACjB,IAAY,EACZ,IAA6B,EACL,EAAE;QAC1B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,2BAA2B,IAAI,sBAAsB,iBAAiB,iCAAiC,SAAS,GAAG,CAAC;YACpI,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,6BAA6B,CAAC,CAAC;YACtF,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC;IAEF,OAAO;QACL,eAAe;QACf,iBAAiB;QACjB,YAAY;YACV,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const OPERATIONS_SYSTEM_PROMPT: "You are KITT's operations engine. Your job is to execute Railway infrastructure commands by calling MCP tools on behalf of the user. You receive a command context describing what the user wants to do and the current workspace state. You respond by calling the appropriate MCP tools.\n\n<rules>\n1. You may ONLY call MCP tools from the set provided to you. Do not suggest CLI commands, code, file changes, or any action outside MCP tool calls.\n2. Every MCP tool call that targets a Railway project MUST use the projectId from context.workspace.railway.projectId. Never target a different project.\n3. You NEVER see or handle actual secret values. Environment variable values matching secret patterns (SECRET, KEY, TOKEN, PASSWORD, CREDENTIAL, PRIVATE, API_KEY, DATABASE_URL, REDIS_URL, DSN, CONNECTION_STRING) are masked as \"****\" in your context. Do not attempt to guess, infer, or fabricate secret values.\n4. When setting environment variables via set-variables, only set non-secret values directly. For secret values, output a placeholder instruction that KITT will intercept and prompt the user to fill in securely.\n5. For state-changing operations (deploy, set-variables, create-environment, deploy-template), include a \"confirmation\" field in your response summary so KITT knows to prompt the user before executing.\n6. Keep your final text responses concise and structured. Report what you did, what succeeded, and what failed. Do not provide lengthy explanations.\n7. For deploy-template, you may ONLY deploy these approved templates: PostgreSQL, MySQL, Redis, MinIO. Reject any other template request.\n8. When multiple services need the same action (e.g., deploy all apps), call the tool once per service in sequence.\n</rules>\n\n<available_tools>\nYou have access to these Railway MCP tools. They are provided as callable functions via the standard tool-use protocol:\n\ncheck-railway-status\n - Parameters: none\n - Returns: Railway CLI installation status, authentication status, linked account info.\n\nlist-projects\n - Parameters: none\n - Returns: Array of Railway projects with IDs and names.\n\ncreate-project-and-link\n - Parameters: { name: string }\n - Returns: New project ID and link confirmation.\n - Note: State-changing. Requires user confirmation.\n\nlist-services\n - Parameters: { projectId: string }\n - Returns: Array of services with IDs, names, deployment status, and domains.\n\nlink-service\n - Parameters: { serviceId: string }\n - Returns: Link confirmation.\n\ndeploy\n - Parameters: { serviceId: string, environmentId?: string }\n - Returns: Deployment status, URL.\n - Note: State-changing. Requires user confirmation.\n\ndeploy-template\n - Parameters: { templateName: string, projectId: string }\n - Returns: New service info (ID, name, connection URL).\n - Note: State-changing. Requires user confirmation. ONLY these templates are allowed: PostgreSQL, MySQL, Redis, MinIO.\n\ncreate-environment\n - Parameters: { projectId: string, name: string }\n - Returns: New environment ID and name.\n - Note: State-changing. Requires user confirmation.\n\nlink-environment\n - Parameters: { environmentId: string }\n - Returns: Link confirmation.\n\nlist-variables\n - Parameters: { serviceId: string, environmentId?: string }\n - Returns: Object of { variableName: value }. Secret values are masked as \"****\".\n\nset-variables\n - Parameters: { serviceId: string, variables: { [key]: value }, environmentId?: string }\n - Returns: Confirmation of variables set.\n - Note: State-changing. Requires user confirmation.\n\ngenerate-domain\n - Parameters: { serviceId: string }\n - Returns: Generated domain URL.\n - Note: State-changing. Requires user confirmation.\n\nget-logs\n - Parameters: { serviceId: string, lines?: number }\n - Returns: Recent log lines for the service.\n</available_tools>\n\n<command_behaviors>\nBased on context.command, here is how you should orchestrate tool calls:\n\n/init\n 1. Call create-project-and-link with the workspace name.\n 2. Return the project ID for KITT to store in the manifest.\n\n/delete <appName>\n 1. Look up the app's serviceId from context.workspace.apps[appName].railway.serviceId.\n 2. Note: The MCP server does not expose a delete-service tool (destructive operations are excluded). Report to the user that the Railway service must be deleted manually via the Railway dashboard. Provide the service ID.\n\n/deploy\n 1. Call list-services to get current state.\n 2. For each app the user selected, call link-environment if a specific environment was chosen, then call deploy with the app's serviceId.\n 3. After deployment, report the deployment URL for each app.\n\n/deploy:template <templateName>\n 1. Verify the template is in the approved list (PostgreSQL, MySQL, Redis, MinIO).\n 2. Call deploy-template with the template name and project ID.\n 3. Return the new service info (name, connection URL).\n\n/env:create <environmentName>\n 1. Call create-environment with the project ID and name.\n 2. Call link-environment with the new environment ID.\n 3. Call list-variables on the production environment to get existing variables.\n 4. Report the variables available for copying (secrets masked). KITT handles the copy confirmation UX.\n\n/env:vars [serviceName]\n 1. Look up the service ID from the manifest.\n 2. Call list-variables for that service.\n 3. Return the variables (secrets will already be masked).\n\n/env:vars set <serviceName> <key> <value>\n 1. Look up the service ID from the manifest.\n 2. Call set-variables with the key-value pair.\n 3. Confirm the variable was set.\n\n/domain <appName>\n 1. Look up the service ID from the manifest.\n 2. Call generate-domain for that service.\n 3. Return the generated domain.\n\n/logs <appName>\n 1. Look up the service ID from the manifest.\n 2. Call get-logs for that service.\n 3. Return the log output.\n\n/status\n 1. Call check-railway-status.\n 2. Call list-projects.\n 3. Call list-services for the linked project.\n 4. Return a summary: auth status, project info, each service with deployment status and domains.\n</command_behaviors>\n\n<infrastructure_provisioning>\nWhen called during /create to provision infrastructure for a new app, follow these rules:\n\n1. Check context.command.infrastructureNeeded for what to provision.\n2. Call list-services to check if the required infrastructure already exists (e.g., a PostgreSQL service from a previous app).\n3. If the service exists, DO NOT provision a duplicate. Instead, retrieve its connection URL via list-variables and use set-variables to add the connection URL to the new app's service.\n4. If the service does not exist, call deploy-template to provision it, then use set-variables to wire the connection URL to the app's service.\n\nInfrastructure \u2192 environment variable mapping:\n PostgreSQL \u2192 DATABASE_URL\n MySQL \u2192 DATABASE_URL\n Redis \u2192 REDIS_URL\n SQLite \u2192 DATABASE_PATH (no Railway service; volume mounting is handled by KITT outside the LLM)\n</infrastructure_provisioning>\n\n<response_format>\nAfter completing your tool calls, provide a brief text summary structured as:\n\n**Completed:**\n- [what succeeded]\n\n**Warnings:** (if any)\n- [non-fatal issues]\n\n**Failed:** (if any)\n- [what failed and why]\n\n**Action required:** (if any)\n- [things the user needs to do manually]\n</response_format>";
|