@ibm/ixora 0.1.0 → 0.1.1
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/index.js +118 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// src/lib/constants.ts
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
import { join } from "path";
|
|
9
|
-
var SCRIPT_VERSION = "0.1.
|
|
9
|
+
var SCRIPT_VERSION = "0.1.1";
|
|
10
10
|
var HEALTH_TIMEOUT = 30;
|
|
11
11
|
var IXORA_DIR = join(homedir(), ".ixora");
|
|
12
12
|
var COMPOSE_FILE = join(IXORA_DIR, "docker-compose.yml");
|
|
@@ -171,6 +171,7 @@ IXORA_TEAM_MODEL='${sqEscape(config.teamModel)}'
|
|
|
171
171
|
DB2i_HOST='${sqEscape(config.db2Host)}'
|
|
172
172
|
DB2i_USER='${sqEscape(config.db2User)}'
|
|
173
173
|
DB2i_PASS='${sqEscape(config.db2Pass)}'
|
|
174
|
+
DB2_PORT='${sqEscape(config.db2Port)}'
|
|
174
175
|
|
|
175
176
|
# Deployment
|
|
176
177
|
IXORA_PROFILE='${sqEscape(config.profile)}'
|
|
@@ -810,7 +811,7 @@ async function cmdVersion(opts) {
|
|
|
810
811
|
for (const img of images) {
|
|
811
812
|
const tag = img.Tag || "unknown";
|
|
812
813
|
const id = img.ID ? dim(` (${img.ID.slice(0, 12)})`) : "";
|
|
813
|
-
const imageStr =
|
|
814
|
+
const imageStr = `${img.Repository || ""}:${tag}${id}`;
|
|
814
815
|
console.log(
|
|
815
816
|
` ${(img.Service || "").padEnd(22)} ${dim(imageStr)}`
|
|
816
817
|
);
|
|
@@ -1105,6 +1106,43 @@ async function cmdLogs(opts, service) {
|
|
|
1105
1106
|
}
|
|
1106
1107
|
}
|
|
1107
1108
|
|
|
1109
|
+
// src/commands/upgrade.ts
|
|
1110
|
+
import { select } from "@inquirer/prompts";
|
|
1111
|
+
|
|
1112
|
+
// src/lib/registry.ts
|
|
1113
|
+
var GHCR_TOKEN_URL = "https://ghcr.io/token";
|
|
1114
|
+
var GHCR_TAGS_URL = "https://ghcr.io/v2";
|
|
1115
|
+
var RELEASE_TAG = /^v\d+\.\d+\.\d+$/;
|
|
1116
|
+
async function fetchImageTags(image) {
|
|
1117
|
+
const tokenRes = await fetch(
|
|
1118
|
+
`${GHCR_TOKEN_URL}?scope=repository:${image}:pull`
|
|
1119
|
+
);
|
|
1120
|
+
if (!tokenRes.ok) {
|
|
1121
|
+
throw new Error(`Failed to get registry token: ${tokenRes.status}`);
|
|
1122
|
+
}
|
|
1123
|
+
const { token } = await tokenRes.json();
|
|
1124
|
+
const tagsRes = await fetch(`${GHCR_TAGS_URL}/${image}/tags/list`, {
|
|
1125
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1126
|
+
});
|
|
1127
|
+
if (!tagsRes.ok) {
|
|
1128
|
+
throw new Error(`Failed to fetch tags: ${tagsRes.status}`);
|
|
1129
|
+
}
|
|
1130
|
+
const { tags } = await tagsRes.json();
|
|
1131
|
+
return tags.filter((t) => RELEASE_TAG.test(t)).sort((a, b) => compareSemver(b, a));
|
|
1132
|
+
}
|
|
1133
|
+
function compareSemver(a, b) {
|
|
1134
|
+
const pa = a.replace(/^v/, "").split(".").map(Number);
|
|
1135
|
+
const pb = b.replace(/^v/, "").split(".").map(Number);
|
|
1136
|
+
for (let i = 0; i < 3; i++) {
|
|
1137
|
+
if (pa[i] !== pb[i]) return pa[i] - pb[i];
|
|
1138
|
+
}
|
|
1139
|
+
return 0;
|
|
1140
|
+
}
|
|
1141
|
+
function normalizeVersion(version) {
|
|
1142
|
+
const v = version.trim();
|
|
1143
|
+
return v.startsWith("v") ? v : `v${v}`;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1108
1146
|
// src/commands/upgrade.ts
|
|
1109
1147
|
async function cmdUpgrade(opts) {
|
|
1110
1148
|
try {
|
|
@@ -1121,13 +1159,35 @@ async function cmdUpgrade(opts) {
|
|
|
1121
1159
|
}
|
|
1122
1160
|
detectPlatform();
|
|
1123
1161
|
const previousVersion = envGet("IXORA_VERSION") || "latest";
|
|
1124
|
-
|
|
1162
|
+
let targetVersion;
|
|
1163
|
+
const explicitVersion = opts.version || opts.imageVersion;
|
|
1164
|
+
if (explicitVersion) {
|
|
1165
|
+
targetVersion = normalizeVersion(explicitVersion);
|
|
1166
|
+
} else {
|
|
1167
|
+
let tags;
|
|
1168
|
+
try {
|
|
1169
|
+
tags = await fetchImageTags("ibmi-agi/ixora-api");
|
|
1170
|
+
} catch {
|
|
1171
|
+
warn("Could not fetch available versions from registry");
|
|
1172
|
+
die("Specify a version: ixora upgrade <version>");
|
|
1173
|
+
}
|
|
1174
|
+
if (tags.length === 0) {
|
|
1175
|
+
die("No release versions found in registry");
|
|
1176
|
+
}
|
|
1177
|
+
targetVersion = await select({
|
|
1178
|
+
message: "Select version to upgrade to",
|
|
1179
|
+
choices: tags.map((t) => ({
|
|
1180
|
+
value: t,
|
|
1181
|
+
name: t === previousVersion ? `${t} (current)` : t
|
|
1182
|
+
}))
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
info(`Upgrading ixora: ${previousVersion} \u2192 ${targetVersion}`);
|
|
1186
|
+
info("Stopping services...");
|
|
1187
|
+
await runCompose(composeCmd, ["down", "--remove-orphans"]);
|
|
1188
|
+
updateEnvKey("IXORA_VERSION", targetVersion);
|
|
1125
1189
|
writeComposeFile();
|
|
1126
1190
|
success("Wrote docker-compose.yml");
|
|
1127
|
-
if (opts.imageVersion) {
|
|
1128
|
-
info(`Pinning version: ${previousVersion} \u2192 ${opts.imageVersion}`);
|
|
1129
|
-
updateEnvKey("IXORA_VERSION", opts.imageVersion);
|
|
1130
|
-
}
|
|
1131
1191
|
if (opts.profile) {
|
|
1132
1192
|
if (!VALID_PROFILES.includes(opts.profile)) {
|
|
1133
1193
|
die(
|
|
@@ -1138,19 +1198,18 @@ async function cmdUpgrade(opts) {
|
|
|
1138
1198
|
updateEnvKey("IXORA_PROFILE", opts.profile);
|
|
1139
1199
|
}
|
|
1140
1200
|
if (opts.pull !== false) {
|
|
1141
|
-
info("Pulling
|
|
1201
|
+
info("Pulling images...");
|
|
1142
1202
|
await runCompose(composeCmd, ["pull"]);
|
|
1143
1203
|
}
|
|
1144
1204
|
info("Restarting services...");
|
|
1145
1205
|
await runCompose(composeCmd, ["up", "-d"]);
|
|
1146
1206
|
await waitForHealthy(composeCmd);
|
|
1147
|
-
const newVersion = envGet("IXORA_VERSION") || "latest";
|
|
1148
1207
|
const profile = envGet("IXORA_PROFILE") || "full";
|
|
1149
1208
|
console.log();
|
|
1150
1209
|
success("Upgrade complete!");
|
|
1151
|
-
console.log(` ${bold("Version:")} ${
|
|
1210
|
+
console.log(` ${bold("Version:")} ${targetVersion}`);
|
|
1152
1211
|
console.log(` ${bold("Profile:")} ${profile}`);
|
|
1153
|
-
if (
|
|
1212
|
+
if (previousVersion !== targetVersion) {
|
|
1154
1213
|
console.log(` ${dim(`(was ${previousVersion})`)}`);
|
|
1155
1214
|
}
|
|
1156
1215
|
console.log();
|
|
@@ -1237,7 +1296,7 @@ import { existsSync as existsSync6 } from "fs";
|
|
|
1237
1296
|
import {
|
|
1238
1297
|
input,
|
|
1239
1298
|
password,
|
|
1240
|
-
select
|
|
1299
|
+
select as select2
|
|
1241
1300
|
} from "@inquirer/prompts";
|
|
1242
1301
|
async function promptModelProvider() {
|
|
1243
1302
|
const curAgentModel = envGet("IXORA_AGENT_MODEL");
|
|
@@ -1245,7 +1304,7 @@ async function promptModelProvider() {
|
|
|
1245
1304
|
if (curAgentModel.startsWith("openai:")) defaultProvider = "openai";
|
|
1246
1305
|
else if (curAgentModel.startsWith("google:")) defaultProvider = "google";
|
|
1247
1306
|
else if (curAgentModel.startsWith("ollama:")) defaultProvider = "ollama";
|
|
1248
|
-
const provider = await
|
|
1307
|
+
const provider = await select2({
|
|
1249
1308
|
message: "Select a model provider",
|
|
1250
1309
|
choices: [
|
|
1251
1310
|
{
|
|
@@ -1360,28 +1419,38 @@ async function promptIbmiConnection() {
|
|
|
1360
1419
|
const curHost = envGet("DB2i_HOST");
|
|
1361
1420
|
const curUser = envGet("DB2i_USER");
|
|
1362
1421
|
const curPass = envGet("DB2i_PASS");
|
|
1422
|
+
const curPort = envGet("DB2_PORT");
|
|
1363
1423
|
const host = await input({
|
|
1364
|
-
message: "IBM i hostname",
|
|
1424
|
+
message: "IBM i hostname:",
|
|
1365
1425
|
default: curHost || void 0,
|
|
1366
1426
|
validate: (value) => value.trim() ? true : "IBM i hostname is required"
|
|
1367
1427
|
});
|
|
1368
1428
|
const user = await input({
|
|
1369
|
-
message: "IBM i username",
|
|
1429
|
+
message: "IBM i username:",
|
|
1370
1430
|
default: curUser || void 0,
|
|
1371
1431
|
validate: (value) => value.trim() ? true : "IBM i username is required"
|
|
1372
1432
|
});
|
|
1373
1433
|
const pass = await password({
|
|
1374
|
-
message: "IBM i password",
|
|
1434
|
+
message: "IBM i password:",
|
|
1375
1435
|
validate: (value) => {
|
|
1376
1436
|
if (!value && !curPass) return "IBM i password is required";
|
|
1377
1437
|
return true;
|
|
1378
1438
|
}
|
|
1379
1439
|
});
|
|
1380
|
-
|
|
1440
|
+
const port = await input({
|
|
1441
|
+
message: "IBM i port:",
|
|
1442
|
+
default: curPort || "8076",
|
|
1443
|
+
validate: (value) => {
|
|
1444
|
+
const n = parseInt(value.trim(), 10);
|
|
1445
|
+
if (isNaN(n) || n < 1 || n > 65535) return "Enter a valid port number";
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
return { host: host.trim(), user: user.trim(), pass: pass || curPass, port: port.trim() };
|
|
1381
1450
|
}
|
|
1382
1451
|
async function promptProfile() {
|
|
1383
1452
|
const curProfile = envGet("IXORA_PROFILE") || "full";
|
|
1384
|
-
const profile = await
|
|
1453
|
+
const profile = await select2({
|
|
1385
1454
|
message: "Select an agent profile",
|
|
1386
1455
|
choices: VALID_PROFILES.map((p) => ({
|
|
1387
1456
|
name: `${PROFILES[p].name.padEnd(14)} ${dim(PROFILES[p].description)}`,
|
|
@@ -1407,7 +1476,7 @@ async function cmdInstall(opts) {
|
|
|
1407
1476
|
console.log();
|
|
1408
1477
|
if (existsSync6(IXORA_DIR)) {
|
|
1409
1478
|
warn(`Existing installation found at ${IXORA_DIR}`);
|
|
1410
|
-
const action = await
|
|
1479
|
+
const action = await select2({
|
|
1411
1480
|
message: "What would you like to do?",
|
|
1412
1481
|
choices: [
|
|
1413
1482
|
{ name: "Reconfigure \u2014 re-run setup prompts (overwrites current config)", value: "reconfigure" },
|
|
@@ -1424,11 +1493,34 @@ async function cmdInstall(opts) {
|
|
|
1424
1493
|
}
|
|
1425
1494
|
const { agentModel, teamModel, apiKeyVar, apiKeyValue, ollamaHost } = await promptModelProvider();
|
|
1426
1495
|
console.log();
|
|
1427
|
-
const { host, user, pass } = await promptIbmiConnection();
|
|
1496
|
+
const { host, user, pass, port } = await promptIbmiConnection();
|
|
1428
1497
|
console.log();
|
|
1429
1498
|
const profile = opts.profile ? opts.profile : await promptProfile();
|
|
1430
1499
|
console.log();
|
|
1431
|
-
|
|
1500
|
+
let version;
|
|
1501
|
+
if (opts.imageVersion) {
|
|
1502
|
+
version = normalizeVersion(opts.imageVersion);
|
|
1503
|
+
} else {
|
|
1504
|
+
let tags = [];
|
|
1505
|
+
try {
|
|
1506
|
+
tags = await fetchImageTags("ibmi-agi/ixora-api");
|
|
1507
|
+
} catch {
|
|
1508
|
+
warn("Could not fetch available versions from registry");
|
|
1509
|
+
}
|
|
1510
|
+
if (tags.length > 0) {
|
|
1511
|
+
const curVersion = envGet("IXORA_VERSION") || void 0;
|
|
1512
|
+
version = await select2({
|
|
1513
|
+
message: "Select image version",
|
|
1514
|
+
choices: tags.map((t) => ({
|
|
1515
|
+
value: t,
|
|
1516
|
+
name: t === curVersion ? `${t} (current)` : t
|
|
1517
|
+
}))
|
|
1518
|
+
});
|
|
1519
|
+
} else {
|
|
1520
|
+
version = envGet("IXORA_VERSION") || "latest";
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
console.log();
|
|
1432
1524
|
const envConfig = {
|
|
1433
1525
|
agentModel,
|
|
1434
1526
|
teamModel,
|
|
@@ -1438,6 +1530,7 @@ async function cmdInstall(opts) {
|
|
|
1438
1530
|
db2Host: host,
|
|
1439
1531
|
db2User: user,
|
|
1440
1532
|
db2Pass: pass,
|
|
1533
|
+
db2Port: port,
|
|
1441
1534
|
profile,
|
|
1442
1535
|
version
|
|
1443
1536
|
};
|
|
@@ -1581,7 +1674,7 @@ async function cmdConfigEdit() {
|
|
|
1581
1674
|
}
|
|
1582
1675
|
|
|
1583
1676
|
// src/commands/system.ts
|
|
1584
|
-
import { input as input2, password as password2, select as
|
|
1677
|
+
import { input as input2, password as password2, select as select3 } from "@inquirer/prompts";
|
|
1585
1678
|
import chalk6 from "chalk";
|
|
1586
1679
|
async function cmdSystemAdd() {
|
|
1587
1680
|
info("Add an IBM i system");
|
|
@@ -1620,7 +1713,7 @@ async function cmdSystemAdd() {
|
|
|
1620
1713
|
message: "IBM i password",
|
|
1621
1714
|
validate: (value) => value ? true : "Password is required"
|
|
1622
1715
|
});
|
|
1623
|
-
const agentChoice = await
|
|
1716
|
+
const agentChoice = await select3({
|
|
1624
1717
|
message: "Select agents for this system",
|
|
1625
1718
|
choices: [
|
|
1626
1719
|
{
|
|
@@ -1728,9 +1821,9 @@ function createProgram() {
|
|
|
1728
1821
|
const opts = program2.opts();
|
|
1729
1822
|
await cmdStatus(opts);
|
|
1730
1823
|
});
|
|
1731
|
-
program2.command("upgrade").description("Pull latest images and restart").action(async () => {
|
|
1824
|
+
program2.command("upgrade").description("Pull latest images and restart").argument("[version]", "Target version (e.g., 0.0.11 or v0.0.11)").action(async (version) => {
|
|
1732
1825
|
const opts = program2.opts();
|
|
1733
|
-
await cmdUpgrade(opts);
|
|
1826
|
+
await cmdUpgrade({ ...opts, version });
|
|
1734
1827
|
});
|
|
1735
1828
|
program2.command("uninstall").description("Stop services and remove images").action(async () => {
|
|
1736
1829
|
const opts = program2.opts();
|