kodu 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +36 -68
- package/README.md +97 -96
- package/dist/package.json +1 -2
- package/dist/src/app.module.js +0 -8
- package/dist/src/app.module.js.map +1 -1
- package/dist/src/commands/init/init.command.d.ts +2 -9
- package/dist/src/commands/init/init.command.js +15 -241
- package/dist/src/commands/init/init.command.js.map +1 -1
- package/dist/src/commands/pack/pack.command.d.ts +7 -0
- package/dist/src/commands/pack/pack.command.js +59 -3
- package/dist/src/commands/pack/pack.command.js.map +1 -1
- package/dist/src/core/config/config.schema.d.ts +0 -46
- package/dist/src/core/config/config.schema.js +1 -51
- package/dist/src/core/config/config.schema.js.map +1 -1
- package/dist/src/core/config/config.service.js +2 -2
- package/dist/src/core/config/config.service.js.map +1 -1
- package/dist/src/core/config/prompt.service.d.ts +1 -4
- package/dist/src/core/config/prompt.service.js +4 -17
- package/dist/src/core/config/prompt.service.js.map +1 -1
- package/dist/src/shared/constants.d.ts +0 -4
- package/dist/src/shared/constants.js +1 -5
- package/dist/src/shared/constants.js.map +1 -1
- package/dist/src/shared/git/git.module.js +0 -2
- package/dist/src/shared/git/git.module.js.map +1 -1
- package/dist/src/shared/git/git.service.d.ts +0 -8
- package/dist/src/shared/git/git.service.js +2 -34
- package/dist/src/shared/git/git.service.js.map +1 -1
- package/dist/src/shared/tokenizer/tokenizer.module.js +0 -2
- package/dist/src/shared/tokenizer/tokenizer.module.js.map +1 -1
- package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -6
- package/dist/src/shared/tokenizer/tokenizer.service.js +8 -38
- package/dist/src/shared/tokenizer/tokenizer.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/kodu.schema.json +0 -139
- package/package.json +1 -2
- package/src/app.module.ts +0 -8
- package/src/commands/init/init.command.ts +15 -310
- package/src/commands/pack/pack.command.ts +56 -3
- package/src/core/config/config.schema.ts +1 -68
- package/src/core/config/config.service.ts +2 -2
- package/src/core/config/prompt.service.ts +4 -26
- package/src/shared/constants.ts +0 -4
- package/src/shared/git/git.module.ts +0 -2
- package/src/shared/git/git.service.ts +1 -33
- package/src/shared/tokenizer/tokenizer.module.ts +0 -2
- package/src/shared/tokenizer/tokenizer.service.ts +9 -39
- package/.kodu/prompts/.keep +0 -0
- package/.kodu/prompts/commit.md +0 -9
- package/.kodu/prompts/pack.md +0 -7
- package/.kodu/prompts/review-bug.md +0 -6
- package/.kodu/prompts/review-security.md +0 -6
- package/.kodu/prompts/review-style.md +0 -6
- package/.opencode/command/openspec-apply.md +0 -24
- package/.opencode/command/openspec-archive.md +0 -27
- package/.opencode/command/openspec-proposal.md +0 -29
- package/.opencode/skills/kodu-ops/SKILL.md +0 -184
- package/dist/src/commands/commit/commit.command.d.ts +0 -18
- package/dist/src/commands/commit/commit.command.js +0 -149
- package/dist/src/commands/commit/commit.command.js.map +0 -1
- package/dist/src/commands/commit/commit.module.d.ts +0 -2
- package/dist/src/commands/commit/commit.module.js +0 -25
- package/dist/src/commands/commit/commit.module.js.map +0 -1
- package/dist/src/commands/ops/ops.command.d.ts +0 -4
- package/dist/src/commands/ops/ops.command.js +0 -39
- package/dist/src/commands/ops/ops.command.js.map +0 -1
- package/dist/src/commands/ops/ops.module.d.ts +0 -2
- package/dist/src/commands/ops/ops.module.js +0 -33
- package/dist/src/commands/ops/ops.module.js.map +0 -1
- package/dist/src/commands/ops/ops.types.d.ts +0 -13
- package/dist/src/commands/ops/ops.types.js +0 -12
- package/dist/src/commands/ops/ops.types.js.map +0 -1
- package/dist/src/commands/ops/ops.utils.d.ts +0 -13
- package/dist/src/commands/ops/ops.utils.js +0 -121
- package/dist/src/commands/ops/ops.utils.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-env.command.d.ts +0 -24
- package/dist/src/commands/ops/subcommands/ops-env.command.js +0 -156
- package/dist/src/commands/ops/subcommands/ops-env.command.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-routes.command.d.ts +0 -22
- package/dist/src/commands/ops/subcommands/ops-routes.command.js +0 -203
- package/dist/src/commands/ops/subcommands/ops-routes.command.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-service.command.d.ts +0 -22
- package/dist/src/commands/ops/subcommands/ops-service.command.js +0 -169
- package/dist/src/commands/ops/subcommands/ops-service.command.js.map +0 -1
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.d.ts +0 -14
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.js +0 -75
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.js.map +0 -1
- package/dist/src/commands/review/review.command.d.ts +0 -26
- package/dist/src/commands/review/review.command.js +0 -205
- package/dist/src/commands/review/review.command.js.map +0 -1
- package/dist/src/commands/review/review.module.d.ts +0 -2
- package/dist/src/commands/review/review.module.js +0 -26
- package/dist/src/commands/review/review.module.js.map +0 -1
- package/dist/src/core/config/default-prompts.d.ts +0 -9
- package/dist/src/core/config/default-prompts.js +0 -49
- package/dist/src/core/config/default-prompts.js.map +0 -1
- package/dist/src/shared/ai/ai.module.d.ts +0 -2
- package/dist/src/shared/ai/ai.module.js +0 -23
- package/dist/src/shared/ai/ai.module.js.map +0 -1
- package/dist/src/shared/ai/ai.service.d.ts +0 -22
- package/dist/src/shared/ai/ai.service.js +0 -164
- package/dist/src/shared/ai/ai.service.js.map +0 -1
- package/dist/src/shared/ssh/ssh.module.d.ts +0 -2
- package/dist/src/shared/ssh/ssh.module.js +0 -21
- package/dist/src/shared/ssh/ssh.module.js.map +0 -1
- package/dist/src/shared/ssh/ssh.service.d.ts +0 -11
- package/dist/src/shared/ssh/ssh.service.js +0 -53
- package/dist/src/shared/ssh/ssh.service.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/commands/commit/commit.command.ts +0 -139
- package/src/commands/commit/commit.module.ts +0 -12
- package/src/commands/ops/ops.command.ts +0 -30
- package/src/commands/ops/ops.module.ts +0 -20
- package/src/commands/ops/ops.types.ts +0 -24
- package/src/commands/ops/ops.utils.ts +0 -160
- package/src/commands/ops/subcommands/ops-env.command.ts +0 -165
- package/src/commands/ops/subcommands/ops-routes.command.ts +0 -221
- package/src/commands/ops/subcommands/ops-service.command.ts +0 -190
- package/src/commands/ops/subcommands/ops-sysinfo.command.ts +0 -77
- package/src/commands/review/review.command.ts +0 -199
- package/src/commands/review/review.module.ts +0 -13
- package/src/core/config/default-prompts.ts +0 -53
- package/src/shared/ai/ai.module.ts +0 -10
- package/src/shared/ai/ai.service.ts +0 -216
- package/src/shared/ssh/ssh.module.ts +0 -8
- package/src/shared/ssh/ssh.service.ts +0 -61
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_REVIEW_PROMPTS = {
|
|
2
|
-
bug: `You are a strict code reviewer. Response format: concise markdown with bullet points.
|
|
3
|
-
Mode: {mode}. Find potential bugs, logical errors, and regressions.
|
|
4
|
-
Provide a concise list of issues and recommendations. If no critical issues found, state that.
|
|
5
|
-
|
|
6
|
-
Diff:
|
|
7
|
-
{diff}`,
|
|
8
|
-
|
|
9
|
-
style: `You are a strict code reviewer. Response format: concise markdown with bullet points.
|
|
10
|
-
Mode: {mode}. Check readability, consistency, formatting, and naming.
|
|
11
|
-
Provide a concise list of issues and recommendations. If no critical issues found, state that.
|
|
12
|
-
|
|
13
|
-
Diff:
|
|
14
|
-
{diff}`,
|
|
15
|
-
|
|
16
|
-
security: `You are a strict code reviewer. Response format: concise markdown with bullet points.
|
|
17
|
-
Mode: {mode}. Find vulnerabilities, secret leaks, improper permission checks.
|
|
18
|
-
Provide a concise list of issues and recommendations. If no critical issues found, state that.
|
|
19
|
-
|
|
20
|
-
Diff:
|
|
21
|
-
{diff}`,
|
|
22
|
-
} as const;
|
|
23
|
-
|
|
24
|
-
export const DEFAULT_COMMIT_PROMPT = `You generate Conventional Commit messages.
|
|
25
|
-
Rules:
|
|
26
|
-
- Format: <type>(<optional scope>): <subject>
|
|
27
|
-
- Lowercase subject, no trailing period.
|
|
28
|
-
- Keep under 70 characters.
|
|
29
|
-
- Summarize the diff accurately.
|
|
30
|
-
|
|
31
|
-
Diff:
|
|
32
|
-
{diff}`;
|
|
33
|
-
|
|
34
|
-
export const DEFAULT_PACK_PROMPT = `Project context below. Keep instructions concise.
|
|
35
|
-
|
|
36
|
-
Files packed ({{tokenCount}} tokens, ~{{usdEstimate}}):
|
|
37
|
-
{{fileList}}
|
|
38
|
-
|
|
39
|
-
Context:
|
|
40
|
-
{{context}}`;
|
|
41
|
-
|
|
42
|
-
export const STANDARD_REVIEW_MODES = ['bug', 'style', 'security'] as const;
|
|
43
|
-
|
|
44
|
-
export function replacePromptVariables(
|
|
45
|
-
prompt: string,
|
|
46
|
-
variables: Record<string, string>,
|
|
47
|
-
): string {
|
|
48
|
-
let result = prompt;
|
|
49
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
50
|
-
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
|
|
51
|
-
}
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
2
|
-
import { ConfigModule } from '../../core/config/config.module';
|
|
3
|
-
import { AiService } from './ai.service';
|
|
4
|
-
|
|
5
|
-
@Module({
|
|
6
|
-
imports: [ConfigModule],
|
|
7
|
-
providers: [AiService],
|
|
8
|
-
exports: [AiService],
|
|
9
|
-
})
|
|
10
|
-
export class AiModule {}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { Agent } from '@mastra/core/agent';
|
|
2
|
-
import { Injectable } from '@nestjs/common';
|
|
3
|
-
import { ConfigService } from '../../core/config/config.service';
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_COMMIT_PROMPT,
|
|
6
|
-
DEFAULT_REVIEW_PROMPTS,
|
|
7
|
-
replacePromptVariables,
|
|
8
|
-
STANDARD_REVIEW_MODES,
|
|
9
|
-
} from '../../core/config/default-prompts';
|
|
10
|
-
import { PromptService } from '../../core/config/prompt.service';
|
|
11
|
-
import { DEFAULT_COMMIT_TOKENS, DEFAULT_REVIEW_TOKENS } from '../constants';
|
|
12
|
-
|
|
13
|
-
export type ReviewMode = string;
|
|
14
|
-
|
|
15
|
-
type ModelSettings = Record<string, unknown> & {
|
|
16
|
-
maxOutputTokens?: number;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
@Injectable()
|
|
20
|
-
export class AiService {
|
|
21
|
-
constructor(
|
|
22
|
-
private readonly configService: ConfigService,
|
|
23
|
-
private readonly promptService: PromptService,
|
|
24
|
-
) {}
|
|
25
|
-
|
|
26
|
-
async reviewDiff(diff: string, mode: ReviewMode): Promise<{ text: string }> {
|
|
27
|
-
const agent = this.createAgent(
|
|
28
|
-
'kodu-review-agent',
|
|
29
|
-
'AI Reviewer for staged git diff. Be concise.',
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const userPrompt = await this.buildReviewPrompt(diff, mode);
|
|
33
|
-
|
|
34
|
-
const modelSettings = this.getModelSettingsForCommand('review');
|
|
35
|
-
const output = await agent.generate(userPrompt, { modelSettings });
|
|
36
|
-
return { text: output.text.trim() };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async generateCommitMessage(diff: string): Promise<string> {
|
|
40
|
-
const agent = this.createAgent(
|
|
41
|
-
'kodu-commit-agent',
|
|
42
|
-
'Generate a concise Conventional Commit message. Only output the message string.',
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const prompt = await this.buildCommitPrompt(diff);
|
|
46
|
-
const modelSettings = this.getModelSettingsForCommand('commit');
|
|
47
|
-
const output = await agent.generate(prompt, { modelSettings });
|
|
48
|
-
|
|
49
|
-
const raw = output.text.trim();
|
|
50
|
-
const cleaned = this.cleanCommitMessage(raw);
|
|
51
|
-
|
|
52
|
-
if (!cleaned) {
|
|
53
|
-
throw new Error('AI did not return a valid commit message.');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return cleaned;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
getAvailableReviewModes(): string[] {
|
|
60
|
-
const config = this.configService.getConfig();
|
|
61
|
-
const standardModes = [...STANDARD_REVIEW_MODES];
|
|
62
|
-
const customModes = config.prompts?.review
|
|
63
|
-
? Object.keys(config.prompts.review)
|
|
64
|
-
: [];
|
|
65
|
-
|
|
66
|
-
return Array.from(new Set([...standardModes, ...customModes]));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
hasApiKey(): boolean {
|
|
70
|
-
const config = this.configService.getConfig();
|
|
71
|
-
if (!config.llm) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
const envName = config.llm.apiKeyEnv ?? 'OPENAI_API_KEY';
|
|
75
|
-
const value = process.env[envName];
|
|
76
|
-
return Boolean(value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getApiKeyEnvName(): string {
|
|
80
|
-
const config = this.configService.getConfig();
|
|
81
|
-
return config.llm?.apiKeyEnv ?? 'OPENAI_API_KEY';
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
private createAgent(id: string, instructions: string): Agent {
|
|
85
|
-
const apiKey = this.getApiKey();
|
|
86
|
-
const modelId = this.getModelId();
|
|
87
|
-
|
|
88
|
-
return new Agent({
|
|
89
|
-
id,
|
|
90
|
-
name: id,
|
|
91
|
-
instructions,
|
|
92
|
-
model: {
|
|
93
|
-
id: modelId as `${string}/${string}`,
|
|
94
|
-
apiKey,
|
|
95
|
-
},
|
|
96
|
-
maxRetries: 1,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private async buildReviewPrompt(
|
|
101
|
-
diff: string,
|
|
102
|
-
mode: ReviewMode,
|
|
103
|
-
): Promise<string> {
|
|
104
|
-
const config = this.configService.getConfig();
|
|
105
|
-
|
|
106
|
-
const customPrompt = config.prompts?.review?.[mode];
|
|
107
|
-
if (customPrompt) {
|
|
108
|
-
return this.promptService.load(customPrompt, { diff, mode });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
STANDARD_REVIEW_MODES.includes(
|
|
113
|
-
mode as (typeof STANDARD_REVIEW_MODES)[number],
|
|
114
|
-
)
|
|
115
|
-
) {
|
|
116
|
-
const defaultPrompt =
|
|
117
|
-
DEFAULT_REVIEW_PROMPTS[mode as keyof typeof DEFAULT_REVIEW_PROMPTS];
|
|
118
|
-
return replacePromptVariables(defaultPrompt, { diff, mode });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return replacePromptVariables(DEFAULT_REVIEW_PROMPTS.bug, { diff, mode });
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
private async buildCommitPrompt(diff: string): Promise<string> {
|
|
125
|
-
const config = this.configService.getConfig();
|
|
126
|
-
const customPrompt = config.prompts?.commit;
|
|
127
|
-
|
|
128
|
-
if (customPrompt) {
|
|
129
|
-
return this.promptService.load(customPrompt, { diff });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return replacePromptVariables(DEFAULT_COMMIT_PROMPT, { diff });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private getApiKey(): string {
|
|
136
|
-
const config = this.configService.getConfig();
|
|
137
|
-
if (!config.llm) {
|
|
138
|
-
throw new Error(
|
|
139
|
-
'LLM configuration not found. Add llm section to kodu.json',
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
const envName = config.llm.apiKeyEnv ?? 'OPENAI_API_KEY';
|
|
143
|
-
const value = process.env[envName];
|
|
144
|
-
|
|
145
|
-
if (!value) {
|
|
146
|
-
throw new Error(`API key not found: set ${envName} in environment.`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return value;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private getModelId(): string {
|
|
153
|
-
const config = this.configService.getConfig();
|
|
154
|
-
if (!config.llm) {
|
|
155
|
-
throw new Error(
|
|
156
|
-
'LLM configuration not found. Add llm section to kodu.json',
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
const model = config.llm.model;
|
|
160
|
-
|
|
161
|
-
// Model should already be in provider/model format, but validate
|
|
162
|
-
if (!model.includes('/')) {
|
|
163
|
-
throw new Error(
|
|
164
|
-
`Invalid model format: "${model}". Expected format "provider/model-name" (e.g., "openai/gpt-4o")`,
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return model;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
private getModelSettingsForCommand(
|
|
172
|
-
command: 'commit' | 'review',
|
|
173
|
-
): ModelSettings {
|
|
174
|
-
const config = this.configService.getConfig();
|
|
175
|
-
const commands = config.llm?.commands;
|
|
176
|
-
const defaultMax =
|
|
177
|
-
command === 'commit' ? DEFAULT_COMMIT_TOKENS : DEFAULT_REVIEW_TOKENS;
|
|
178
|
-
const base: ModelSettings = { maxOutputTokens: defaultMax };
|
|
179
|
-
|
|
180
|
-
if (!commands) {
|
|
181
|
-
return base;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const override = commands[command];
|
|
185
|
-
const settings = override?.modelSettings as ModelSettings | undefined;
|
|
186
|
-
|
|
187
|
-
if (!settings) {
|
|
188
|
-
return base;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
...settings,
|
|
193
|
-
maxOutputTokens: settings.maxOutputTokens ?? defaultMax,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private cleanCommitMessage(text: string): string {
|
|
198
|
-
const unfenced = text.replace(/^```[a-zA-Z]*\s*/g, '').replace(/```$/g, '');
|
|
199
|
-
const lines = unfenced
|
|
200
|
-
.split('\n')
|
|
201
|
-
.map((line) => line.trim())
|
|
202
|
-
.filter((line) => line.length > 0);
|
|
203
|
-
|
|
204
|
-
if (lines.length === 0) {
|
|
205
|
-
return '';
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const first = lines[0]
|
|
209
|
-
.replace(/^"|"$/g, '')
|
|
210
|
-
.replace(/^'|'$/g, '')
|
|
211
|
-
.replace(/^Commit message:?\s*/i, '')
|
|
212
|
-
.trim();
|
|
213
|
-
|
|
214
|
-
return first;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import { execa } from 'execa';
|
|
3
|
-
import type { ServerConfig } from '../../core/config/config.schema';
|
|
4
|
-
|
|
5
|
-
export type SshResult = {
|
|
6
|
-
success: boolean;
|
|
7
|
-
stdout: string;
|
|
8
|
-
stderr: string;
|
|
9
|
-
exitCode: number;
|
|
10
|
-
error?: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
@Injectable()
|
|
14
|
-
export class SshService {
|
|
15
|
-
async execute(
|
|
16
|
-
serverConfig: ServerConfig,
|
|
17
|
-
command: string,
|
|
18
|
-
): Promise<SshResult> {
|
|
19
|
-
const args = [
|
|
20
|
-
'-i',
|
|
21
|
-
serverConfig.sshKeyPath,
|
|
22
|
-
'-p',
|
|
23
|
-
String(serverConfig.port ?? 22),
|
|
24
|
-
'-o',
|
|
25
|
-
'StrictHostKeyChecking=no',
|
|
26
|
-
'-o',
|
|
27
|
-
'ConnectTimeout=10',
|
|
28
|
-
`${serverConfig.user}@${serverConfig.host}`,
|
|
29
|
-
command,
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const { stdout, stderr, exitCode } = await execa('ssh', args, {
|
|
34
|
-
env: serverConfig.env,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
success: (exitCode ?? 0) === 0,
|
|
39
|
-
stdout,
|
|
40
|
-
stderr,
|
|
41
|
-
exitCode: exitCode ?? 0,
|
|
42
|
-
};
|
|
43
|
-
} catch (error) {
|
|
44
|
-
const failure = error as {
|
|
45
|
-
stdout?: string;
|
|
46
|
-
stderr?: string;
|
|
47
|
-
exitCode?: number;
|
|
48
|
-
shortMessage?: string;
|
|
49
|
-
message?: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
success: false,
|
|
54
|
-
stdout: failure.stdout ?? '',
|
|
55
|
-
stderr: failure.stderr ?? '',
|
|
56
|
-
exitCode: failure.exitCode ?? -1,
|
|
57
|
-
error: failure.shortMessage ?? failure.message ?? 'Unknown SSH error',
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|