@ibm/ixora 0.1.1 → 0.1.3
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/chunk-4XGJZZ73.js +442 -0
- package/dist/chunk-VCQRSQ4F.js +368 -0
- package/dist/index.js +257 -993
- package/dist/restart-7ECL2NPC.js +8 -0
- package/dist/systems-KIQ3KFX3.js +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/systems.ts
|
|
4
|
+
import {
|
|
5
|
+
readFileSync as readFileSync2,
|
|
6
|
+
writeFileSync as writeFileSync2,
|
|
7
|
+
existsSync as existsSync2,
|
|
8
|
+
mkdirSync as mkdirSync2,
|
|
9
|
+
chmodSync as chmodSync2
|
|
10
|
+
} from "fs";
|
|
11
|
+
import { dirname as dirname2 } from "path";
|
|
12
|
+
|
|
13
|
+
// src/lib/constants.ts
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
var SCRIPT_VERSION = "0.1.3";
|
|
17
|
+
var HEALTH_TIMEOUT = 30;
|
|
18
|
+
var IXORA_DIR = join(homedir(), ".ixora");
|
|
19
|
+
var COMPOSE_FILE = join(IXORA_DIR, "docker-compose.yml");
|
|
20
|
+
var SYSTEMS_CONFIG = join(IXORA_DIR, "ixora-systems.yaml");
|
|
21
|
+
var ENV_FILE = join(IXORA_DIR, ".env");
|
|
22
|
+
var PROFILES = {
|
|
23
|
+
full: {
|
|
24
|
+
name: "full",
|
|
25
|
+
label: "Full",
|
|
26
|
+
description: "All agents, teams, and workflows (3 agents, 2 teams, 1 workflow)"
|
|
27
|
+
},
|
|
28
|
+
"sql-services": {
|
|
29
|
+
name: "sql-services",
|
|
30
|
+
label: "SQL Services",
|
|
31
|
+
description: "SQL Services agent for database queries and performance monitoring"
|
|
32
|
+
},
|
|
33
|
+
security: {
|
|
34
|
+
name: "security",
|
|
35
|
+
label: "Security",
|
|
36
|
+
description: "Security agent, multi-system security team, and assessment workflow"
|
|
37
|
+
},
|
|
38
|
+
knowledge: {
|
|
39
|
+
name: "knowledge",
|
|
40
|
+
label: "Knowledge",
|
|
41
|
+
description: "Knowledge agent only \u2014 documentation retrieval (lightest)"
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var VALID_PROFILES = Object.keys(PROFILES);
|
|
45
|
+
var PROVIDERS = {
|
|
46
|
+
anthropic: {
|
|
47
|
+
name: "anthropic",
|
|
48
|
+
label: "Anthropic",
|
|
49
|
+
agentModel: "anthropic:claude-sonnet-4-6",
|
|
50
|
+
teamModel: "anthropic:claude-haiku-4-5",
|
|
51
|
+
apiKeyVar: "ANTHROPIC_API_KEY",
|
|
52
|
+
description: "Claude Sonnet 4.6 / Haiku 4.5 (recommended)"
|
|
53
|
+
},
|
|
54
|
+
openai: {
|
|
55
|
+
name: "openai",
|
|
56
|
+
label: "OpenAI",
|
|
57
|
+
agentModel: "openai:gpt-4o",
|
|
58
|
+
teamModel: "openai:gpt-4o-mini",
|
|
59
|
+
apiKeyVar: "OPENAI_API_KEY",
|
|
60
|
+
description: "GPT-4o / GPT-4o-mini"
|
|
61
|
+
},
|
|
62
|
+
google: {
|
|
63
|
+
name: "google",
|
|
64
|
+
label: "Google",
|
|
65
|
+
agentModel: "google:gemini-2.5-pro",
|
|
66
|
+
teamModel: "google:gemini-2.5-flash",
|
|
67
|
+
apiKeyVar: "GOOGLE_API_KEY",
|
|
68
|
+
description: "Gemini 2.5 Pro / Gemini 2.5 Flash"
|
|
69
|
+
},
|
|
70
|
+
ollama: {
|
|
71
|
+
name: "ollama",
|
|
72
|
+
label: "Ollama",
|
|
73
|
+
agentModel: "ollama:llama3.1",
|
|
74
|
+
teamModel: "ollama:llama3.1",
|
|
75
|
+
apiKeyVar: "",
|
|
76
|
+
description: "Local models via Ollama (no API key needed)"
|
|
77
|
+
},
|
|
78
|
+
custom: {
|
|
79
|
+
name: "custom",
|
|
80
|
+
label: "Custom",
|
|
81
|
+
agentModel: "",
|
|
82
|
+
teamModel: "",
|
|
83
|
+
apiKeyVar: "",
|
|
84
|
+
description: "Enter provider:model strings"
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var ALL_AGENTS = [
|
|
88
|
+
"ibmi-security-assistant",
|
|
89
|
+
"ibmi-system-health",
|
|
90
|
+
"ibmi-db-explorer",
|
|
91
|
+
"ibmi-db-performance",
|
|
92
|
+
"ibmi-work-management",
|
|
93
|
+
"ibmi-system-config",
|
|
94
|
+
"ibmi-sql-service-guide",
|
|
95
|
+
"ibmi-knowledge-agent"
|
|
96
|
+
];
|
|
97
|
+
var OPS_AGENTS = [
|
|
98
|
+
"ibmi-system-health",
|
|
99
|
+
"ibmi-db-explorer",
|
|
100
|
+
"ibmi-db-performance",
|
|
101
|
+
"ibmi-work-management",
|
|
102
|
+
"ibmi-system-config",
|
|
103
|
+
"ibmi-sql-service-guide"
|
|
104
|
+
];
|
|
105
|
+
var AGENT_PRESETS = {
|
|
106
|
+
all: [...ALL_AGENTS],
|
|
107
|
+
"security-ops": ["ibmi-security-assistant", ...OPS_AGENTS],
|
|
108
|
+
security: ["ibmi-security-assistant"],
|
|
109
|
+
operations: [...OPS_AGENTS],
|
|
110
|
+
knowledge: ["ibmi-knowledge-agent"]
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/lib/env.ts
|
|
114
|
+
import {
|
|
115
|
+
readFileSync,
|
|
116
|
+
writeFileSync,
|
|
117
|
+
existsSync,
|
|
118
|
+
mkdirSync,
|
|
119
|
+
chmodSync
|
|
120
|
+
} from "fs";
|
|
121
|
+
import { dirname } from "path";
|
|
122
|
+
function sqEscape(value) {
|
|
123
|
+
return value.replace(/'/g, "'\\''");
|
|
124
|
+
}
|
|
125
|
+
function envGet(key, envFile = ENV_FILE) {
|
|
126
|
+
if (!existsSync(envFile)) return "";
|
|
127
|
+
const content = readFileSync(envFile, "utf-8");
|
|
128
|
+
for (const line of content.split("\n")) {
|
|
129
|
+
if (line.startsWith(`${key}=`)) {
|
|
130
|
+
let val = line.slice(key.length + 1);
|
|
131
|
+
if (val.startsWith("'") && val.endsWith("'") || val.startsWith('"') && val.endsWith('"')) {
|
|
132
|
+
val = val.slice(1, -1);
|
|
133
|
+
}
|
|
134
|
+
return val;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
var KNOWN_KEYS = [
|
|
140
|
+
"ANTHROPIC_API_KEY",
|
|
141
|
+
"OPENAI_API_KEY",
|
|
142
|
+
"GOOGLE_API_KEY",
|
|
143
|
+
"OLLAMA_HOST",
|
|
144
|
+
"DB2i_HOST",
|
|
145
|
+
"DB2i_USER",
|
|
146
|
+
"DB2i_PASS",
|
|
147
|
+
"DB2_PORT",
|
|
148
|
+
"IXORA_PROFILE",
|
|
149
|
+
"IXORA_VERSION",
|
|
150
|
+
"IXORA_AGENT_MODEL",
|
|
151
|
+
"IXORA_TEAM_MODEL"
|
|
152
|
+
];
|
|
153
|
+
function writeEnvFile(config, envFile = ENV_FILE) {
|
|
154
|
+
mkdirSync(dirname(envFile), { recursive: true });
|
|
155
|
+
let extra = "";
|
|
156
|
+
if (existsSync(envFile)) {
|
|
157
|
+
const existing = readFileSync(envFile, "utf-8");
|
|
158
|
+
const extraLines = existing.split("\n").filter((line) => {
|
|
159
|
+
const trimmed = line.trim();
|
|
160
|
+
if (!trimmed || trimmed.startsWith("#")) return false;
|
|
161
|
+
const lineKey = trimmed.split("=")[0];
|
|
162
|
+
return !KNOWN_KEYS.includes(lineKey);
|
|
163
|
+
});
|
|
164
|
+
extra = extraLines.join("\n");
|
|
165
|
+
}
|
|
166
|
+
let content = `# Model provider
|
|
167
|
+
IXORA_AGENT_MODEL='${sqEscape(config.agentModel)}'
|
|
168
|
+
IXORA_TEAM_MODEL='${sqEscape(config.teamModel)}'
|
|
169
|
+
`;
|
|
170
|
+
if (config.apiKeyVar && config.apiKeyValue) {
|
|
171
|
+
content += `${config.apiKeyVar}='${sqEscape(config.apiKeyValue)}'
|
|
172
|
+
`;
|
|
173
|
+
}
|
|
174
|
+
if (config.ollamaHost) {
|
|
175
|
+
content += `OLLAMA_HOST='${sqEscape(config.ollamaHost)}'
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
content += `
|
|
179
|
+
# IBM i connection
|
|
180
|
+
DB2i_HOST='${sqEscape(config.db2Host)}'
|
|
181
|
+
DB2i_USER='${sqEscape(config.db2User)}'
|
|
182
|
+
DB2i_PASS='${sqEscape(config.db2Pass)}'
|
|
183
|
+
DB2_PORT='${sqEscape(config.db2Port)}'
|
|
184
|
+
|
|
185
|
+
# Deployment
|
|
186
|
+
IXORA_PROFILE='${sqEscape(config.profile)}'
|
|
187
|
+
IXORA_VERSION='${sqEscape(config.version)}'
|
|
188
|
+
`;
|
|
189
|
+
if (extra) {
|
|
190
|
+
content += `
|
|
191
|
+
# Preserved user settings
|
|
192
|
+
${extra}
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
writeFileSync(envFile, content, "utf-8");
|
|
196
|
+
chmodSync(envFile, 384);
|
|
197
|
+
}
|
|
198
|
+
function updateEnvKey(key, value, envFile = ENV_FILE) {
|
|
199
|
+
const escaped = sqEscape(value);
|
|
200
|
+
if (existsSync(envFile)) {
|
|
201
|
+
const content = readFileSync(envFile, "utf-8");
|
|
202
|
+
const lines = content.split("\n");
|
|
203
|
+
let found = false;
|
|
204
|
+
const updated = lines.map((line) => {
|
|
205
|
+
if (line.startsWith(`${key}=`)) {
|
|
206
|
+
found = true;
|
|
207
|
+
return `${key}='${escaped}'`;
|
|
208
|
+
}
|
|
209
|
+
return line;
|
|
210
|
+
});
|
|
211
|
+
if (found) {
|
|
212
|
+
writeFileSync(envFile, updated.join("\n"), "utf-8");
|
|
213
|
+
} else {
|
|
214
|
+
const existing = readFileSync(envFile, "utf-8");
|
|
215
|
+
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
216
|
+
writeFileSync(
|
|
217
|
+
envFile,
|
|
218
|
+
`${existing}${suffix}${key}='${escaped}'
|
|
219
|
+
`,
|
|
220
|
+
"utf-8"
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
mkdirSync(dirname(envFile), { recursive: true });
|
|
225
|
+
writeFileSync(envFile, `${key}='${escaped}'
|
|
226
|
+
`, "utf-8");
|
|
227
|
+
}
|
|
228
|
+
chmodSync(envFile, 384);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/lib/systems.ts
|
|
232
|
+
function readSystems(configFile = SYSTEMS_CONFIG) {
|
|
233
|
+
if (!existsSync2(configFile)) return [];
|
|
234
|
+
const content = readFileSync2(configFile, "utf-8");
|
|
235
|
+
const systems = [];
|
|
236
|
+
let current = null;
|
|
237
|
+
for (const line of content.split("\n")) {
|
|
238
|
+
const idMatch = line.match(/^ {2}- id: (.+)$/);
|
|
239
|
+
if (idMatch) {
|
|
240
|
+
if (current?.id) {
|
|
241
|
+
systems.push({
|
|
242
|
+
id: current.id,
|
|
243
|
+
name: current.name ?? current.id,
|
|
244
|
+
profile: current.profile ?? "full",
|
|
245
|
+
agents: current.agents ?? []
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
current = { id: idMatch[1] };
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (!current) continue;
|
|
252
|
+
const nameMatch = line.match(/name: *'?([^']*)'?/);
|
|
253
|
+
if (nameMatch) {
|
|
254
|
+
current.name = nameMatch[1];
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const profileMatch = line.match(/profile: *'?([^']*)'?/);
|
|
258
|
+
if (profileMatch) {
|
|
259
|
+
current.profile = profileMatch[1];
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const agentsMatch = line.match(/agents: *\[([^\]]*)\]/);
|
|
263
|
+
if (agentsMatch) {
|
|
264
|
+
current.agents = agentsMatch[1].split(",").map((a) => a.trim()).filter(Boolean);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (current?.id) {
|
|
268
|
+
systems.push({
|
|
269
|
+
id: current.id,
|
|
270
|
+
name: current.name ?? current.id,
|
|
271
|
+
profile: current.profile ?? "full",
|
|
272
|
+
agents: current.agents ?? []
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return systems;
|
|
276
|
+
}
|
|
277
|
+
function systemCount(configFile = SYSTEMS_CONFIG) {
|
|
278
|
+
return readSystems(configFile).length;
|
|
279
|
+
}
|
|
280
|
+
function systemIdExists(id, configFile = SYSTEMS_CONFIG) {
|
|
281
|
+
return readSystems(configFile).some((s) => s.id === id);
|
|
282
|
+
}
|
|
283
|
+
function totalSystemCount(envFile = ENV_FILE, configFile = SYSTEMS_CONFIG) {
|
|
284
|
+
return readSystems(configFile).length;
|
|
285
|
+
}
|
|
286
|
+
function addSystem(system, envFile = ENV_FILE, configFile = SYSTEMS_CONFIG) {
|
|
287
|
+
const idUpper = system.id.toUpperCase().replace(/-/g, "_");
|
|
288
|
+
updateEnvKey(`SYSTEM_${idUpper}_HOST`, system.host, envFile);
|
|
289
|
+
updateEnvKey(`SYSTEM_${idUpper}_PORT`, system.port, envFile);
|
|
290
|
+
updateEnvKey(`SYSTEM_${idUpper}_USER`, system.user, envFile);
|
|
291
|
+
updateEnvKey(`SYSTEM_${idUpper}_PASS`, system.pass, envFile);
|
|
292
|
+
const escapedName = system.name.replace(/'/g, "'\\''");
|
|
293
|
+
const profile = system.profile || "full";
|
|
294
|
+
const agentsList = system.agents.join(", ");
|
|
295
|
+
const entry = ` - id: ${system.id}
|
|
296
|
+
name: '${escapedName}'
|
|
297
|
+
profile: ${profile}
|
|
298
|
+
agents: [${agentsList}]
|
|
299
|
+
`;
|
|
300
|
+
mkdirSync2(dirname2(configFile), { recursive: true });
|
|
301
|
+
if (!existsSync2(configFile) || systemCount(configFile) === 0) {
|
|
302
|
+
const content = `# yaml-language-server: $schema=
|
|
303
|
+
# Ixora Systems Configuration
|
|
304
|
+
# Manage with: ixora system add|remove|list
|
|
305
|
+
# Credentials stored in .env (SYSTEM_<ID>_USER, SYSTEM_<ID>_PASS)
|
|
306
|
+
systems:
|
|
307
|
+
${entry}`;
|
|
308
|
+
writeFileSync2(configFile, content, "utf-8");
|
|
309
|
+
} else {
|
|
310
|
+
const existing = readFileSync2(configFile, "utf-8");
|
|
311
|
+
writeFileSync2(configFile, `${existing}${entry}`, "utf-8");
|
|
312
|
+
}
|
|
313
|
+
chmodSync2(configFile, 384);
|
|
314
|
+
}
|
|
315
|
+
function removeSystem(id, envFile = ENV_FILE, configFile = SYSTEMS_CONFIG) {
|
|
316
|
+
if (!existsSync2(configFile)) {
|
|
317
|
+
throw new Error("No systems configured");
|
|
318
|
+
}
|
|
319
|
+
if (!systemIdExists(id, configFile)) {
|
|
320
|
+
throw new Error(`System '${id}' not found`);
|
|
321
|
+
}
|
|
322
|
+
const content = readFileSync2(configFile, "utf-8");
|
|
323
|
+
const lines = content.split("\n");
|
|
324
|
+
const output = [];
|
|
325
|
+
let skip = false;
|
|
326
|
+
for (const line of lines) {
|
|
327
|
+
if (line === ` - id: ${id}`) {
|
|
328
|
+
skip = true;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (line.match(/^ {2}- id: /)) {
|
|
332
|
+
skip = false;
|
|
333
|
+
}
|
|
334
|
+
if (!skip) {
|
|
335
|
+
output.push(line);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
writeFileSync2(configFile, output.join("\n"), "utf-8");
|
|
339
|
+
chmodSync2(configFile, 384);
|
|
340
|
+
if (existsSync2(envFile)) {
|
|
341
|
+
const idUpper = id.toUpperCase().replace(/-/g, "_");
|
|
342
|
+
const envContent = readFileSync2(envFile, "utf-8");
|
|
343
|
+
const filtered = envContent.split("\n").filter((line) => !line.startsWith(`SYSTEM_${idUpper}_`)).join("\n");
|
|
344
|
+
writeFileSync2(envFile, filtered, "utf-8");
|
|
345
|
+
chmodSync2(envFile, 384);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export {
|
|
350
|
+
SCRIPT_VERSION,
|
|
351
|
+
HEALTH_TIMEOUT,
|
|
352
|
+
IXORA_DIR,
|
|
353
|
+
COMPOSE_FILE,
|
|
354
|
+
SYSTEMS_CONFIG,
|
|
355
|
+
ENV_FILE,
|
|
356
|
+
PROFILES,
|
|
357
|
+
VALID_PROFILES,
|
|
358
|
+
PROVIDERS,
|
|
359
|
+
envGet,
|
|
360
|
+
writeEnvFile,
|
|
361
|
+
updateEnvKey,
|
|
362
|
+
readSystems,
|
|
363
|
+
systemCount,
|
|
364
|
+
systemIdExists,
|
|
365
|
+
totalSystemCount,
|
|
366
|
+
addSystem,
|
|
367
|
+
removeSystem
|
|
368
|
+
};
|