iranti 0.2.19 → 0.2.20
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 +5 -5
- package/dist/scripts/iranti-cli.js +181 -36
- package/dist/scripts/iranti-mcp.js +1 -1
- package/dist/scripts/setup.js +31 -6
- package/dist/src/api/server.js +1 -1
- package/package.json +2 -1
- package/prisma.config.ts +14 -0
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
Iranti gives agents persistent, identity-based memory. Facts written by one agent are retrievable by any other agent through exact entity+key lookup. Iranti also supports hybrid search (lexical + vector) when exact keys are unknown. Memory persists across sessions and survives context window limits.
|
|
11
11
|
|
|
12
|
-
**Latest release:** [`v0.2.
|
|
12
|
+
**Latest release:** [`v0.2.20`](https://github.com/nfemmanuel/iranti/releases/tag/v0.2.20)
|
|
13
13
|
Published packages:
|
|
14
|
-
- `iranti@0.2.
|
|
15
|
-
- `@iranti/sdk@0.2.
|
|
14
|
+
- `iranti@0.2.20`
|
|
15
|
+
- `@iranti/sdk@0.2.20`
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -173,7 +173,7 @@ The next leverage is still product simplicity: setup, operations, and day-to-day
|
|
|
173
173
|
|
|
174
174
|
## Quickstart
|
|
175
175
|
|
|
176
|
-
**Requirements**: Node.js 18+, PostgreSQL, Python 3.8+
|
|
176
|
+
**Requirements**: Node.js 18+, PostgreSQL with pgvector support, Python 3.8+
|
|
177
177
|
|
|
178
178
|
Docker is optional. It is one local way to run PostgreSQL if you do not already have a database. Iranti still requires PostgreSQL; the setup improvement is smarter bootstrap and clearer guidance, not a second storage backend.
|
|
179
179
|
|
|
@@ -190,7 +190,7 @@ iranti run --instance local
|
|
|
190
190
|
|
|
191
191
|
`iranti setup` now defaults to an isolated per-project runtime. Shared machine-level instances are still supported, but they are now an explicit choice rather than the default.
|
|
192
192
|
|
|
193
|
-
If local PostgreSQL is available, setup can bootstrap a localhost database for you. If
|
|
193
|
+
If local PostgreSQL is available and pgvector-capable, setup can bootstrap a localhost database for you. If Docker is available, setup now prefers the Docker path over a plain local listener because it guarantees pgvector. If local PostgreSQL is reachable but does not provide pgvector, setup now fails early with a direct action path instead of a late Prisma migration error.
|
|
194
194
|
|
|
195
195
|
Long-running agents can now checkpoint and recover interrupted work. Programmatic session lifecycle methods are available through the SDK and REST API:
|
|
196
196
|
- `checkpoint()`
|
|
@@ -221,6 +221,17 @@ function builtScriptPath(scriptName) {
|
|
|
221
221
|
}
|
|
222
222
|
function formatSetupBootstrapFailure(error) {
|
|
223
223
|
const reason = error instanceof Error ? error.message : String(error);
|
|
224
|
+
if (/Could not find Prisma Schema/i.test(reason)) {
|
|
225
|
+
return new Error(`Database bootstrap failed because Prisma could not locate prisma/schema.prisma from the active package. ` +
|
|
226
|
+
`This usually means the installed CLI bundle or working-directory handoff is wrong. ` +
|
|
227
|
+
`Underlying error: ${reason}`);
|
|
228
|
+
}
|
|
229
|
+
if (/extension "vector" is not available|pgvector extension is not installed|does not have the pgvector extension installed/i.test(reason)) {
|
|
230
|
+
return new Error(`Database bootstrap failed because the target PostgreSQL server does not provide pgvector. ` +
|
|
231
|
+
`Iranti currently requires pgvector-capable PostgreSQL for schema bootstrap. ` +
|
|
232
|
+
`Install pgvector on that server, or rerun setup with --db-mode docker or a managed pgvector-capable database. ` +
|
|
233
|
+
`Underlying error: ${reason}`);
|
|
234
|
+
}
|
|
224
235
|
return new Error(`Database bootstrap failed after instance configuration. ` +
|
|
225
236
|
`Common causes are a non-empty database that Prisma has not baselined yet, or a PostgreSQL server without the pgvector extension installed. ` +
|
|
226
237
|
`Re-run setup without --bootstrap-db, or point Iranti at a fresh pgvector-capable database. ` +
|
|
@@ -761,29 +772,31 @@ function inferProjectMode(projectPath, instanceEnvFile) {
|
|
|
761
772
|
}
|
|
762
773
|
function recommendDatabaseMode(checks) {
|
|
763
774
|
const reachableLocal = checks.find((check) => check.name === 'localhost:5432')?.status === 'pass';
|
|
764
|
-
const docker = checks.find((check) => check.name === 'docker')?.status === 'pass';
|
|
765
|
-
if (reachableLocal)
|
|
775
|
+
const docker = checks.find((check) => check.name === 'docker daemon')?.status === 'pass';
|
|
776
|
+
if (reachableLocal && !docker)
|
|
766
777
|
return 'local';
|
|
767
778
|
if (docker)
|
|
768
779
|
return 'docker';
|
|
780
|
+
if (reachableLocal)
|
|
781
|
+
return 'local';
|
|
769
782
|
return 'managed';
|
|
770
783
|
}
|
|
771
784
|
function quickInstallGuidanceLines() {
|
|
772
785
|
if (process.platform === 'win32') {
|
|
773
786
|
return [
|
|
774
787
|
'Docker: winget install Docker.DockerDesktop',
|
|
775
|
-
'PostgreSQL:
|
|
788
|
+
'PostgreSQL: install PostgreSQL 17 and a pgvector-capable build or extension package.',
|
|
776
789
|
];
|
|
777
790
|
}
|
|
778
791
|
if (process.platform === 'darwin') {
|
|
779
792
|
return [
|
|
780
793
|
'Docker: brew install --cask docker',
|
|
781
|
-
'PostgreSQL: brew install postgresql@17',
|
|
794
|
+
'PostgreSQL: brew install postgresql@17 plus pgvector for that server.',
|
|
782
795
|
];
|
|
783
796
|
}
|
|
784
797
|
return [
|
|
785
798
|
'Docker: install Docker Engine using your distro package manager or the official Docker instructions.',
|
|
786
|
-
'PostgreSQL: install PostgreSQL 16+ using your distro package manager.',
|
|
799
|
+
'PostgreSQL: install PostgreSQL 16+ and the pgvector extension using your distro package manager.',
|
|
787
800
|
];
|
|
788
801
|
}
|
|
789
802
|
function printQuickInstallGuidance() {
|
|
@@ -1181,6 +1194,32 @@ function hasDockerInstalled() {
|
|
|
1181
1194
|
return false;
|
|
1182
1195
|
}
|
|
1183
1196
|
}
|
|
1197
|
+
function inspectDockerAvailability() {
|
|
1198
|
+
if (!hasDockerInstalled()) {
|
|
1199
|
+
return {
|
|
1200
|
+
installed: false,
|
|
1201
|
+
daemonReachable: false,
|
|
1202
|
+
detail: 'Docker is not installed or not on PATH.',
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
const proc = runCommandCapture('docker', ['info', '--format', '{{.ServerVersion}}']);
|
|
1206
|
+
if (proc.status === 0) {
|
|
1207
|
+
const version = proc.stdout.trim();
|
|
1208
|
+
return {
|
|
1209
|
+
installed: true,
|
|
1210
|
+
daemonReachable: true,
|
|
1211
|
+
detail: version
|
|
1212
|
+
? `Docker daemon is reachable (server ${version}).`
|
|
1213
|
+
: 'Docker daemon is reachable.',
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
const reason = (proc.stderr || proc.stdout).trim() || 'Docker daemon did not respond.';
|
|
1217
|
+
return {
|
|
1218
|
+
installed: true,
|
|
1219
|
+
daemonReachable: false,
|
|
1220
|
+
detail: `Docker CLI is installed, but the daemon is not reachable. ${reason}`,
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1184
1223
|
async function isPortAvailable(port, host = '127.0.0.1') {
|
|
1185
1224
|
return await new Promise((resolve) => {
|
|
1186
1225
|
const server = net_1.default.createServer();
|
|
@@ -1244,9 +1283,19 @@ async function waitForTcpPort(host, port, timeoutMs) {
|
|
|
1244
1283
|
throw new Error(`Timed out waiting for ${host}:${port} to accept TCP connections.`);
|
|
1245
1284
|
}
|
|
1246
1285
|
async function runDockerPostgresContainer(options) {
|
|
1286
|
+
const docker = inspectDockerAvailability();
|
|
1287
|
+
if (!docker.installed) {
|
|
1288
|
+
throw new Error('Docker CLI is not installed or not on PATH. Install Docker Desktop or Docker Engine before using --db-mode docker.');
|
|
1289
|
+
}
|
|
1290
|
+
if (!docker.daemonReachable) {
|
|
1291
|
+
throw new Error(`Docker daemon is not reachable. Start Docker Desktop or Docker Engine, then retry. ${docker.detail}`);
|
|
1292
|
+
}
|
|
1247
1293
|
const inspect = process.platform === 'win32'
|
|
1248
1294
|
? (0, child_process_1.spawnSync)(process.env.ComSpec ?? 'cmd.exe', ['/d', '/c', `docker ps -a --format "{{.Names}}"`], { encoding: 'utf8' })
|
|
1249
1295
|
: (0, child_process_1.spawnSync)('docker', ['ps', '-a', '--format', '{{.Names}}'], { encoding: 'utf8' });
|
|
1296
|
+
if (inspect.status !== 0) {
|
|
1297
|
+
throw new Error(`Failed to inspect Docker containers. ${(inspect.stderr ?? inspect.stdout ?? '').trim() || 'docker ps returned a non-zero exit code.'}`);
|
|
1298
|
+
}
|
|
1250
1299
|
const names = (inspect.stdout ?? '').split(/\r?\n/).map((value) => value.trim()).filter(Boolean);
|
|
1251
1300
|
if (names.includes(options.containerName)) {
|
|
1252
1301
|
const start = process.platform === 'win32'
|
|
@@ -1306,6 +1355,7 @@ async function executeSetupPlan(plan) {
|
|
|
1306
1355
|
}
|
|
1307
1356
|
if (plan.databaseMode === 'local') {
|
|
1308
1357
|
await ensurePostgresDatabaseExists(plan.databaseUrl);
|
|
1358
|
+
await ensureLocalPostgresPgvectorAvailable(plan.databaseUrl);
|
|
1309
1359
|
}
|
|
1310
1360
|
await runBundledScript('setup', [], {
|
|
1311
1361
|
DATABASE_URL: plan.databaseUrl,
|
|
@@ -1313,6 +1363,9 @@ async function executeSetupPlan(plan) {
|
|
|
1313
1363
|
});
|
|
1314
1364
|
}
|
|
1315
1365
|
catch (error) {
|
|
1366
|
+
if (plan.databaseMode === 'docker' && error instanceof Error && /Docker daemon is not reachable|Docker CLI is not installed/i.test(error.message)) {
|
|
1367
|
+
throw error;
|
|
1368
|
+
}
|
|
1316
1369
|
throw formatSetupBootstrapFailure(error);
|
|
1317
1370
|
}
|
|
1318
1371
|
}
|
|
@@ -1437,7 +1490,7 @@ function defaultsSetupPlan(args) {
|
|
|
1437
1490
|
if (!explicit) {
|
|
1438
1491
|
if (hasCommandInstalled('psql'))
|
|
1439
1492
|
return 'local';
|
|
1440
|
-
if (
|
|
1493
|
+
if (inspectDockerAvailability().daemonReachable)
|
|
1441
1494
|
return 'docker';
|
|
1442
1495
|
return 'managed';
|
|
1443
1496
|
}
|
|
@@ -1699,15 +1752,20 @@ async function canConnectTcp(port, host = '127.0.0.1', timeoutMs = 800) {
|
|
|
1699
1752
|
});
|
|
1700
1753
|
}
|
|
1701
1754
|
async function collectDependencyChecks() {
|
|
1702
|
-
const docker =
|
|
1755
|
+
const docker = inspectDockerAvailability();
|
|
1703
1756
|
const psql = hasCommandInstalled('psql');
|
|
1704
1757
|
const pgIsReady = hasCommandInstalled('pg_isready');
|
|
1705
1758
|
const postgresPort = await canConnectTcp(5432);
|
|
1706
1759
|
const checks = [
|
|
1707
1760
|
{
|
|
1708
1761
|
name: 'docker',
|
|
1709
|
-
status: docker ? 'pass' : 'warn',
|
|
1710
|
-
detail: docker ? 'Docker is installed.' :
|
|
1762
|
+
status: docker.installed ? 'pass' : 'warn',
|
|
1763
|
+
detail: docker.installed ? 'Docker CLI is installed.' : docker.detail,
|
|
1764
|
+
},
|
|
1765
|
+
{
|
|
1766
|
+
name: 'docker daemon',
|
|
1767
|
+
status: docker.daemonReachable ? 'pass' : 'warn',
|
|
1768
|
+
detail: docker.detail,
|
|
1711
1769
|
},
|
|
1712
1770
|
{
|
|
1713
1771
|
name: 'psql',
|
|
@@ -1942,6 +2000,46 @@ async function ensurePostgresDatabaseExists(databaseUrl) {
|
|
|
1942
2000
|
throw new Error(`Failed to create local PostgreSQL database '${databaseName}'. ${create.stderr?.trim() || create.stdout?.trim() || 'psql returned a non-zero exit code.'}`);
|
|
1943
2001
|
}
|
|
1944
2002
|
}
|
|
2003
|
+
async function ensureLocalPostgresPgvectorAvailable(databaseUrl) {
|
|
2004
|
+
const parsed = parsePostgresConnectionString(databaseUrl);
|
|
2005
|
+
if (!isLocalPostgresHost(parsed.hostname)) {
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
2008
|
+
if (!hasCommandInstalled('psql')) {
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
const adminDatabase = parsed.searchParams.get('admin_db')?.trim() || 'postgres';
|
|
2012
|
+
const host = parsed.hostname;
|
|
2013
|
+
const port = parsed.port || '5432';
|
|
2014
|
+
const user = decodeURIComponent(parsed.username || 'postgres');
|
|
2015
|
+
const password = decodeURIComponent(parsed.password || '');
|
|
2016
|
+
const extraEnv = password ? { PGPASSWORD: password } : undefined;
|
|
2017
|
+
const probe = (0, child_process_1.spawnSync)('psql', [
|
|
2018
|
+
'-h',
|
|
2019
|
+
host,
|
|
2020
|
+
'-p',
|
|
2021
|
+
port,
|
|
2022
|
+
'-U',
|
|
2023
|
+
user,
|
|
2024
|
+
'-d',
|
|
2025
|
+
adminDatabase,
|
|
2026
|
+
'-tAc',
|
|
2027
|
+
"SELECT 1 FROM pg_available_extensions WHERE name = 'vector';",
|
|
2028
|
+
], {
|
|
2029
|
+
encoding: 'utf8',
|
|
2030
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
2031
|
+
env: {
|
|
2032
|
+
...process.env,
|
|
2033
|
+
...extraEnv,
|
|
2034
|
+
},
|
|
2035
|
+
});
|
|
2036
|
+
if (probe.status !== 0) {
|
|
2037
|
+
throw new Error(`Failed to inspect local PostgreSQL for pgvector availability. ${probe.stderr?.trim() || probe.stdout?.trim() || 'psql returned a non-zero exit code.'}`);
|
|
2038
|
+
}
|
|
2039
|
+
if ((probe.stdout ?? '').trim() !== '1') {
|
|
2040
|
+
throw new Error('Local PostgreSQL server does not have the pgvector extension installed.');
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
1945
2043
|
function detectPythonLauncher() {
|
|
1946
2044
|
const candidates = process.platform === 'win32'
|
|
1947
2045
|
? [
|
|
@@ -2270,6 +2368,32 @@ function canScheduleWindowsGlobalNpmSelfUpgrade(context) {
|
|
|
2270
2368
|
function escapeForSingleQuotedPowerShell(value) {
|
|
2271
2369
|
return value.replace(/'/g, "''");
|
|
2272
2370
|
}
|
|
2371
|
+
function resolveWindowsDetachedExecutable(executable) {
|
|
2372
|
+
if (path_1.default.isAbsolute(executable)) {
|
|
2373
|
+
return executable;
|
|
2374
|
+
}
|
|
2375
|
+
const candidates = executable.toLowerCase().endsWith('.cmd') || executable.toLowerCase().endsWith('.exe')
|
|
2376
|
+
? [executable]
|
|
2377
|
+
: [`${executable}.cmd`, `${executable}.exe`, executable];
|
|
2378
|
+
for (const candidate of candidates) {
|
|
2379
|
+
const probe = (0, child_process_1.spawnSync)('where', [candidate], { encoding: 'utf8' });
|
|
2380
|
+
if (probe.status === 0) {
|
|
2381
|
+
const resolved = (probe.stdout ?? '').split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
2382
|
+
if (resolved) {
|
|
2383
|
+
if (!path_1.default.extname(resolved)) {
|
|
2384
|
+
const cmdVariant = `${resolved}.cmd`;
|
|
2385
|
+
const exeVariant = `${resolved}.exe`;
|
|
2386
|
+
if (fs_1.default.existsSync(cmdVariant))
|
|
2387
|
+
return cmdVariant;
|
|
2388
|
+
if (fs_1.default.existsSync(exeVariant))
|
|
2389
|
+
return exeVariant;
|
|
2390
|
+
}
|
|
2391
|
+
return resolved;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
return executable;
|
|
2396
|
+
}
|
|
2273
2397
|
function resolveDetachedUpgradeCwd(command) {
|
|
2274
2398
|
const desired = command.cwd?.trim();
|
|
2275
2399
|
if (!desired) {
|
|
@@ -2283,12 +2407,30 @@ function resolveDetachedUpgradeCwd(command) {
|
|
|
2283
2407
|
}
|
|
2284
2408
|
return normalized;
|
|
2285
2409
|
}
|
|
2410
|
+
function launchDetachedWindowsPowerShellFile(scriptPath, cwd) {
|
|
2411
|
+
const command = [
|
|
2412
|
+
'Start-Process',
|
|
2413
|
+
'-WindowStyle Hidden',
|
|
2414
|
+
`-WorkingDirectory '${escapeForSingleQuotedPowerShell(cwd)}'`,
|
|
2415
|
+
"-FilePath 'powershell.exe'",
|
|
2416
|
+
`-ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File','${escapeForSingleQuotedPowerShell(scriptPath)}')`,
|
|
2417
|
+
].join(' ');
|
|
2418
|
+
const proc = (0, child_process_1.spawnSync)('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', command], {
|
|
2419
|
+
encoding: 'utf8',
|
|
2420
|
+
cwd,
|
|
2421
|
+
env: process.env,
|
|
2422
|
+
windowsHide: true,
|
|
2423
|
+
});
|
|
2424
|
+
if (proc.status !== 0) {
|
|
2425
|
+
throw new Error(`Failed to schedule detached PowerShell handoff. ${(proc.stderr || proc.stdout).trim() || 'powershell returned a non-zero exit code.'}`);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2286
2428
|
function scheduleDetachedWindowsGlobalNpmUpgrade(command, postCommand) {
|
|
2287
2429
|
const neutralCwd = resolveDetachedUpgradeCwd(command);
|
|
2288
2430
|
const parentPid = process.pid;
|
|
2289
|
-
const powershell = 'powershell.exe';
|
|
2290
2431
|
const escapedCwd = escapeForSingleQuotedPowerShell(neutralCwd);
|
|
2291
|
-
const
|
|
2432
|
+
const detachedExecutable = resolveWindowsDetachedExecutable(command.executable);
|
|
2433
|
+
const escapedExecutable = escapeForSingleQuotedPowerShell(detachedExecutable);
|
|
2292
2434
|
const escapedArgs = command.args.map((arg) => `'${escapeForSingleQuotedPowerShell(arg)}'`).join(', ');
|
|
2293
2435
|
const script = [
|
|
2294
2436
|
`$parentPid = ${parentPid}`,
|
|
@@ -2297,15 +2439,9 @@ function scheduleDetachedWindowsGlobalNpmUpgrade(command, postCommand) {
|
|
|
2297
2439
|
`& '${escapedExecutable}' @(${escapedArgs})`,
|
|
2298
2440
|
...(postCommand ? [`if ($LASTEXITCODE -eq 0) { ${postCommand} }`] : []),
|
|
2299
2441
|
'exit $LASTEXITCODE',
|
|
2300
|
-
].join('
|
|
2301
|
-
const
|
|
2302
|
-
|
|
2303
|
-
stdio: 'ignore',
|
|
2304
|
-
windowsHide: true,
|
|
2305
|
-
cwd: neutralCwd,
|
|
2306
|
-
env: process.env,
|
|
2307
|
-
});
|
|
2308
|
-
child.unref();
|
|
2442
|
+
].join(';\r\n');
|
|
2443
|
+
const scriptPath = writeDetachedWindowsScript('iranti-upgrade', `${script};\r\n`);
|
|
2444
|
+
launchDetachedWindowsPowerShellFile(scriptPath, neutralCwd);
|
|
2309
2445
|
}
|
|
2310
2446
|
function verifyPythonInstall(command) {
|
|
2311
2447
|
const version = detectPythonInstalledVersion(command);
|
|
@@ -2684,6 +2820,7 @@ async function stopUninstallProcesses(processes) {
|
|
|
2684
2820
|
}
|
|
2685
2821
|
function buildDetachedWindowsUninstallScript(options) {
|
|
2686
2822
|
const lines = [
|
|
2823
|
+
"$ErrorActionPreference = 'SilentlyContinue'",
|
|
2687
2824
|
`$parentPid = ${options.parentPid}`,
|
|
2688
2825
|
'while (Get-Process -Id $parentPid -ErrorAction SilentlyContinue) { Start-Sleep -Milliseconds 500 }',
|
|
2689
2826
|
];
|
|
@@ -2691,15 +2828,20 @@ function buildDetachedWindowsUninstallScript(options) {
|
|
|
2691
2828
|
lines.push(`taskkill /PID ${pid} /T /F > $null 2>&1`);
|
|
2692
2829
|
}
|
|
2693
2830
|
if (options.removeCodex) {
|
|
2694
|
-
|
|
2695
|
-
lines.push(
|
|
2831
|
+
const codexExecutable = escapeForSingleQuotedPowerShell(options.codexExecutable ?? resolveWindowsDetachedExecutable('codex'));
|
|
2832
|
+
lines.push(`$codexExe = '${codexExecutable}'`);
|
|
2833
|
+
lines.push("if (Test-Path -LiteralPath $codexExe) { & $codexExe mcp get iranti --json > $null 2>&1; if ($LASTEXITCODE -eq 0) { & $codexExe mcp remove iranti > $null 2>&1 } }");
|
|
2696
2834
|
}
|
|
2697
2835
|
if (options.removeGlobalNpm) {
|
|
2698
|
-
|
|
2836
|
+
const npmExecutable = escapeForSingleQuotedPowerShell(options.npmExecutable ?? resolveWindowsDetachedExecutable('npm'));
|
|
2837
|
+
lines.push(`$npmExe = '${npmExecutable}'`);
|
|
2838
|
+
lines.push("if (Test-Path -LiteralPath $npmExe) { & $npmExe uninstall -g iranti > $null 2>&1 }");
|
|
2699
2839
|
}
|
|
2700
2840
|
if (options.python) {
|
|
2701
2841
|
const args = options.python.args.map((arg) => `'${escapeForSingleQuotedPowerShell(arg)}'`).join(', ');
|
|
2702
|
-
|
|
2842
|
+
const pythonExecutable = escapeForSingleQuotedPowerShell(resolveWindowsDetachedExecutable(options.python.executable));
|
|
2843
|
+
lines.push(`$pythonExe = '${pythonExecutable}'`);
|
|
2844
|
+
lines.push(`if (Test-Path -LiteralPath $pythonExe) { & $pythonExe @(${args}) > $null 2>&1 }`);
|
|
2703
2845
|
}
|
|
2704
2846
|
for (const filePath of options.artifactFiles) {
|
|
2705
2847
|
lines.push(`if (Test-Path -LiteralPath '${escapeForSingleQuotedPowerShell(filePath)}') { Remove-Item -LiteralPath '${escapeForSingleQuotedPowerShell(filePath)}' -Force }`);
|
|
@@ -2708,7 +2850,13 @@ function buildDetachedWindowsUninstallScript(options) {
|
|
|
2708
2850
|
lines.push(`if (Test-Path -LiteralPath '${escapeForSingleQuotedPowerShell(dirPath)}') { Remove-Item -LiteralPath '${escapeForSingleQuotedPowerShell(dirPath)}' -Recurse -Force }`);
|
|
2709
2851
|
}
|
|
2710
2852
|
lines.push('exit 0');
|
|
2711
|
-
return lines.join('
|
|
2853
|
+
return `${lines.join(';\r\n')};\r\n`;
|
|
2854
|
+
}
|
|
2855
|
+
function writeDetachedWindowsScript(prefix, scriptContents) {
|
|
2856
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `${prefix}-`));
|
|
2857
|
+
const scriptPath = path_1.default.join(tempDir, `${prefix}.ps1`);
|
|
2858
|
+
fs_1.default.writeFileSync(scriptPath, scriptContents, 'utf8');
|
|
2859
|
+
return scriptPath;
|
|
2712
2860
|
}
|
|
2713
2861
|
async function uninstallCommand(args) {
|
|
2714
2862
|
const scope = normalizeScope(getFlag(args, 'scope'));
|
|
@@ -2786,17 +2934,13 @@ async function uninstallCommand(args) {
|
|
|
2786
2934
|
removeCodex: actions.removeCodexRegistration,
|
|
2787
2935
|
python: actions.removePython ? pythonCommand : null,
|
|
2788
2936
|
removeGlobalNpm: actions.removeGlobalNpm,
|
|
2937
|
+
npmExecutable: resolveWindowsDetachedExecutable('npm'),
|
|
2938
|
+
codexExecutable: resolveWindowsDetachedExecutable('codex'),
|
|
2789
2939
|
runtimeRoots: actions.removeRuntimeRoots ? runtimeRoots.map((entry) => entry.path) : [],
|
|
2790
2940
|
artifactFiles,
|
|
2791
2941
|
});
|
|
2792
|
-
const
|
|
2793
|
-
|
|
2794
|
-
stdio: 'ignore',
|
|
2795
|
-
windowsHide: true,
|
|
2796
|
-
cwd: os_1.default.homedir(),
|
|
2797
|
-
env: process.env,
|
|
2798
|
-
});
|
|
2799
|
-
child.unref();
|
|
2942
|
+
const scriptPath = writeDetachedWindowsScript('iranti-uninstall', script);
|
|
2943
|
+
launchDetachedWindowsPowerShellFile(scriptPath, os_1.default.homedir());
|
|
2800
2944
|
execution.push({
|
|
2801
2945
|
label: 'detached-uninstall',
|
|
2802
2946
|
status: 'warn',
|
|
@@ -3130,7 +3274,7 @@ async function setupCommand(args) {
|
|
|
3130
3274
|
const dependencyChecks = await collectDependencyChecks();
|
|
3131
3275
|
printDependencyChecks(dependencyChecks);
|
|
3132
3276
|
const recommendedDatabaseMode = recommendDatabaseMode(dependencyChecks);
|
|
3133
|
-
console.log(`${infoLabel()} Recommended database path: ${recommendedDatabaseMode === 'local' ? 'local PostgreSQL' : recommendedDatabaseMode === 'docker' ? 'Docker-hosted PostgreSQL' : 'managed PostgreSQL'}`);
|
|
3277
|
+
console.log(`${infoLabel()} Recommended database path: ${recommendedDatabaseMode === 'local' ? 'local PostgreSQL (pgvector required)' : recommendedDatabaseMode === 'docker' ? 'Docker-hosted PostgreSQL' : 'managed PostgreSQL'}`);
|
|
3134
3278
|
if (recommendedDatabaseMode === 'managed' && dependencyChecks.every((check) => check.status === 'warn')) {
|
|
3135
3279
|
printQuickInstallGuidance();
|
|
3136
3280
|
}
|
|
@@ -3177,7 +3321,8 @@ async function setupCommand(args) {
|
|
|
3177
3321
|
}
|
|
3178
3322
|
const existingPort = Number.parseInt(existingInstance?.env.IRANTI_PORT ?? '3001', 10);
|
|
3179
3323
|
const port = await chooseAvailablePort(prompt, 'API port', existingPort, Boolean(existingInstance));
|
|
3180
|
-
const
|
|
3324
|
+
const dockerStatus = inspectDockerAvailability();
|
|
3325
|
+
const dockerAvailable = dockerStatus.daemonReachable;
|
|
3181
3326
|
const psqlAvailable = hasCommandInstalled('psql');
|
|
3182
3327
|
let dbUrl = '';
|
|
3183
3328
|
let bootstrapDatabase = false;
|
|
@@ -3210,7 +3355,7 @@ async function setupCommand(args) {
|
|
|
3210
3355
|
if (dbMode === 'docker') {
|
|
3211
3356
|
databaseMode = 'docker';
|
|
3212
3357
|
if (!dockerAvailable) {
|
|
3213
|
-
console.log(`${warnLabel()}
|
|
3358
|
+
console.log(`${warnLabel()} ${dockerStatus.detail}`);
|
|
3214
3359
|
continue;
|
|
3215
3360
|
}
|
|
3216
3361
|
const dbHostPort = await chooseAvailablePort(prompt, 'Docker PostgreSQL host port', 5432, false);
|
|
@@ -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.20',
|
|
148
148
|
});
|
|
149
149
|
server.registerTool('iranti_handshake', {
|
|
150
150
|
description: `Initialize or refresh an agent's working-memory brief for the current task.
|
package/dist/scripts/setup.js
CHANGED
|
@@ -9,15 +9,35 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const client_1 = require("../src/library/client");
|
|
11
11
|
const escalationPaths_1 = require("../src/lib/escalationPaths");
|
|
12
|
-
|
|
12
|
+
function resolvePackageRoot() {
|
|
13
|
+
let dir = __dirname;
|
|
14
|
+
for (let i = 0; i < 8; i += 1) {
|
|
15
|
+
const pkgPath = path_1.default.join(dir, 'package.json');
|
|
16
|
+
const prismaSchema = path_1.default.join(dir, 'prisma', 'schema.prisma');
|
|
17
|
+
if (fs_1.default.existsSync(pkgPath) && fs_1.default.existsSync(prismaSchema)) {
|
|
18
|
+
return dir;
|
|
19
|
+
}
|
|
20
|
+
const parent = path_1.default.dirname(dir);
|
|
21
|
+
if (parent === dir)
|
|
22
|
+
break;
|
|
23
|
+
dir = parent;
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`Could not resolve Iranti package root from ${__dirname}`);
|
|
26
|
+
}
|
|
27
|
+
const PACKAGE_ROOT = resolvePackageRoot();
|
|
28
|
+
const PRISMA_SCHEMA = path_1.default.join(PACKAGE_ROOT, 'prisma', 'schema.prisma');
|
|
29
|
+
const BASE_ENV = { ...process.env };
|
|
13
30
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
14
|
-
function run(command, label) {
|
|
31
|
+
function run(command, label, extraEnv) {
|
|
15
32
|
console.log(` Running: ${label}...`);
|
|
16
33
|
try {
|
|
17
34
|
(0, child_process_1.execSync)(command, {
|
|
18
35
|
stdio: 'inherit',
|
|
19
36
|
cwd: PACKAGE_ROOT,
|
|
20
|
-
env:
|
|
37
|
+
env: {
|
|
38
|
+
...BASE_ENV,
|
|
39
|
+
...extraEnv,
|
|
40
|
+
},
|
|
21
41
|
});
|
|
22
42
|
}
|
|
23
43
|
catch {
|
|
@@ -26,7 +46,7 @@ function run(command, label) {
|
|
|
26
46
|
}
|
|
27
47
|
}
|
|
28
48
|
function scriptCommand(distScriptName, sourceScriptPath) {
|
|
29
|
-
const distScriptPath = path_1.default.
|
|
49
|
+
const distScriptPath = path_1.default.join(PACKAGE_ROOT, 'dist', 'scripts', distScriptName);
|
|
30
50
|
if (fs_1.default.existsSync(distScriptPath)) {
|
|
31
51
|
return `node ${JSON.stringify(distScriptPath)}`;
|
|
32
52
|
}
|
|
@@ -57,13 +77,18 @@ async function setup() {
|
|
|
57
77
|
process.env.DATABASE_URL = dbUrl;
|
|
58
78
|
(0, client_1.initDb)(dbUrl);
|
|
59
79
|
}
|
|
80
|
+
const runtimeEnv = dbUrl
|
|
81
|
+
? {
|
|
82
|
+
DATABASE_URL: dbUrl,
|
|
83
|
+
}
|
|
84
|
+
: undefined;
|
|
60
85
|
// 1. Run migrations
|
|
61
86
|
console.log('Step 1 — Running database migrations...');
|
|
62
|
-
run(
|
|
87
|
+
run(`npx prisma migrate deploy --schema ${JSON.stringify(PRISMA_SCHEMA)}`, 'prisma migrate deploy', runtimeEnv);
|
|
63
88
|
console.log(' ✓ Migrations complete\n');
|
|
64
89
|
// 2. Generate Prisma client
|
|
65
90
|
console.log('Step 2 — Generating Prisma client...');
|
|
66
|
-
run(
|
|
91
|
+
run(`npx prisma generate --schema ${JSON.stringify(PRISMA_SCHEMA)}`, 'prisma generate', runtimeEnv);
|
|
67
92
|
console.log(' ✓ Client generated\n');
|
|
68
93
|
// 3. Seed Staff Namespace if not already seeded
|
|
69
94
|
console.log('Step 3 — Seeding Staff Namespace...');
|
package/dist/src/api/server.js
CHANGED
|
@@ -39,7 +39,7 @@ const INSTANCE_DIR = process.env.IRANTI_INSTANCE_DIR?.trim()
|
|
|
39
39
|
const INSTANCE_RUNTIME_FILE = process.env.IRANTI_INSTANCE_RUNTIME_FILE?.trim()
|
|
40
40
|
|| (INSTANCE_DIR ? (0, runtimeLifecycle_1.runtimeFileForInstance)(INSTANCE_DIR) : null);
|
|
41
41
|
const INSTANCE_NAME = process.env.IRANTI_INSTANCE_NAME?.trim() || (INSTANCE_DIR ? path_1.default.basename(INSTANCE_DIR) : 'adhoc');
|
|
42
|
-
const VERSION = '0.2.
|
|
42
|
+
const VERSION = '0.2.20';
|
|
43
43
|
try {
|
|
44
44
|
fs_1.default.mkdirSync(path_1.default.dirname(REQUEST_LOG_FILE), { recursive: true });
|
|
45
45
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iranti",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.20",
|
|
4
4
|
"description": "Memory infrastructure for multi-agent AI systems",
|
|
5
5
|
"main": "dist/src/sdk/index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"dist/scripts/api-key-list.js",
|
|
17
17
|
"dist/scripts/api-key-revoke.js",
|
|
18
18
|
"bin/iranti.js",
|
|
19
|
+
"prisma.config.ts",
|
|
19
20
|
"prisma/schema.prisma",
|
|
20
21
|
"prisma/migrations/**/*",
|
|
21
22
|
".env.example",
|
package/prisma.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// This file was generated by Prisma, and assumes you have installed the following:
|
|
2
|
+
// npm install --save-dev prisma dotenv
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
import { defineConfig } from "prisma/config";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
schema: "prisma/schema.prisma",
|
|
8
|
+
migrations: {
|
|
9
|
+
path: "prisma/migrations",
|
|
10
|
+
},
|
|
11
|
+
datasource: {
|
|
12
|
+
url: process.env["DATABASE_URL"],
|
|
13
|
+
},
|
|
14
|
+
});
|