@ch4p/cli 0.1.3 → 0.1.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/dist/agent-6WIHK7NM.js +767 -0
- package/dist/agent-ANIZYPPF.js +767 -0
- package/dist/agent-HSAJ5EBN.js +761 -0
- package/dist/agent-N2P2SXGG.js +767 -0
- package/dist/audit-HLOQBMBT.js +12 -0
- package/dist/audit-UIGPH3FK.js +12 -0
- package/dist/canvas-3VTC4XPV.js +313 -0
- package/dist/canvas-4FMNW6FZ.js +313 -0
- package/dist/canvas-HJSLG76B.js +313 -0
- package/dist/canvas-XQHVCY27.js +313 -0
- package/dist/chunk-3XAW4XHG.js +185 -0
- package/dist/chunk-4IRZQCRN.js +1832 -0
- package/dist/chunk-AORLXQHZ.js +304 -0
- package/dist/chunk-BMEBRUYL.js +6995 -0
- package/dist/chunk-G6PJSDEJ.js +4372 -0
- package/dist/chunk-IN2I6XRM.js +185 -0
- package/dist/chunk-PAJOAXLQ.js +4368 -0
- package/dist/chunk-TB4IZ7F7.js +301 -0
- package/dist/chunk-U7S375OS.js +1841 -0
- package/dist/chunk-VJATFD4D.js +7003 -0
- package/dist/dist-37TB6EWP.js +25 -0
- package/dist/dist-CIJPZC2B.js +25 -0
- package/dist/doctor-5M3ZB435.js +274 -0
- package/dist/doctor-IQ3MWQSN.js +274 -0
- package/dist/gateway-DV5OL45G.js +2164 -0
- package/dist/gateway-H4Z2EQK2.js +2165 -0
- package/dist/gateway-LUCG72YX.js +2129 -0
- package/dist/gateway-O3QNSZKF.js +2123 -0
- package/dist/gateway-OJW7RY3H.js +2094 -0
- package/dist/gateway-PBLJEK5I.js +2165 -0
- package/dist/gateway-PHPRQTZP.js +2165 -0
- package/dist/gateway-YKKJ4DZE.js +2115 -0
- package/dist/gateway-Z65DCM2Q.js +2097 -0
- package/dist/gateway-ZSXTAYPF.js +2157 -0
- package/dist/gateway-ZVLF7B4C.js +2165 -0
- package/dist/identity-RHQFPSDS.js +215 -0
- package/dist/identity-VGDDAKBY.js +215 -0
- package/dist/index.js +12 -12
- package/dist/install-6LV7B2SV.js +378 -0
- package/dist/install-NAUPXVCI.js +378 -0
- package/dist/message-PTH4CEOD.js +189 -0
- package/dist/message-QCRZIBTO.js +189 -0
- package/dist/message-TGAPVVI4.js +189 -0
- package/dist/message-YQGIARNE.js +189 -0
- package/dist/onboard-CN56V5P6.js +849 -0
- package/dist/onboard-LJFC6HXD.js +849 -0
- package/dist/pairing-ARWQYATE.js +147 -0
- package/dist/pairing-PXCJMCT2.js +147 -0
- package/dist/skills-4EELFYO2.js +138 -0
- package/dist/skills-KXRTDSF2.js +138 -0
- package/dist/status-2ZJPK3VL.js +94 -0
- package/dist/status-W2OXOSH4.js +94 -0
- package/package.json +24 -24
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
+
import { resolve, join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
var CH4P_DIR_NAME = ".ch4p";
|
|
6
|
+
var CONFIG_FILE_NAME = "config.json";
|
|
7
|
+
var LOGS_DIR_NAME = "logs";
|
|
8
|
+
function getCh4pDir() {
|
|
9
|
+
return resolve(homedir(), CH4P_DIR_NAME);
|
|
10
|
+
}
|
|
11
|
+
function getConfigPath() {
|
|
12
|
+
return join(getCh4pDir(), CONFIG_FILE_NAME);
|
|
13
|
+
}
|
|
14
|
+
function getLogsDir() {
|
|
15
|
+
return join(getCh4pDir(), LOGS_DIR_NAME);
|
|
16
|
+
}
|
|
17
|
+
function getDefaultConfig() {
|
|
18
|
+
return {
|
|
19
|
+
agent: {
|
|
20
|
+
model: "claude-sonnet-4-6",
|
|
21
|
+
provider: "anthropic",
|
|
22
|
+
thinkingLevel: "medium"
|
|
23
|
+
},
|
|
24
|
+
providers: {
|
|
25
|
+
anthropic: {
|
|
26
|
+
apiKey: "${ANTHROPIC_API_KEY}"
|
|
27
|
+
},
|
|
28
|
+
openai: {
|
|
29
|
+
apiKey: "${OPENAI_API_KEY}"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
channels: {},
|
|
33
|
+
memory: {
|
|
34
|
+
backend: "sqlite",
|
|
35
|
+
autoSave: true,
|
|
36
|
+
vectorWeight: 0.7,
|
|
37
|
+
keywordWeight: 0.3
|
|
38
|
+
},
|
|
39
|
+
gateway: {
|
|
40
|
+
port: 18789,
|
|
41
|
+
requirePairing: true,
|
|
42
|
+
allowPublicBind: false
|
|
43
|
+
},
|
|
44
|
+
security: {
|
|
45
|
+
workspaceOnly: true,
|
|
46
|
+
blockedPaths: []
|
|
47
|
+
},
|
|
48
|
+
autonomy: {
|
|
49
|
+
level: "supervised",
|
|
50
|
+
allowedCommands: [
|
|
51
|
+
"git",
|
|
52
|
+
"npm",
|
|
53
|
+
"pnpm",
|
|
54
|
+
"node",
|
|
55
|
+
"npx",
|
|
56
|
+
"cargo",
|
|
57
|
+
"ls",
|
|
58
|
+
"cat",
|
|
59
|
+
"grep",
|
|
60
|
+
"find",
|
|
61
|
+
"wc",
|
|
62
|
+
"sort",
|
|
63
|
+
"head",
|
|
64
|
+
"tail",
|
|
65
|
+
"mkdir",
|
|
66
|
+
"cp",
|
|
67
|
+
"mv",
|
|
68
|
+
"echo",
|
|
69
|
+
"touch"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
engines: {
|
|
73
|
+
default: "native",
|
|
74
|
+
available: {
|
|
75
|
+
native: {
|
|
76
|
+
provider: "anthropic",
|
|
77
|
+
model: "claude-sonnet-4-6"
|
|
78
|
+
},
|
|
79
|
+
"claude-cli": {
|
|
80
|
+
command: "claude",
|
|
81
|
+
timeout: 6e5
|
|
82
|
+
},
|
|
83
|
+
"codex-cli": {
|
|
84
|
+
command: "codex",
|
|
85
|
+
timeout: 6e5
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
tunnel: {
|
|
90
|
+
provider: "none"
|
|
91
|
+
},
|
|
92
|
+
secrets: {
|
|
93
|
+
encrypt: true
|
|
94
|
+
},
|
|
95
|
+
observability: {
|
|
96
|
+
observers: ["console"],
|
|
97
|
+
logLevel: "info"
|
|
98
|
+
},
|
|
99
|
+
skills: {
|
|
100
|
+
enabled: true,
|
|
101
|
+
paths: ["~/.ch4p/skills", ".ch4p/skills", ".agents/skills"],
|
|
102
|
+
autoLoad: true,
|
|
103
|
+
contextBudget: 16e3
|
|
104
|
+
},
|
|
105
|
+
verification: {
|
|
106
|
+
enabled: true,
|
|
107
|
+
semantic: true
|
|
108
|
+
},
|
|
109
|
+
mesh: {
|
|
110
|
+
enabled: false,
|
|
111
|
+
maxConcurrency: 3,
|
|
112
|
+
defaultTimeout: 12e4
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function loadEnvFile() {
|
|
117
|
+
const envPath = join(getCh4pDir(), ".env");
|
|
118
|
+
let raw;
|
|
119
|
+
try {
|
|
120
|
+
raw = readFileSync(envPath, "utf8");
|
|
121
|
+
} catch {
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
let loaded = 0;
|
|
125
|
+
for (const line of raw.split("\n")) {
|
|
126
|
+
const trimmed = line.trim();
|
|
127
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
128
|
+
const stripped = trimmed.startsWith("export ") ? trimmed.slice(7).trim() : trimmed;
|
|
129
|
+
const eqIdx = stripped.indexOf("=");
|
|
130
|
+
if (eqIdx === -1) continue;
|
|
131
|
+
const key = stripped.slice(0, eqIdx).trim();
|
|
132
|
+
let value = stripped.slice(eqIdx + 1).trim();
|
|
133
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
134
|
+
value = value.slice(1, -1);
|
|
135
|
+
}
|
|
136
|
+
if (key && process.env[key] === void 0) {
|
|
137
|
+
process.env[key] = value;
|
|
138
|
+
loaded++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return loaded;
|
|
142
|
+
}
|
|
143
|
+
function resolveEnvVars(obj, missing) {
|
|
144
|
+
if (typeof obj === "string") {
|
|
145
|
+
return obj.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
146
|
+
const value = process.env[varName];
|
|
147
|
+
if (value === void 0) {
|
|
148
|
+
missing?.add(varName);
|
|
149
|
+
}
|
|
150
|
+
return value ?? "";
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (Array.isArray(obj)) {
|
|
154
|
+
return obj.map((item) => resolveEnvVars(item, missing));
|
|
155
|
+
}
|
|
156
|
+
if (obj !== null && typeof obj === "object") {
|
|
157
|
+
const result = {};
|
|
158
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
159
|
+
result[key] = resolveEnvVars(value, missing);
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
return obj;
|
|
164
|
+
}
|
|
165
|
+
function deepMerge(target, source, seen = /* @__PURE__ */ new WeakSet()) {
|
|
166
|
+
if (seen.has(source)) return target;
|
|
167
|
+
seen.add(source);
|
|
168
|
+
const result = { ...target };
|
|
169
|
+
for (const key of Object.keys(source)) {
|
|
170
|
+
const sourceVal = source[key];
|
|
171
|
+
const targetVal = result[key];
|
|
172
|
+
if (sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal !== null && typeof targetVal === "object" && !Array.isArray(targetVal)) {
|
|
173
|
+
result[key] = deepMerge(
|
|
174
|
+
targetVal,
|
|
175
|
+
sourceVal,
|
|
176
|
+
seen
|
|
177
|
+
);
|
|
178
|
+
} else {
|
|
179
|
+
result[key] = sourceVal;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
function validateConfig(config) {
|
|
185
|
+
const errors = [];
|
|
186
|
+
if (!config.agent?.model) {
|
|
187
|
+
errors.push({ field: "agent.model", message: "Model is required" });
|
|
188
|
+
}
|
|
189
|
+
if (!config.agent?.provider) {
|
|
190
|
+
errors.push({ field: "agent.provider", message: "Provider is required" });
|
|
191
|
+
}
|
|
192
|
+
if (config.agent?.maxToolResults != null && (typeof config.agent.maxToolResults !== "number" || config.agent.maxToolResults < 1)) {
|
|
193
|
+
errors.push({ field: "agent.maxToolResults", message: "Must be a positive number" });
|
|
194
|
+
}
|
|
195
|
+
if (config.agent?.maxToolOutputLen != null && (typeof config.agent.maxToolOutputLen !== "number" || config.agent.maxToolOutputLen < 1024)) {
|
|
196
|
+
errors.push({ field: "agent.maxToolOutputLen", message: "Must be at least 1024 bytes" });
|
|
197
|
+
}
|
|
198
|
+
if (config.agent?.maxStateRecords != null && (typeof config.agent.maxStateRecords !== "number" || config.agent.maxStateRecords < 1)) {
|
|
199
|
+
errors.push({ field: "agent.maxStateRecords", message: "Must be a positive number" });
|
|
200
|
+
}
|
|
201
|
+
if (config.agent?.maxSessionErrors != null && (typeof config.agent.maxSessionErrors !== "number" || config.agent.maxSessionErrors < 1)) {
|
|
202
|
+
errors.push({ field: "agent.maxSessionErrors", message: "Must be a positive number" });
|
|
203
|
+
}
|
|
204
|
+
if (config.agent?.runTimeout != null && (typeof config.agent.runTimeout !== "number" || config.agent.runTimeout < 3e4)) {
|
|
205
|
+
errors.push({ field: "agent.runTimeout", message: "Must be at least 30000 (30 seconds)" });
|
|
206
|
+
}
|
|
207
|
+
if (typeof config.gateway?.port !== "number" || config.gateway.port < 1 || config.gateway.port > 65535) {
|
|
208
|
+
errors.push({ field: "gateway.port", message: "Port must be a number between 1 and 65535" });
|
|
209
|
+
}
|
|
210
|
+
if (config.autonomy?.level && !["readonly", "supervised", "full"].includes(config.autonomy.level)) {
|
|
211
|
+
errors.push({ field: "autonomy.level", message: "Must be one of: readonly, supervised, full" });
|
|
212
|
+
}
|
|
213
|
+
if (config.observability?.logLevel && !["debug", "info", "warn", "error"].includes(config.observability.logLevel)) {
|
|
214
|
+
errors.push({ field: "observability.logLevel", message: "Must be one of: debug, info, warn, error" });
|
|
215
|
+
}
|
|
216
|
+
if (config.memory?.backend && !["sqlite", "markdown", "noop"].includes(config.memory.backend)) {
|
|
217
|
+
errors.push({ field: "memory.backend", message: "Must be one of: sqlite, markdown, noop" });
|
|
218
|
+
}
|
|
219
|
+
if (config.tunnel?.provider && !["none", "cloudflare", "tailscale", "ngrok"].includes(config.tunnel.provider)) {
|
|
220
|
+
errors.push({ field: "tunnel.provider", message: "Must be one of: none, cloudflare, tailscale, ngrok" });
|
|
221
|
+
}
|
|
222
|
+
if (config.engines?.default && config.engines.available && !Object.keys(config.engines.available).includes(config.engines.default)) {
|
|
223
|
+
errors.push({
|
|
224
|
+
field: "engines.default",
|
|
225
|
+
message: `Engine "${config.engines.default}" is not defined in engines.available`
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (config.skills?.contextBudget != null && (typeof config.skills.contextBudget !== "number" || config.skills.contextBudget < 0)) {
|
|
229
|
+
errors.push({ field: "skills.contextBudget", message: "Must be a non-negative number" });
|
|
230
|
+
}
|
|
231
|
+
if (config.mesh?.maxConcurrency != null && (typeof config.mesh.maxConcurrency !== "number" || config.mesh.maxConcurrency < 1)) {
|
|
232
|
+
errors.push({ field: "mesh.maxConcurrency", message: "Must be a positive number" });
|
|
233
|
+
}
|
|
234
|
+
if (config.mesh?.defaultTimeout != null && (typeof config.mesh.defaultTimeout !== "number" || config.mesh.defaultTimeout < 1e3)) {
|
|
235
|
+
errors.push({ field: "mesh.defaultTimeout", message: "Must be at least 1000ms" });
|
|
236
|
+
}
|
|
237
|
+
return errors;
|
|
238
|
+
}
|
|
239
|
+
function ensureConfigDir() {
|
|
240
|
+
const ch4pDir = getCh4pDir();
|
|
241
|
+
const logsDir = getLogsDir();
|
|
242
|
+
if (!existsSync(ch4pDir)) {
|
|
243
|
+
mkdirSync(ch4pDir, { recursive: true, mode: 448 });
|
|
244
|
+
}
|
|
245
|
+
if (!existsSync(logsDir)) {
|
|
246
|
+
mkdirSync(logsDir, { recursive: true, mode: 448 });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function loadConfig() {
|
|
250
|
+
loadEnvFile();
|
|
251
|
+
const defaults = getDefaultConfig();
|
|
252
|
+
let merged = defaults;
|
|
253
|
+
const configPath = getConfigPath();
|
|
254
|
+
if (existsSync(configPath)) {
|
|
255
|
+
try {
|
|
256
|
+
const raw = readFileSync(configPath, "utf8");
|
|
257
|
+
const userConfig = JSON.parse(raw);
|
|
258
|
+
merged = deepMerge(defaults, userConfig);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
261
|
+
throw new ConfigLoadError(`Failed to parse ${configPath}: ${message}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const missingVars = /* @__PURE__ */ new Set();
|
|
265
|
+
const resolved = resolveEnvVars(merged, missingVars);
|
|
266
|
+
for (const varName of missingVars) {
|
|
267
|
+
console.warn(` \u26A0 Config references \${${varName}} but it is not set in environment.`);
|
|
268
|
+
}
|
|
269
|
+
const errors = validateConfig(resolved);
|
|
270
|
+
if (errors.length > 0) {
|
|
271
|
+
const details = errors.map((e) => ` - ${e.field}: ${e.message}`).join("\n");
|
|
272
|
+
throw new ConfigLoadError(`Configuration validation failed:
|
|
273
|
+
${details}`);
|
|
274
|
+
}
|
|
275
|
+
return resolved;
|
|
276
|
+
}
|
|
277
|
+
function saveConfig(config) {
|
|
278
|
+
ensureConfigDir();
|
|
279
|
+
const configPath = getConfigPath();
|
|
280
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", {
|
|
281
|
+
encoding: "utf8",
|
|
282
|
+
mode: 384
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function configExists() {
|
|
286
|
+
return existsSync(getConfigPath());
|
|
287
|
+
}
|
|
288
|
+
var ConfigLoadError = class extends Error {
|
|
289
|
+
constructor(message) {
|
|
290
|
+
super(message);
|
|
291
|
+
this.name = "ConfigLoadError";
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export {
|
|
296
|
+
getCh4pDir,
|
|
297
|
+
getConfigPath,
|
|
298
|
+
getLogsDir,
|
|
299
|
+
getDefaultConfig,
|
|
300
|
+
ensureConfigDir,
|
|
301
|
+
loadConfig,
|
|
302
|
+
saveConfig,
|
|
303
|
+
configExists
|
|
304
|
+
};
|