kiroo 0.8.0 → 0.9.5
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 +386 -293
- package/bin/kiroo.js +412 -288
- package/package.json +2 -1
- package/src/analyze.js +568 -0
- package/src/bench.js +11 -4
- package/src/checker.js +26 -9
- package/src/config.js +109 -0
- package/src/deterministic.js +22 -0
- package/src/env.js +31 -3
- package/src/executor.js +18 -1
- package/src/export.js +560 -93
- package/src/formatter.js +18 -6
- package/src/init.js +80 -48
- package/src/lingo.js +55 -36
- package/src/proxy.js +140 -0
- package/src/replay.js +5 -4
- package/src/run.js +246 -0
- package/src/sanitizer.js +100 -0
- package/src/snapshot.js +76 -19
- package/src/stats.js +15 -5
- package/src/storage.js +223 -142
package/src/config.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { stableJSONStringify } from './deterministic.js';
|
|
4
|
+
|
|
5
|
+
const KIROO_DIR = '.kiroo';
|
|
6
|
+
const CONFIG_FILE = join(KIROO_DIR, 'config.json');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_CONFIG_TEMPLATE = {
|
|
9
|
+
projectName: 'my-api-project',
|
|
10
|
+
baseUrl: '',
|
|
11
|
+
settings: {
|
|
12
|
+
determinism: {
|
|
13
|
+
sortKeys: true
|
|
14
|
+
},
|
|
15
|
+
redaction: {
|
|
16
|
+
enabled: true,
|
|
17
|
+
redactedValue: '<REDACTED>'
|
|
18
|
+
},
|
|
19
|
+
analysis: {
|
|
20
|
+
failOnSeverity: 'none',
|
|
21
|
+
provider: 'groq',
|
|
22
|
+
modelPriority: [
|
|
23
|
+
'qwen/qwen3-32b',
|
|
24
|
+
'moonshotai/kimi-k2-instruct-0905',
|
|
25
|
+
'openai/gpt-oss-20b'
|
|
26
|
+
],
|
|
27
|
+
maxCompletionTokens: 900
|
|
28
|
+
},
|
|
29
|
+
export: {
|
|
30
|
+
defaultFormat: 'postman'
|
|
31
|
+
},
|
|
32
|
+
ci: {
|
|
33
|
+
failOnSeverity: 'high'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function cloneDefaultConfig() {
|
|
39
|
+
return JSON.parse(JSON.stringify(DEFAULT_CONFIG_TEMPLATE));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function mergeWithDefaults(defaultObj, currentObj) {
|
|
43
|
+
if (Array.isArray(defaultObj)) {
|
|
44
|
+
if (Array.isArray(currentObj)) return currentObj;
|
|
45
|
+
return [...defaultObj];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (defaultObj && typeof defaultObj === 'object') {
|
|
49
|
+
const merged = {};
|
|
50
|
+
const source = currentObj && typeof currentObj === 'object' ? currentObj : {};
|
|
51
|
+
const keys = new Set([...Object.keys(defaultObj), ...Object.keys(source)]);
|
|
52
|
+
|
|
53
|
+
for (const key of keys) {
|
|
54
|
+
if (key in defaultObj) {
|
|
55
|
+
merged[key] = mergeWithDefaults(defaultObj[key], source[key]);
|
|
56
|
+
} else {
|
|
57
|
+
merged[key] = source[key];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return merged;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return currentObj !== undefined ? currentObj : defaultObj;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ensureConfigFile() {
|
|
67
|
+
if (!existsSync(KIROO_DIR)) {
|
|
68
|
+
mkdirSync(KIROO_DIR);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
72
|
+
const initialConfig = {
|
|
73
|
+
...cloneDefaultConfig(),
|
|
74
|
+
createdAt: new Date().toISOString()
|
|
75
|
+
};
|
|
76
|
+
writeFileSync(CONFIG_FILE, stableJSONStringify(initialConfig));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function loadKirooConfig() {
|
|
81
|
+
ensureConfigFile();
|
|
82
|
+
|
|
83
|
+
const raw = readFileSync(CONFIG_FILE, 'utf8');
|
|
84
|
+
const parsed = JSON.parse(raw);
|
|
85
|
+
const defaults = cloneDefaultConfig();
|
|
86
|
+
const normalized = mergeWithDefaults(defaults, parsed);
|
|
87
|
+
|
|
88
|
+
if (!normalized.createdAt) {
|
|
89
|
+
normalized.createdAt = new Date().toISOString();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const normalizedRaw = stableJSONStringify(normalized);
|
|
93
|
+
if (normalizedRaw !== raw) {
|
|
94
|
+
writeFileSync(CONFIG_FILE, normalizedRaw);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return normalized;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function saveKirooConfig(partialConfig = {}) {
|
|
101
|
+
const current = loadKirooConfig();
|
|
102
|
+
const merged = mergeWithDefaults(current, partialConfig);
|
|
103
|
+
writeFileSync(CONFIG_FILE, stableJSONStringify(merged));
|
|
104
|
+
return merged;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function getDefaultKirooConfig() {
|
|
108
|
+
return cloneDefaultConfig();
|
|
109
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function sortObjectKeysDeep(value) {
|
|
2
|
+
if (Array.isArray(value)) {
|
|
3
|
+
return value.map((item) => sortObjectKeysDeep(item));
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (value && typeof value === 'object') {
|
|
7
|
+
const sorted = {};
|
|
8
|
+
const keys = Object.keys(value).sort((a, b) => a.localeCompare(b));
|
|
9
|
+
for (const key of keys) {
|
|
10
|
+
sorted[key] = sortObjectKeysDeep(value[key]);
|
|
11
|
+
}
|
|
12
|
+
return sorted;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function stableJSONStringify(value, space = 2) {
|
|
19
|
+
return JSON.stringify(sortObjectKeysDeep(value), null, space);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { sortObjectKeysDeep };
|
package/src/env.js
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import Table from 'cli-table3';
|
|
3
3
|
import { loadEnv, saveEnv } from './storage.js';
|
|
4
|
+
import { isSensitiveKey } from './sanitizer.js';
|
|
5
|
+
|
|
6
|
+
function maskEnvValue(key, value) {
|
|
7
|
+
if (!isSensitiveKey(key)) {
|
|
8
|
+
return String(value);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const raw = String(value || '');
|
|
12
|
+
if (raw.length <= 4) {
|
|
13
|
+
return '<REDACTED>';
|
|
14
|
+
}
|
|
15
|
+
return `${raw.slice(0, 2)}***${raw.slice(-2)}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getCurrentEnvVars() {
|
|
19
|
+
const env = loadEnv();
|
|
20
|
+
if (!env.environments[env.current]) {
|
|
21
|
+
env.environments[env.current] = {};
|
|
22
|
+
saveEnv(env);
|
|
23
|
+
}
|
|
24
|
+
return env.environments[env.current];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getEnvVar(key) {
|
|
28
|
+
const vars = getCurrentEnvVars();
|
|
29
|
+
return vars[key];
|
|
30
|
+
}
|
|
4
31
|
|
|
5
32
|
export function listEnv() {
|
|
6
33
|
const env = loadEnv();
|
|
@@ -19,8 +46,8 @@ export function listEnv() {
|
|
|
19
46
|
colWidths: [20, 40]
|
|
20
47
|
});
|
|
21
48
|
|
|
22
|
-
Object.entries(currentVars).forEach(([k, v]) => {
|
|
23
|
-
table.push([chalk.white(k), chalk.gray(
|
|
49
|
+
Object.entries(currentVars).sort(([a], [b]) => a.localeCompare(b)).forEach(([k, v]) => {
|
|
50
|
+
table.push([chalk.white(k), chalk.gray(maskEnvValue(k, v))]);
|
|
24
51
|
});
|
|
25
52
|
console.log(table.toString());
|
|
26
53
|
} else {
|
|
@@ -49,7 +76,8 @@ export function setVar(key, value) {
|
|
|
49
76
|
const env = loadEnv();
|
|
50
77
|
env.environments[env.current][key] = value;
|
|
51
78
|
saveEnv(env);
|
|
52
|
-
|
|
79
|
+
const printValue = isSensitiveKey(key) ? '<REDACTED>' : value;
|
|
80
|
+
console.log(chalk.green(` ✅ Set ${key}=${printValue} in`), chalk.white(env.current));
|
|
53
81
|
}
|
|
54
82
|
|
|
55
83
|
export function deleteVar(key) {
|
package/src/executor.js
CHANGED
|
@@ -177,7 +177,7 @@ export async function executeRequest(method, url, options = {}) {
|
|
|
177
177
|
spinner.succeed(chalk.green(`${response.status} ${response.statusText}`) + chalk.gray(` (${duration}ms)`));
|
|
178
178
|
|
|
179
179
|
// Format and display response
|
|
180
|
-
console.log(formatResponse(response));
|
|
180
|
+
console.log(await formatResponse(response, options.lang));
|
|
181
181
|
|
|
182
182
|
// Handle --save option
|
|
183
183
|
if (options.save) {
|
|
@@ -230,6 +230,23 @@ export async function executeRequest(method, url, options = {}) {
|
|
|
230
230
|
console.error(chalk.red('\n ✗ Error:'), error.message, '\n');
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// Save failed interaction
|
|
234
|
+
await saveInteraction({
|
|
235
|
+
method,
|
|
236
|
+
url,
|
|
237
|
+
headers,
|
|
238
|
+
body,
|
|
239
|
+
response: {
|
|
240
|
+
status: 0,
|
|
241
|
+
statusText: error.code || 'FAILED',
|
|
242
|
+
headers: {},
|
|
243
|
+
data: { error: error.message, code: error.code }
|
|
244
|
+
},
|
|
245
|
+
duration,
|
|
246
|
+
saves: [],
|
|
247
|
+
uses: Array.from(usedKeys)
|
|
248
|
+
});
|
|
249
|
+
|
|
233
250
|
process.exit(1);
|
|
234
251
|
}
|
|
235
252
|
}
|