@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.
Files changed (2) hide show
  1. package/dist/index.js +118 -25
  2. 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.0";
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 = tag === "latest" ? `${img.Repository || ""}:latest${id}` : `${img.Repository || ""}:${tag}`;
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
- info("Upgrading ixora...");
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 latest images...");
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:")} ${newVersion}`);
1210
+ console.log(` ${bold("Version:")} ${targetVersion}`);
1152
1211
  console.log(` ${bold("Profile:")} ${profile}`);
1153
- if (opts.imageVersion && previousVersion !== opts.imageVersion) {
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 select({
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
- return { host: host.trim(), user: user.trim(), pass: pass || curPass };
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 select({
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 select({
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
- const version = opts.imageVersion ?? envGet("IXORA_VERSION") ?? "latest";
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 select2 } from "@inquirer/prompts";
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 select2({
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibm/ixora",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for managing ixora AI agent deployments on IBM i",
5
5
  "type": "module",
6
6
  "bin": {