iranti 0.2.2 → 0.2.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/README.md +38 -11
- package/dist/scripts/iranti-cli.js +513 -38
- package/dist/scripts/iranti-mcp.js +1 -1
- package/dist/scripts/seed.js +10 -10
- package/dist/src/api/server.js +1 -1
- package/dist/src/chat/index.d.ts +8 -0
- package/dist/src/chat/index.d.ts.map +1 -0
- package/dist/src/chat/index.js +565 -0
- package/dist/src/chat/index.js.map +1 -0
- package/dist/src/lib/llm.d.ts +1 -0
- package/dist/src/lib/llm.d.ts.map +1 -1
- package/dist/src/lib/llm.js +4 -0
- package/dist/src/lib/llm.js.map +1 -1
- package/dist/src/lib/router.d.ts.map +1 -1
- package/dist/src/lib/router.js +46 -42
- package/dist/src/lib/router.js.map +1 -1
- package/dist/src/librarian/contextual-conflicts.d.ts +9 -0
- package/dist/src/librarian/contextual-conflicts.d.ts.map +1 -0
- package/dist/src/librarian/contextual-conflicts.js +243 -0
- package/dist/src/librarian/contextual-conflicts.js.map +1 -0
- package/dist/src/librarian/index.d.ts.map +1 -1
- package/dist/src/librarian/index.js +50 -0
- package/dist/src/librarian/index.js.map +1 -1
- package/dist/src/library/backends/chromaBackend.d.ts +27 -0
- package/dist/src/library/backends/chromaBackend.d.ts.map +1 -0
- package/dist/src/library/backends/chromaBackend.js +99 -0
- package/dist/src/library/backends/chromaBackend.js.map +1 -0
- package/dist/src/library/backends/index.d.ts +15 -0
- package/dist/src/library/backends/index.d.ts.map +1 -0
- package/dist/src/library/backends/index.js +39 -0
- package/dist/src/library/backends/index.js.map +1 -0
- package/dist/src/library/backends/pgvectorBackend.d.ts +8 -0
- package/dist/src/library/backends/pgvectorBackend.d.ts.map +1 -0
- package/dist/src/library/backends/pgvectorBackend.js +128 -0
- package/dist/src/library/backends/pgvectorBackend.js.map +1 -0
- package/dist/src/library/backends/qdrantBackend.d.ts +21 -0
- package/dist/src/library/backends/qdrantBackend.d.ts.map +1 -0
- package/dist/src/library/backends/qdrantBackend.js +107 -0
- package/dist/src/library/backends/qdrantBackend.js.map +1 -0
- package/dist/src/library/queries.d.ts.map +1 -1
- package/dist/src/library/queries.js +105 -123
- package/dist/src/library/queries.js.map +1 -1
- package/dist/src/library/vectorBackend.d.ts +19 -0
- package/dist/src/library/vectorBackend.d.ts.map +1 -0
- package/dist/src/library/vectorBackend.js +3 -0
- package/dist/src/library/vectorBackend.js.map +1 -0
- package/dist/src/resolutionist/index.d.ts +8 -0
- package/dist/src/resolutionist/index.d.ts.map +1 -0
- package/dist/src/resolutionist/index.js +265 -0
- package/dist/src/resolutionist/index.js.map +1 -0
- package/package.json +2 -1
|
@@ -6,6 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const https_1 = __importDefault(require("https"));
|
|
9
10
|
const os_1 = __importDefault(require("os"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const child_process_1 = require("child_process");
|
|
@@ -14,6 +15,10 @@ const stream_1 = require("stream");
|
|
|
14
15
|
const net_1 = __importDefault(require("net"));
|
|
15
16
|
const client_1 = require("../src/library/client");
|
|
16
17
|
const apiKeys_1 = require("../src/security/apiKeys");
|
|
18
|
+
const escalationPaths_1 = require("../src/lib/escalationPaths");
|
|
19
|
+
const resolutionist_1 = require("../src/resolutionist");
|
|
20
|
+
const chat_1 = require("../src/chat");
|
|
21
|
+
const backends_1 = require("../src/library/backends");
|
|
17
22
|
const PROVIDER_ENV_KEYS = {
|
|
18
23
|
mock: null,
|
|
19
24
|
ollama: null,
|
|
@@ -314,6 +319,13 @@ function formatEnvValue(value) {
|
|
|
314
319
|
? `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`
|
|
315
320
|
: value;
|
|
316
321
|
}
|
|
322
|
+
function vectorBackendUrl(name, env) {
|
|
323
|
+
if (name === 'qdrant')
|
|
324
|
+
return env.IRANTI_QDRANT_URL ?? null;
|
|
325
|
+
if (name === 'chroma')
|
|
326
|
+
return env.IRANTI_CHROMA_URL ?? 'http://localhost:8000';
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
317
329
|
async function upsertEnvFile(filePath, updates) {
|
|
318
330
|
const existingRaw = fs_1.default.existsSync(filePath) ? await promises_1.default.readFile(filePath, 'utf-8') : '';
|
|
319
331
|
const lines = existingRaw.length > 0 ? existingRaw.split(/\r?\n/) : [];
|
|
@@ -1003,6 +1015,337 @@ function summarizeStatus(checks) {
|
|
|
1003
1015
|
return 'warn';
|
|
1004
1016
|
return 'pass';
|
|
1005
1017
|
}
|
|
1018
|
+
function resolveDoctorEnvTarget(args) {
|
|
1019
|
+
const scope = normalizeScope(getFlag(args, 'scope'));
|
|
1020
|
+
const instanceName = getFlag(args, 'instance');
|
|
1021
|
+
const explicitEnv = getFlag(args, 'env');
|
|
1022
|
+
const cwd = process.cwd();
|
|
1023
|
+
if (explicitEnv) {
|
|
1024
|
+
return {
|
|
1025
|
+
envFile: path_1.default.resolve(explicitEnv),
|
|
1026
|
+
envSource: 'explicit-env',
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
if (instanceName) {
|
|
1030
|
+
const root = resolveInstallRoot(args, scope);
|
|
1031
|
+
return {
|
|
1032
|
+
envFile: path_1.default.join(root, 'instances', instanceName, '.env'),
|
|
1033
|
+
envSource: `instance:${instanceName}`,
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
const repoEnv = path_1.default.join(cwd, '.env');
|
|
1037
|
+
const projectEnv = path_1.default.join(cwd, '.env.iranti');
|
|
1038
|
+
if (fs_1.default.existsSync(repoEnv)) {
|
|
1039
|
+
return { envFile: repoEnv, envSource: 'repo' };
|
|
1040
|
+
}
|
|
1041
|
+
if (fs_1.default.existsSync(projectEnv)) {
|
|
1042
|
+
return { envFile: projectEnv, envSource: 'project-binding' };
|
|
1043
|
+
}
|
|
1044
|
+
return { envFile: null, envSource: 'repo' };
|
|
1045
|
+
}
|
|
1046
|
+
function resolveUpgradeTarget(raw) {
|
|
1047
|
+
if (!raw)
|
|
1048
|
+
return 'auto';
|
|
1049
|
+
const normalized = raw.trim().toLowerCase();
|
|
1050
|
+
if (normalized === 'auto' || normalized === 'npm-global' || normalized === 'npm-repo' || normalized === 'python') {
|
|
1051
|
+
return normalized;
|
|
1052
|
+
}
|
|
1053
|
+
throw new Error(`Invalid --target '${raw}'. Use auto, npm-global, npm-repo, or python.`);
|
|
1054
|
+
}
|
|
1055
|
+
function parseVersion(value) {
|
|
1056
|
+
if (!value)
|
|
1057
|
+
return [0];
|
|
1058
|
+
const match = value.trim().match(/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
|
1059
|
+
if (!match)
|
|
1060
|
+
return [0];
|
|
1061
|
+
return [
|
|
1062
|
+
Number.parseInt(match[1] ?? '0', 10),
|
|
1063
|
+
Number.parseInt(match[2] ?? '0', 10),
|
|
1064
|
+
Number.parseInt(match[3] ?? '0', 10),
|
|
1065
|
+
];
|
|
1066
|
+
}
|
|
1067
|
+
function compareVersions(left, right) {
|
|
1068
|
+
const a = parseVersion(left);
|
|
1069
|
+
const b = parseVersion(right);
|
|
1070
|
+
const limit = Math.max(a.length, b.length, 3);
|
|
1071
|
+
for (let i = 0; i < limit; i++) {
|
|
1072
|
+
const av = a[i] ?? 0;
|
|
1073
|
+
const bv = b[i] ?? 0;
|
|
1074
|
+
if (av > bv)
|
|
1075
|
+
return 1;
|
|
1076
|
+
if (av < bv)
|
|
1077
|
+
return -1;
|
|
1078
|
+
}
|
|
1079
|
+
return 0;
|
|
1080
|
+
}
|
|
1081
|
+
function normalizePathForCompare(value) {
|
|
1082
|
+
return path_1.default.resolve(value).replace(/\\/g, '/').toLowerCase();
|
|
1083
|
+
}
|
|
1084
|
+
function isPathInside(parentDir, childDir) {
|
|
1085
|
+
const parent = normalizePathForCompare(parentDir);
|
|
1086
|
+
const child = normalizePathForCompare(childDir);
|
|
1087
|
+
return child === parent || child.startsWith(`${parent}/`);
|
|
1088
|
+
}
|
|
1089
|
+
function resolveSpawnExecutable(executable) {
|
|
1090
|
+
if (process.platform !== 'win32')
|
|
1091
|
+
return executable;
|
|
1092
|
+
if (executable === 'npm')
|
|
1093
|
+
return 'npm.cmd';
|
|
1094
|
+
if (executable === 'npx')
|
|
1095
|
+
return 'npx.cmd';
|
|
1096
|
+
return executable;
|
|
1097
|
+
}
|
|
1098
|
+
function runCommandCapture(executable, args, cwd) {
|
|
1099
|
+
const proc = (0, child_process_1.spawnSync)(resolveSpawnExecutable(executable), args, {
|
|
1100
|
+
cwd,
|
|
1101
|
+
encoding: 'utf8',
|
|
1102
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1103
|
+
});
|
|
1104
|
+
return {
|
|
1105
|
+
status: proc.status,
|
|
1106
|
+
stdout: proc.stdout ?? '',
|
|
1107
|
+
stderr: proc.stderr ?? '',
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
function runCommandInteractive(step) {
|
|
1111
|
+
const proc = (0, child_process_1.spawnSync)(resolveSpawnExecutable(step.executable), step.args, {
|
|
1112
|
+
cwd: step.cwd,
|
|
1113
|
+
stdio: 'inherit',
|
|
1114
|
+
});
|
|
1115
|
+
return proc.status;
|
|
1116
|
+
}
|
|
1117
|
+
function detectPythonLauncher() {
|
|
1118
|
+
const candidates = process.platform === 'win32'
|
|
1119
|
+
? [
|
|
1120
|
+
{ label: 'python', display: 'python -m pip install --upgrade iranti', executable: 'python', args: ['-m', 'pip', 'install', '--upgrade', 'iranti'] },
|
|
1121
|
+
{ label: 'py', display: 'py -3 -m pip install --upgrade iranti', executable: 'py', args: ['-3', '-m', 'pip', 'install', '--upgrade', 'iranti'] },
|
|
1122
|
+
]
|
|
1123
|
+
: [
|
|
1124
|
+
{ label: 'python3', display: 'python3 -m pip install --upgrade iranti', executable: 'python3', args: ['-m', 'pip', 'install', '--upgrade', 'iranti'] },
|
|
1125
|
+
{ label: 'python', display: 'python -m pip install --upgrade iranti', executable: 'python', args: ['-m', 'pip', 'install', '--upgrade', 'iranti'] },
|
|
1126
|
+
];
|
|
1127
|
+
for (const candidate of candidates) {
|
|
1128
|
+
const probeArgs = candidate.args[0] === '-3' ? ['-3', '--version'] : ['--version'];
|
|
1129
|
+
const probe = runCommandCapture(candidate.executable, probeArgs);
|
|
1130
|
+
if (probe.status === 0)
|
|
1131
|
+
return candidate;
|
|
1132
|
+
}
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
function detectGlobalNpmRoot() {
|
|
1136
|
+
const proc = runCommandCapture('npm', ['root', '-g']);
|
|
1137
|
+
if (proc.status !== 0)
|
|
1138
|
+
return null;
|
|
1139
|
+
const value = proc.stdout.trim();
|
|
1140
|
+
return value ? path_1.default.resolve(value) : null;
|
|
1141
|
+
}
|
|
1142
|
+
function readJsonFile(filePath) {
|
|
1143
|
+
if (!fs_1.default.existsSync(filePath))
|
|
1144
|
+
return null;
|
|
1145
|
+
try {
|
|
1146
|
+
return JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
|
|
1147
|
+
}
|
|
1148
|
+
catch {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function httpsJson(url, headers = {}) {
|
|
1153
|
+
return new Promise((resolve, reject) => {
|
|
1154
|
+
const request = https_1.default.get(url, { headers }, (response) => {
|
|
1155
|
+
const statusCode = response.statusCode ?? 0;
|
|
1156
|
+
if (statusCode >= 300 && statusCode < 400 && response.headers.location) {
|
|
1157
|
+
response.resume();
|
|
1158
|
+
const redirect = new URL(response.headers.location, url).toString();
|
|
1159
|
+
httpsJson(redirect, headers).then(resolve).catch(reject);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
1163
|
+
response.resume();
|
|
1164
|
+
reject(new Error(`HTTP ${statusCode} from ${url}`));
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
let raw = '';
|
|
1168
|
+
response.setEncoding('utf8');
|
|
1169
|
+
response.on('data', (chunk) => {
|
|
1170
|
+
raw += chunk;
|
|
1171
|
+
});
|
|
1172
|
+
response.on('end', () => {
|
|
1173
|
+
try {
|
|
1174
|
+
resolve(JSON.parse(raw));
|
|
1175
|
+
}
|
|
1176
|
+
catch (error) {
|
|
1177
|
+
reject(error);
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
});
|
|
1181
|
+
request.setTimeout(5000, () => {
|
|
1182
|
+
request.destroy(new Error(`Timed out fetching ${url}`));
|
|
1183
|
+
});
|
|
1184
|
+
request.on('error', reject);
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
async function fetchLatestNpmVersion() {
|
|
1188
|
+
try {
|
|
1189
|
+
const payload = await httpsJson('https://registry.npmjs.org/iranti/latest');
|
|
1190
|
+
return typeof payload?.version === 'string' ? payload.version : null;
|
|
1191
|
+
}
|
|
1192
|
+
catch {
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
async function fetchLatestPypiVersion() {
|
|
1197
|
+
try {
|
|
1198
|
+
const payload = await httpsJson('https://pypi.org/pypi/iranti/json');
|
|
1199
|
+
return typeof payload?.info?.version === 'string' ? payload.info.version : null;
|
|
1200
|
+
}
|
|
1201
|
+
catch {
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
function repoUpgradeCommands(root) {
|
|
1206
|
+
return [
|
|
1207
|
+
{ label: 'git pull', display: 'git pull --ff-only', executable: 'git', args: ['pull', '--ff-only'], cwd: root },
|
|
1208
|
+
{ label: 'npm install', display: 'npm install', executable: 'npm', args: ['install'], cwd: root },
|
|
1209
|
+
{ label: 'npm build', display: 'npm run build', executable: 'npm', args: ['run', 'build'], cwd: root },
|
|
1210
|
+
];
|
|
1211
|
+
}
|
|
1212
|
+
function repoIsDirty(root) {
|
|
1213
|
+
const proc = runCommandCapture('git', ['status', '--porcelain'], root);
|
|
1214
|
+
return proc.status === 0 && proc.stdout.trim().length > 0;
|
|
1215
|
+
}
|
|
1216
|
+
function detectUpgradeContext(args) {
|
|
1217
|
+
const scope = normalizeScope(getFlag(args, 'scope'));
|
|
1218
|
+
const packageRootPath = packageRoot();
|
|
1219
|
+
const runtimeRoot = resolveInstallRoot(args, scope);
|
|
1220
|
+
const runtimeInstalled = fs_1.default.existsSync(path_1.default.join(runtimeRoot, 'install.json'));
|
|
1221
|
+
const repoCheckout = fs_1.default.existsSync(path_1.default.join(packageRootPath, '.git'));
|
|
1222
|
+
const globalNpmRoot = detectGlobalNpmRoot();
|
|
1223
|
+
const globalNpmInstall = globalNpmRoot !== null && isPathInside(globalNpmRoot, packageRootPath);
|
|
1224
|
+
const python = detectPythonLauncher();
|
|
1225
|
+
const availableTargets = [];
|
|
1226
|
+
if (globalNpmInstall)
|
|
1227
|
+
availableTargets.push('npm-global');
|
|
1228
|
+
if (repoCheckout)
|
|
1229
|
+
availableTargets.push('npm-repo');
|
|
1230
|
+
if (python)
|
|
1231
|
+
availableTargets.push('python');
|
|
1232
|
+
return {
|
|
1233
|
+
packageRootPath,
|
|
1234
|
+
currentVersion: getPackageVersion(),
|
|
1235
|
+
runtimeRoot,
|
|
1236
|
+
runtimeInstalled,
|
|
1237
|
+
repoCheckout,
|
|
1238
|
+
globalNpmInstall,
|
|
1239
|
+
globalNpmRoot,
|
|
1240
|
+
python,
|
|
1241
|
+
availableTargets,
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
function chooseUpgradeTarget(requested, context) {
|
|
1245
|
+
if (requested !== 'auto') {
|
|
1246
|
+
if (!context.availableTargets.includes(requested)) {
|
|
1247
|
+
throw new Error(`Requested target '${requested}' is not available in this environment.`);
|
|
1248
|
+
}
|
|
1249
|
+
return requested;
|
|
1250
|
+
}
|
|
1251
|
+
if (context.repoCheckout)
|
|
1252
|
+
return 'npm-repo';
|
|
1253
|
+
if (context.globalNpmInstall)
|
|
1254
|
+
return 'npm-global';
|
|
1255
|
+
if (context.python)
|
|
1256
|
+
return 'python';
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
function commandListForTarget(target, context) {
|
|
1260
|
+
if (target === 'npm-repo') {
|
|
1261
|
+
return repoUpgradeCommands(context.packageRootPath);
|
|
1262
|
+
}
|
|
1263
|
+
if (target === 'npm-global') {
|
|
1264
|
+
return [{
|
|
1265
|
+
label: 'npm global',
|
|
1266
|
+
display: 'npm install -g iranti@latest',
|
|
1267
|
+
executable: 'npm',
|
|
1268
|
+
args: ['install', '-g', 'iranti@latest'],
|
|
1269
|
+
cwd: context.packageRootPath,
|
|
1270
|
+
}];
|
|
1271
|
+
}
|
|
1272
|
+
if (!context.python) {
|
|
1273
|
+
throw new Error('Python launcher not found for python upgrade target.');
|
|
1274
|
+
}
|
|
1275
|
+
return [context.python];
|
|
1276
|
+
}
|
|
1277
|
+
async function refreshInstallMetaVersion(runtimeRoot, version) {
|
|
1278
|
+
const installMetaPath = path_1.default.join(runtimeRoot, 'install.json');
|
|
1279
|
+
const meta = readJsonFile(installMetaPath);
|
|
1280
|
+
if (!meta)
|
|
1281
|
+
return;
|
|
1282
|
+
await writeJson(installMetaPath, {
|
|
1283
|
+
...meta,
|
|
1284
|
+
version,
|
|
1285
|
+
upgradedAt: new Date().toISOString(),
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
function verifyGlobalNpmInstall() {
|
|
1289
|
+
const proc = runCommandCapture('npm', ['list', '-g', 'iranti', '--depth=0', '--json']);
|
|
1290
|
+
if (proc.status !== 0) {
|
|
1291
|
+
return {
|
|
1292
|
+
status: 'warn',
|
|
1293
|
+
detail: 'npm global upgrade finished, but `npm list -g iranti` did not return cleanly.',
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
try {
|
|
1297
|
+
const payload = JSON.parse(proc.stdout);
|
|
1298
|
+
const version = payload?.dependencies?.iranti?.version;
|
|
1299
|
+
return typeof version === 'string'
|
|
1300
|
+
? { status: 'pass', detail: `npm global install reports iranti@${version}.` }
|
|
1301
|
+
: { status: 'warn', detail: 'npm global upgrade finished, but installed version could not be confirmed.' };
|
|
1302
|
+
}
|
|
1303
|
+
catch {
|
|
1304
|
+
return {
|
|
1305
|
+
status: 'warn',
|
|
1306
|
+
detail: 'npm global upgrade finished, but version verification output was unreadable.',
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
function verifyPythonInstall(command) {
|
|
1311
|
+
const args = command.executable === 'py' ? ['-3', '-m', 'pip', 'show', 'iranti'] : ['-m', 'pip', 'show', 'iranti'];
|
|
1312
|
+
const proc = runCommandCapture(command.executable, args);
|
|
1313
|
+
if (proc.status !== 0) {
|
|
1314
|
+
return {
|
|
1315
|
+
status: 'warn',
|
|
1316
|
+
detail: 'Python upgrade finished, but `pip show iranti` did not confirm the installed version.',
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
const versionLine = proc.stdout.split(/\r?\n/).find((line) => line.toLowerCase().startsWith('version:'));
|
|
1320
|
+
return versionLine
|
|
1321
|
+
? { status: 'pass', detail: `Python client ${versionLine.trim()}.` }
|
|
1322
|
+
: { status: 'warn', detail: 'Python upgrade finished, but installed version could not be confirmed.' };
|
|
1323
|
+
}
|
|
1324
|
+
async function executeUpgradeTarget(target, context) {
|
|
1325
|
+
if (target === 'npm-repo' && repoIsDirty(context.packageRootPath)) {
|
|
1326
|
+
throw new Error('Repository worktree is dirty. Commit or stash changes before running `iranti upgrade --target npm-repo --yes`.');
|
|
1327
|
+
}
|
|
1328
|
+
const commands = commandListForTarget(target, context);
|
|
1329
|
+
const steps = [];
|
|
1330
|
+
for (const command of commands) {
|
|
1331
|
+
console.log(`${infoLabel()} ${command.display}`);
|
|
1332
|
+
const status = runCommandInteractive(command);
|
|
1333
|
+
steps.push({ label: command.label, command: command.display });
|
|
1334
|
+
if (status !== 0) {
|
|
1335
|
+
throw new Error(`Upgrade step failed: ${command.display}`);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
const verification = target === 'npm-global'
|
|
1339
|
+
? verifyGlobalNpmInstall()
|
|
1340
|
+
: target === 'python'
|
|
1341
|
+
? verifyPythonInstall(commands[0])
|
|
1342
|
+
: { status: 'pass', detail: 'Repository refresh completed and build succeeded.' };
|
|
1343
|
+
if (context.runtimeInstalled && verification.status !== 'fail') {
|
|
1344
|
+
const nextVersion = target === 'python' ? context.currentVersion : (await fetchLatestNpmVersion()) ?? context.currentVersion;
|
|
1345
|
+
await refreshInstallMetaVersion(context.runtimeRoot, nextVersion);
|
|
1346
|
+
}
|
|
1347
|
+
return { target, steps, verification };
|
|
1348
|
+
}
|
|
1006
1349
|
async function listProviderKeysCommand(args) {
|
|
1007
1350
|
const target = await resolveProviderKeyTarget(args);
|
|
1008
1351
|
const currentProvider = normalizeProvider(target.env.LLM_PROVIDER ?? 'mock');
|
|
@@ -1385,34 +1728,8 @@ async function setupCommand(args) {
|
|
|
1385
1728
|
console.log(` 2. iranti doctor --instance ${finalResult.instanceName} --root "${finalResult.root}"`);
|
|
1386
1729
|
}
|
|
1387
1730
|
async function doctorCommand(args) {
|
|
1388
|
-
const scope = normalizeScope(getFlag(args, 'scope'));
|
|
1389
|
-
const instanceName = getFlag(args, 'instance');
|
|
1390
|
-
const explicitEnv = getFlag(args, 'env');
|
|
1391
1731
|
const json = hasFlag(args, 'json');
|
|
1392
|
-
const
|
|
1393
|
-
let envFile = null;
|
|
1394
|
-
let envSource = 'repo';
|
|
1395
|
-
if (explicitEnv) {
|
|
1396
|
-
envFile = path_1.default.resolve(explicitEnv);
|
|
1397
|
-
envSource = 'explicit-env';
|
|
1398
|
-
}
|
|
1399
|
-
else if (instanceName) {
|
|
1400
|
-
const root = resolveInstallRoot(args, scope);
|
|
1401
|
-
envFile = path_1.default.join(root, 'instances', instanceName, '.env');
|
|
1402
|
-
envSource = `instance:${instanceName}`;
|
|
1403
|
-
}
|
|
1404
|
-
else {
|
|
1405
|
-
const repoEnv = path_1.default.join(cwd, '.env');
|
|
1406
|
-
const projectEnv = path_1.default.join(cwd, '.env.iranti');
|
|
1407
|
-
if (fs_1.default.existsSync(repoEnv)) {
|
|
1408
|
-
envFile = repoEnv;
|
|
1409
|
-
envSource = 'repo';
|
|
1410
|
-
}
|
|
1411
|
-
else if (fs_1.default.existsSync(projectEnv)) {
|
|
1412
|
-
envFile = projectEnv;
|
|
1413
|
-
envSource = 'project-binding';
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1732
|
+
const { envFile, envSource } = resolveDoctorEnvTarget(args);
|
|
1416
1733
|
const checks = [];
|
|
1417
1734
|
const version = getPackageVersion();
|
|
1418
1735
|
checks.push({
|
|
@@ -1507,6 +1824,46 @@ async function doctorCommand(args) {
|
|
|
1507
1824
|
detail: `LLM_PROVIDER=${provider}`,
|
|
1508
1825
|
});
|
|
1509
1826
|
checks.push(detectProviderKey(provider, env));
|
|
1827
|
+
try {
|
|
1828
|
+
const backendName = (0, backends_1.resolveVectorBackendName)({
|
|
1829
|
+
vectorBackend: env.IRANTI_VECTOR_BACKEND,
|
|
1830
|
+
qdrantUrl: env.IRANTI_QDRANT_URL,
|
|
1831
|
+
qdrantApiKey: env.IRANTI_QDRANT_API_KEY,
|
|
1832
|
+
qdrantCollection: env.IRANTI_QDRANT_COLLECTION,
|
|
1833
|
+
chromaUrl: env.IRANTI_CHROMA_URL,
|
|
1834
|
+
chromaCollection: env.IRANTI_CHROMA_COLLECTION,
|
|
1835
|
+
chromaTenant: env.IRANTI_CHROMA_TENANT,
|
|
1836
|
+
chromaDatabase: env.IRANTI_CHROMA_DATABASE,
|
|
1837
|
+
chromaToken: env.IRANTI_CHROMA_TOKEN,
|
|
1838
|
+
});
|
|
1839
|
+
const backend = (0, backends_1.createVectorBackend)({
|
|
1840
|
+
vectorBackend: backendName,
|
|
1841
|
+
qdrantUrl: env.IRANTI_QDRANT_URL,
|
|
1842
|
+
qdrantApiKey: env.IRANTI_QDRANT_API_KEY,
|
|
1843
|
+
qdrantCollection: env.IRANTI_QDRANT_COLLECTION,
|
|
1844
|
+
chromaUrl: env.IRANTI_CHROMA_URL,
|
|
1845
|
+
chromaCollection: env.IRANTI_CHROMA_COLLECTION,
|
|
1846
|
+
chromaTenant: env.IRANTI_CHROMA_TENANT,
|
|
1847
|
+
chromaDatabase: env.IRANTI_CHROMA_DATABASE,
|
|
1848
|
+
chromaToken: env.IRANTI_CHROMA_TOKEN,
|
|
1849
|
+
});
|
|
1850
|
+
const reachable = await backend.ping();
|
|
1851
|
+
const url = vectorBackendUrl(backendName, env);
|
|
1852
|
+
checks.push({
|
|
1853
|
+
name: 'vector backend',
|
|
1854
|
+
status: reachable ? 'pass' : 'warn',
|
|
1855
|
+
detail: url
|
|
1856
|
+
? `${backendName} (${url}) is ${reachable ? 'reachable' : 'unreachable'}`
|
|
1857
|
+
: `${backendName} is ${reachable ? 'reachable' : 'unreachable'}`,
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
catch (error) {
|
|
1861
|
+
checks.push({
|
|
1862
|
+
name: 'vector backend',
|
|
1863
|
+
status: 'fail',
|
|
1864
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1510
1867
|
}
|
|
1511
1868
|
const result = {
|
|
1512
1869
|
version,
|
|
@@ -1608,28 +1965,119 @@ async function statusCommand(args) {
|
|
|
1608
1965
|
}
|
|
1609
1966
|
}
|
|
1610
1967
|
async function upgradeCommand(args) {
|
|
1968
|
+
const checkOnly = hasFlag(args, 'check');
|
|
1969
|
+
const dryRun = hasFlag(args, 'dry-run');
|
|
1970
|
+
const execute = hasFlag(args, 'yes');
|
|
1611
1971
|
const json = hasFlag(args, 'json');
|
|
1612
|
-
const
|
|
1972
|
+
const requestedTarget = resolveUpgradeTarget(getFlag(args, 'target'));
|
|
1973
|
+
const context = detectUpgradeContext(args);
|
|
1974
|
+
const latestNpm = await fetchLatestNpmVersion();
|
|
1975
|
+
const latestPython = await fetchLatestPypiVersion();
|
|
1976
|
+
const chosenTarget = chooseUpgradeTarget(requestedTarget, context);
|
|
1613
1977
|
const commands = {
|
|
1614
1978
|
npmGlobal: 'npm install -g iranti@latest',
|
|
1615
|
-
npmRepo: 'git pull && npm install && npm run build',
|
|
1616
|
-
python: 'pip install --upgrade iranti',
|
|
1979
|
+
npmRepo: 'git pull --ff-only && npm install && npm run build',
|
|
1980
|
+
python: context.python?.display ?? 'python -m pip install --upgrade iranti',
|
|
1617
1981
|
};
|
|
1982
|
+
const updateAvailable = {
|
|
1983
|
+
npm: latestNpm ? compareVersions(latestNpm, context.currentVersion) > 0 : null,
|
|
1984
|
+
python: latestPython ? compareVersions(latestPython, context.currentVersion) > 0 : null,
|
|
1985
|
+
};
|
|
1986
|
+
const plan = chosenTarget ? commandListForTarget(chosenTarget, context) : [];
|
|
1987
|
+
let execution = null;
|
|
1988
|
+
let note = null;
|
|
1989
|
+
if (execute) {
|
|
1990
|
+
if (!chosenTarget) {
|
|
1991
|
+
throw new Error('No executable upgrade path was detected. Use --target npm-global, --target npm-repo, or --target python.');
|
|
1992
|
+
}
|
|
1993
|
+
if (dryRun || checkOnly) {
|
|
1994
|
+
note = 'Execution skipped because --dry-run or --check was provided.';
|
|
1995
|
+
}
|
|
1996
|
+
else if (chosenTarget === 'npm-global' && updateAvailable.npm === false) {
|
|
1997
|
+
note = 'npm global install is already at the latest published version.';
|
|
1998
|
+
}
|
|
1999
|
+
else if (chosenTarget === 'python' && updateAvailable.python === false) {
|
|
2000
|
+
note = 'Python client is already at the latest published version.';
|
|
2001
|
+
}
|
|
2002
|
+
else {
|
|
2003
|
+
execution = await executeUpgradeTarget(chosenTarget, context);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
else if (!checkOnly && !dryRun) {
|
|
2007
|
+
note = 'Run with --yes to execute the selected upgrade path. Use --check to inspect and --dry-run to print exact commands.';
|
|
2008
|
+
}
|
|
1618
2009
|
if (json) {
|
|
1619
2010
|
console.log(JSON.stringify({
|
|
1620
|
-
|
|
1621
|
-
|
|
2011
|
+
currentVersion: context.currentVersion,
|
|
2012
|
+
latest: {
|
|
2013
|
+
npm: latestNpm,
|
|
2014
|
+
python: latestPython,
|
|
2015
|
+
},
|
|
2016
|
+
install: {
|
|
2017
|
+
packageRoot: context.packageRootPath,
|
|
2018
|
+
runtimeRoot: context.runtimeRoot,
|
|
2019
|
+
runtimeInstalled: context.runtimeInstalled,
|
|
2020
|
+
repoCheckout: context.repoCheckout,
|
|
2021
|
+
globalNpmInstall: context.globalNpmInstall,
|
|
2022
|
+
globalNpmRoot: context.globalNpmRoot,
|
|
2023
|
+
pythonLauncher: context.python?.executable ?? null,
|
|
2024
|
+
},
|
|
2025
|
+
requestedTarget,
|
|
2026
|
+
selectedTarget: chosenTarget,
|
|
2027
|
+
availableTargets: context.availableTargets,
|
|
2028
|
+
updateAvailable,
|
|
1622
2029
|
commands,
|
|
2030
|
+
plan: plan.map((step) => step.display),
|
|
2031
|
+
action: execute && !dryRun && !checkOnly ? 'upgrade' : checkOnly ? 'check' : dryRun ? 'dry-run' : 'inspect',
|
|
2032
|
+
execution,
|
|
2033
|
+
note,
|
|
1623
2034
|
}, null, 2));
|
|
1624
2035
|
return;
|
|
1625
2036
|
}
|
|
1626
2037
|
console.log(bold('Iranti upgrade'));
|
|
1627
|
-
console.log(` current_version ${
|
|
1628
|
-
console.log(
|
|
2038
|
+
console.log(` current_version ${context.currentVersion}`);
|
|
2039
|
+
console.log(` latest_npm ${latestNpm ?? '(unavailable)'}`);
|
|
2040
|
+
console.log(` latest_python ${latestPython ?? '(unavailable)'}`);
|
|
2041
|
+
console.log(` package_root ${context.packageRootPath}`);
|
|
2042
|
+
console.log(` runtime_root ${context.runtimeRoot}`);
|
|
2043
|
+
console.log(` repo_checkout ${context.repoCheckout ? paint('yes', 'green') : paint('no', 'gray')}`);
|
|
2044
|
+
console.log(` npm_global ${context.globalNpmInstall ? paint('yes', 'green') : paint('no', 'gray')}`);
|
|
2045
|
+
console.log(` python ${context.python?.executable ?? paint('not found', 'yellow')}`);
|
|
2046
|
+
console.log('');
|
|
2047
|
+
if (chosenTarget) {
|
|
2048
|
+
console.log(` selected_target ${paint(chosenTarget, 'cyan')}${requestedTarget === 'auto' ? paint(' (auto)', 'gray') : ''}`);
|
|
2049
|
+
console.log(' plan');
|
|
2050
|
+
for (const step of plan) {
|
|
2051
|
+
console.log(` - ${step.display}`);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
else {
|
|
2055
|
+
console.log(` selected_target ${paint('none', 'yellow')}`);
|
|
2056
|
+
console.log(' plan No executable upgrade path detected automatically.');
|
|
2057
|
+
}
|
|
1629
2058
|
console.log('');
|
|
1630
2059
|
console.log(` npm global ${commands.npmGlobal}`);
|
|
1631
2060
|
console.log(` npm repo ${commands.npmRepo}`);
|
|
1632
2061
|
console.log(` python client ${commands.python}`);
|
|
2062
|
+
if (execution) {
|
|
2063
|
+
const marker = execution.verification.status === 'pass'
|
|
2064
|
+
? okLabel('PASS')
|
|
2065
|
+
: execution.verification.status === 'warn'
|
|
2066
|
+
? warnLabel('WARN')
|
|
2067
|
+
: failLabel('FAIL');
|
|
2068
|
+
console.log('');
|
|
2069
|
+
console.log(`${okLabel()} Upgrade completed for ${execution.target}.`);
|
|
2070
|
+
console.log(`${marker} ${execution.verification.detail}`);
|
|
2071
|
+
const { envFile } = resolveDoctorEnvTarget(args);
|
|
2072
|
+
if (envFile) {
|
|
2073
|
+
console.log(`${infoLabel()} Run \`iranti doctor\` to verify the active environment after the package upgrade.`);
|
|
2074
|
+
}
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
if (note) {
|
|
2078
|
+
console.log('');
|
|
2079
|
+
console.log(`${infoLabel()} ${note}`);
|
|
2080
|
+
}
|
|
1633
2081
|
}
|
|
1634
2082
|
async function installCommand(args) {
|
|
1635
2083
|
const scope = normalizeScope(getFlag(args, 'scope'));
|
|
@@ -2066,6 +2514,23 @@ async function authRevokeKeyCommand(args) {
|
|
|
2066
2514
|
console.log(`${okLabel()} Revoked API key '${keyId}' for instance '${instanceName}'.`);
|
|
2067
2515
|
process.exit(0);
|
|
2068
2516
|
}
|
|
2517
|
+
async function resolveCommand(args) {
|
|
2518
|
+
const explicitDir = getFlag(args, 'dir');
|
|
2519
|
+
const escalationDir = explicitDir ? path_1.default.resolve(explicitDir) : (0, escalationPaths_1.getEscalationPaths)().root;
|
|
2520
|
+
await (0, resolutionist_1.resolveInteractive)(escalationDir);
|
|
2521
|
+
}
|
|
2522
|
+
async function chatCommand(args) {
|
|
2523
|
+
const provider = normalizeProvider(getFlag(args, 'provider'));
|
|
2524
|
+
if (provider && !isSupportedProvider(provider)) {
|
|
2525
|
+
throw new Error(`Unsupported provider '${provider}'.`);
|
|
2526
|
+
}
|
|
2527
|
+
await (0, chat_1.startChatSession)({
|
|
2528
|
+
agentId: getFlag(args, 'agent') ?? 'iranti_chat',
|
|
2529
|
+
provider,
|
|
2530
|
+
model: getFlag(args, 'model'),
|
|
2531
|
+
cwd: process.cwd(),
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2069
2534
|
function printHelp() {
|
|
2070
2535
|
console.log(`Iranti CLI
|
|
2071
2536
|
|
|
@@ -2097,10 +2562,12 @@ Configuration:
|
|
|
2097
2562
|
Project-level:
|
|
2098
2563
|
iranti project init [path] --instance <name> [--api-key <token>] [--agent-id <id>] [--force]
|
|
2099
2564
|
|
|
2100
|
-
Diagnostics:
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2565
|
+
Diagnostics:
|
|
2566
|
+
iranti doctor [--instance <name>] [--scope user|system] [--env <file>] [--json]
|
|
2567
|
+
iranti status [--scope user|system] [--json]
|
|
2568
|
+
iranti upgrade [--check] [--dry-run] [--yes] [--target auto|npm-global|npm-repo|python] [--json]
|
|
2569
|
+
iranti chat [--agent <agent-id>] [--provider <provider>] [--model <model>]
|
|
2570
|
+
iranti resolve [--dir <escalation-dir>]
|
|
2104
2571
|
|
|
2105
2572
|
Integrations:
|
|
2106
2573
|
iranti mcp [--help]
|
|
@@ -2199,6 +2666,14 @@ async function main() {
|
|
|
2199
2666
|
await upgradeCommand(args);
|
|
2200
2667
|
return;
|
|
2201
2668
|
}
|
|
2669
|
+
if (args.command === 'chat') {
|
|
2670
|
+
await chatCommand(args);
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
if (args.command === 'resolve') {
|
|
2674
|
+
await resolveCommand(args);
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2202
2677
|
if (args.command === 'mcp') {
|
|
2203
2678
|
await handoffToScript('iranti-mcp', process.argv.slice(3));
|
|
2204
2679
|
return;
|
|
@@ -144,7 +144,7 @@ async function main() {
|
|
|
144
144
|
await ensureDefaultAgent(iranti);
|
|
145
145
|
const server = new mcp_js_1.McpServer({
|
|
146
146
|
name: 'iranti-mcp',
|
|
147
|
-
version: '0.2.
|
|
147
|
+
version: '0.2.3',
|
|
148
148
|
});
|
|
149
149
|
server.registerTool('iranti_handshake', {
|
|
150
150
|
description: `Initialize or refresh an agent's working-memory brief for the current task.
|