@automagik/omni 2.260520.10 → 2.260520.11
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 +342 -143
- package/dist/lib/canonical-pgserve.d.ts.map +1 -1
- package/dist/lib/role-cutover.d.ts +92 -0
- package/dist/lib/role-cutover.d.ts.map +1 -0
- package/dist/runtime-env.d.ts +9 -0
- package/dist/runtime-env.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22772,7 +22772,7 @@ var require_node_transport = __commonJS((exports) => {
|
|
|
22772
22772
|
var util_1 = require_util();
|
|
22773
22773
|
var tls_1 = __require("tls");
|
|
22774
22774
|
var { resolve: resolve2 } = __require("path");
|
|
22775
|
-
var { readFile, existsSync:
|
|
22775
|
+
var { readFile, existsSync: existsSync12 } = __require("fs");
|
|
22776
22776
|
var dns = __require("dns");
|
|
22777
22777
|
var VERSION2 = "2.29.3";
|
|
22778
22778
|
var LANG = "nats.js";
|
|
@@ -22888,7 +22888,7 @@ var require_node_transport = __commonJS((exports) => {
|
|
|
22888
22888
|
const d = (0, nats_base_client_1.deferred)();
|
|
22889
22889
|
try {
|
|
22890
22890
|
fn = resolve2(fn);
|
|
22891
|
-
if (!
|
|
22891
|
+
if (!existsSync12(fn)) {
|
|
22892
22892
|
d.reject(new Error(`${fn} doesn't exist`));
|
|
22893
22893
|
}
|
|
22894
22894
|
readFile(fn, (err, data2) => {
|
|
@@ -61931,8 +61931,8 @@ var init_a2a_provider = __esm(() => {
|
|
|
61931
61931
|
|
|
61932
61932
|
// ../core/src/providers/nats-genie-provider.ts
|
|
61933
61933
|
import { mkdir, writeFile } from "fs/promises";
|
|
61934
|
-
import { homedir as
|
|
61935
|
-
import { join as
|
|
61934
|
+
import { homedir as homedir9 } from "os";
|
|
61935
|
+
import { join as join15 } from "path";
|
|
61936
61936
|
|
|
61937
61937
|
class NatsGenieProvider {
|
|
61938
61938
|
id;
|
|
@@ -62122,10 +62122,10 @@ class NatsGenieProvider {
|
|
|
62122
62122
|
}
|
|
62123
62123
|
async writeDeadLetter(payload, error2) {
|
|
62124
62124
|
try {
|
|
62125
|
-
const dlDir =
|
|
62125
|
+
const dlDir = join15(homedir9(), ".omni", "dead-letters");
|
|
62126
62126
|
await mkdir(dlDir, { recursive: true });
|
|
62127
62127
|
const filename = `nats-genie-${Date.now()}-${payload.chatId}.json`;
|
|
62128
|
-
await writeFile(
|
|
62128
|
+
await writeFile(join15(dlDir, filename), JSON.stringify({
|
|
62129
62129
|
payload,
|
|
62130
62130
|
error: error2 instanceof Error ? error2.message : String(error2),
|
|
62131
62131
|
timestamp: new Date().toISOString()
|
|
@@ -64004,7 +64004,7 @@ var init_sql = __esm(() => {
|
|
|
64004
64004
|
return new SQL([new StringChunk(str)]);
|
|
64005
64005
|
}
|
|
64006
64006
|
sql2.raw = raw2;
|
|
64007
|
-
function
|
|
64007
|
+
function join16(chunks, separator) {
|
|
64008
64008
|
const result = [];
|
|
64009
64009
|
for (const [i6, chunk] of chunks.entries()) {
|
|
64010
64010
|
if (i6 > 0 && separator !== undefined) {
|
|
@@ -64014,7 +64014,7 @@ var init_sql = __esm(() => {
|
|
|
64014
64014
|
}
|
|
64015
64015
|
return new SQL(result);
|
|
64016
64016
|
}
|
|
64017
|
-
sql2.join =
|
|
64017
|
+
sql2.join = join16;
|
|
64018
64018
|
function identifier(value) {
|
|
64019
64019
|
return new Name(value);
|
|
64020
64020
|
}
|
|
@@ -67241,7 +67241,7 @@ var init_select2 = __esm(() => {
|
|
|
67241
67241
|
return (table2, on) => {
|
|
67242
67242
|
const baseTableName = this.tableName;
|
|
67243
67243
|
const tableName = getTableLikeName(table2);
|
|
67244
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
67244
|
+
if (typeof tableName === "string" && this.config.joins?.some((join16) => join16.alias === tableName)) {
|
|
67245
67245
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
67246
67246
|
}
|
|
67247
67247
|
if (!this.isPartialSelect) {
|
|
@@ -67752,7 +67752,7 @@ var init_update = __esm(() => {
|
|
|
67752
67752
|
createJoin(joinType) {
|
|
67753
67753
|
return (table2, on) => {
|
|
67754
67754
|
const tableName = getTableLikeName(table2);
|
|
67755
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
67755
|
+
if (typeof tableName === "string" && this.config.joins.some((join16) => join16.alias === tableName)) {
|
|
67756
67756
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
67757
67757
|
}
|
|
67758
67758
|
if (typeof on === "function") {
|
|
@@ -67802,10 +67802,10 @@ var init_update = __esm(() => {
|
|
|
67802
67802
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
67803
67803
|
fields[tableName] = fromFields;
|
|
67804
67804
|
}
|
|
67805
|
-
for (const
|
|
67806
|
-
const tableName2 = getTableLikeName(
|
|
67807
|
-
if (typeof tableName2 === "string" && !is(
|
|
67808
|
-
const fromFields = this.getTableLikeFields(
|
|
67805
|
+
for (const join16 of this.config.joins) {
|
|
67806
|
+
const tableName2 = getTableLikeName(join16.table);
|
|
67807
|
+
if (typeof tableName2 === "string" && !is(join16.table, SQL)) {
|
|
67808
|
+
const fromFields = this.getTableLikeFields(join16.table);
|
|
67809
67809
|
fields[tableName2] = fromFields;
|
|
67810
67810
|
}
|
|
67811
67811
|
}
|
|
@@ -82474,7 +82474,7 @@ var require_path = __commonJS((exports) => {
|
|
|
82474
82474
|
function isAbsolute(path) {
|
|
82475
82475
|
return path.charAt(0) === "/";
|
|
82476
82476
|
}
|
|
82477
|
-
function
|
|
82477
|
+
function join20(...args) {
|
|
82478
82478
|
return normalizePath(args.join("/"));
|
|
82479
82479
|
}
|
|
82480
82480
|
function dirname6(path) {
|
|
@@ -82499,7 +82499,7 @@ var require_path = __commonJS((exports) => {
|
|
|
82499
82499
|
exports.basename = basename6;
|
|
82500
82500
|
exports.dirname = dirname6;
|
|
82501
82501
|
exports.isAbsolute = isAbsolute;
|
|
82502
|
-
exports.join =
|
|
82502
|
+
exports.join = join20;
|
|
82503
82503
|
exports.normalizePath = normalizePath;
|
|
82504
82504
|
exports.relative = relative;
|
|
82505
82505
|
exports.resolve = resolve4;
|
|
@@ -124067,7 +124067,7 @@ import { fileURLToPath } from "url";
|
|
|
124067
124067
|
// package.json
|
|
124068
124068
|
var package_default = {
|
|
124069
124069
|
name: "@automagik/omni",
|
|
124070
|
-
version: "2.260520.
|
|
124070
|
+
version: "2.260520.11",
|
|
124071
124071
|
description: "LLM-optimized CLI for Omni",
|
|
124072
124072
|
type: "module",
|
|
124073
124073
|
bin: {
|
|
@@ -125268,7 +125268,7 @@ function pm2NotFoundError() {
|
|
|
125268
125268
|
}
|
|
125269
125269
|
|
|
125270
125270
|
// src/runtime-env.ts
|
|
125271
|
-
import { join as
|
|
125271
|
+
import { join as join7 } from "path";
|
|
125272
125272
|
|
|
125273
125273
|
// src/lib/pgserve-transport.ts
|
|
125274
125274
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -125335,6 +125335,182 @@ function buildDatabaseUrlForTransport(transport, database, options = {}) {
|
|
|
125335
125335
|
return `postgresql://${auth}@${transport.host}:${transport.port}/${encodeURIComponent(database)}`;
|
|
125336
125336
|
}
|
|
125337
125337
|
|
|
125338
|
+
// src/lib/role-cutover.ts
|
|
125339
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
125340
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
125341
|
+
import { homedir as homedir3 } from "os";
|
|
125342
|
+
import { join as join6 } from "path";
|
|
125343
|
+
var NAME_PREFIX = "pgserve_";
|
|
125344
|
+
var ROLE_SUFFIX = "_role";
|
|
125345
|
+
var POSTGRES_MAX_IDENT = 63;
|
|
125346
|
+
var FINGERPRINT_HEX_LEN = 12;
|
|
125347
|
+
var OMNI_PUBLISHER = "omni";
|
|
125348
|
+
var SAFE_IDENT = /^[a-z_][a-z0-9_]*$/;
|
|
125349
|
+
function sha256Hex(input) {
|
|
125350
|
+
return createHash2("sha256").update(input, "utf8").digest("hex");
|
|
125351
|
+
}
|
|
125352
|
+
function sanitizeSlug(input) {
|
|
125353
|
+
return input.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/^_+|_+$/g, "").replace(/__+/g, "_");
|
|
125354
|
+
}
|
|
125355
|
+
function deriveOmniScopedRoleName() {
|
|
125356
|
+
const fp = sha256Hex("@automagik/omni").slice(0, FINGERPRINT_HEX_LEN);
|
|
125357
|
+
const slug = sanitizeSlug(OMNI_PUBLISHER);
|
|
125358
|
+
const budget = POSTGRES_MAX_IDENT - NAME_PREFIX.length - 1 - fp.length - ROLE_SUFFIX.length;
|
|
125359
|
+
const truncated = slug.slice(0, Math.max(0, budget));
|
|
125360
|
+
return `${NAME_PREFIX}${truncated}_${fp}${ROLE_SUFFIX}`;
|
|
125361
|
+
}
|
|
125362
|
+
function assertSafeIdent(label, value) {
|
|
125363
|
+
if (!SAFE_IDENT.test(value)) {
|
|
125364
|
+
throw new Error(`role-cutover: unsafe ${label} identifier: ${JSON.stringify(value)}`);
|
|
125365
|
+
}
|
|
125366
|
+
}
|
|
125367
|
+
function assertSafePassword(value) {
|
|
125368
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value)) {
|
|
125369
|
+
throw new Error("role-cutover: generated password contains unsafe characters");
|
|
125370
|
+
}
|
|
125371
|
+
}
|
|
125372
|
+
function sentinelDir() {
|
|
125373
|
+
return join6(homedir3(), ".omni");
|
|
125374
|
+
}
|
|
125375
|
+
function sentinelPath() {
|
|
125376
|
+
return join6(sentinelDir(), "scoped-role.json");
|
|
125377
|
+
}
|
|
125378
|
+
function readOmniCutoverSentinel() {
|
|
125379
|
+
const path = sentinelPath();
|
|
125380
|
+
if (!existsSync5(path))
|
|
125381
|
+
return null;
|
|
125382
|
+
try {
|
|
125383
|
+
const raw2 = readFileSync4(path, "utf-8");
|
|
125384
|
+
const parsed = JSON.parse(raw2);
|
|
125385
|
+
if (typeof parsed.roleName === "string" && typeof parsed.database === "string" && typeof parsed.password === "string" && typeof parsed.provisionedAt === "string") {
|
|
125386
|
+
return parsed;
|
|
125387
|
+
}
|
|
125388
|
+
} catch {}
|
|
125389
|
+
return null;
|
|
125390
|
+
}
|
|
125391
|
+
function writeOmniCutoverSentinel(data2) {
|
|
125392
|
+
const dir = sentinelDir();
|
|
125393
|
+
if (!existsSync5(dir))
|
|
125394
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
125395
|
+
const path = sentinelPath();
|
|
125396
|
+
writeFileSync3(path, `${JSON.stringify(data2, null, 2)}
|
|
125397
|
+
`, { mode: 384 });
|
|
125398
|
+
}
|
|
125399
|
+
function isOmniRoleCutoverEnabled() {
|
|
125400
|
+
return process.env.OMNI_ROLE_CUTOVER !== "0";
|
|
125401
|
+
}
|
|
125402
|
+
function generatePassword() {
|
|
125403
|
+
return randomBytes(32).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
125404
|
+
}
|
|
125405
|
+
async function ensureOmniScopedRole(opts) {
|
|
125406
|
+
const enabled = opts.enabled ?? isOmniRoleCutoverEnabled();
|
|
125407
|
+
if (!enabled) {
|
|
125408
|
+
return { status: "skipped", reason: "disabled" };
|
|
125409
|
+
}
|
|
125410
|
+
const database = opts.database ?? "omni";
|
|
125411
|
+
const port = opts.port ?? 5432;
|
|
125412
|
+
const roleName = deriveOmniScopedRoleName();
|
|
125413
|
+
try {
|
|
125414
|
+
assertSafeIdent("role", roleName);
|
|
125415
|
+
assertSafeIdent("database", database);
|
|
125416
|
+
} catch (err) {
|
|
125417
|
+
return { status: "skipped", reason: err.message };
|
|
125418
|
+
}
|
|
125419
|
+
const existing = readOmniCutoverSentinel();
|
|
125420
|
+
const password = existing && existing.roleName === roleName && existing.database === database ? existing.password : generatePassword();
|
|
125421
|
+
try {
|
|
125422
|
+
assertSafePassword(password);
|
|
125423
|
+
} catch (err) {
|
|
125424
|
+
return { status: "skipped", reason: err.message };
|
|
125425
|
+
}
|
|
125426
|
+
const sqlScript = [
|
|
125427
|
+
`DO $$
|
|
125428
|
+
BEGIN
|
|
125429
|
+
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${roleName}') THEN
|
|
125430
|
+
CREATE ROLE "${roleName}" WITH LOGIN PASSWORD '${password}'
|
|
125431
|
+
NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
|
|
125432
|
+
ELSE
|
|
125433
|
+
ALTER ROLE "${roleName}" WITH LOGIN PASSWORD '${password}'
|
|
125434
|
+
NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
|
|
125435
|
+
END IF;
|
|
125436
|
+
END
|
|
125437
|
+
$$;`,
|
|
125438
|
+
`GRANT CONNECT, TEMPORARY ON DATABASE "${database}" TO "${roleName}";`
|
|
125439
|
+
];
|
|
125440
|
+
const dbCreateOk = await runPsql(sqlScript.join(`
|
|
125441
|
+
`), { socketDir: opts.socketDir, port, database: "postgres" });
|
|
125442
|
+
if (!dbCreateOk) {
|
|
125443
|
+
return { status: "skipped", reason: "psql role provisioning failed" };
|
|
125444
|
+
}
|
|
125445
|
+
const grantsScript = [
|
|
125446
|
+
`GRANT USAGE, CREATE ON SCHEMA public TO "${roleName}";`,
|
|
125447
|
+
`GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "${roleName}";`,
|
|
125448
|
+
`GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO "${roleName}";`,
|
|
125449
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "${roleName}";`,
|
|
125450
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO "${roleName}";`,
|
|
125451
|
+
`ALTER DEFAULT PRIVILEGES FOR ROLE "${roleName}" IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "${roleName}";`,
|
|
125452
|
+
`ALTER DEFAULT PRIVILEGES FOR ROLE "${roleName}" IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO "${roleName}";`
|
|
125453
|
+
].join(`
|
|
125454
|
+
`);
|
|
125455
|
+
const grantsOk = await runPsql(grantsScript, { socketDir: opts.socketDir, port, database });
|
|
125456
|
+
if (!grantsOk) {
|
|
125457
|
+
return { status: "skipped", reason: "psql grant step failed" };
|
|
125458
|
+
}
|
|
125459
|
+
writeOmniCutoverSentinel({
|
|
125460
|
+
roleName,
|
|
125461
|
+
database,
|
|
125462
|
+
password,
|
|
125463
|
+
provisionedAt: new Date().toISOString()
|
|
125464
|
+
});
|
|
125465
|
+
return existing && existing.roleName === roleName ? { status: "refreshed", roleName } : { status: "provisioned", roleName };
|
|
125466
|
+
}
|
|
125467
|
+
async function runPsql(sql, opts) {
|
|
125468
|
+
const proc = Bun.spawn({
|
|
125469
|
+
cmd: [
|
|
125470
|
+
"psql",
|
|
125471
|
+
"-h",
|
|
125472
|
+
opts.socketDir,
|
|
125473
|
+
"-p",
|
|
125474
|
+
String(opts.port),
|
|
125475
|
+
"-U",
|
|
125476
|
+
"postgres",
|
|
125477
|
+
"-d",
|
|
125478
|
+
opts.database,
|
|
125479
|
+
"-v",
|
|
125480
|
+
"ON_ERROR_STOP=1",
|
|
125481
|
+
"-c",
|
|
125482
|
+
sql
|
|
125483
|
+
],
|
|
125484
|
+
stdout: "pipe",
|
|
125485
|
+
stderr: "pipe",
|
|
125486
|
+
env: { ...process.env, PGPASSWORD: "postgres" }
|
|
125487
|
+
});
|
|
125488
|
+
const stderr = await new Response(proc.stderr).text();
|
|
125489
|
+
const code = await proc.exited;
|
|
125490
|
+
if (code !== 0) {
|
|
125491
|
+
process.stderr.write(`role-cutover: psql exited ${code}: ${stderr.trim()}
|
|
125492
|
+
`);
|
|
125493
|
+
return false;
|
|
125494
|
+
}
|
|
125495
|
+
return true;
|
|
125496
|
+
}
|
|
125497
|
+
function resolveOmniScopedCredentials() {
|
|
125498
|
+
if (!isOmniRoleCutoverEnabled())
|
|
125499
|
+
return null;
|
|
125500
|
+
const sentinel = readOmniCutoverSentinel();
|
|
125501
|
+
if (!sentinel)
|
|
125502
|
+
return null;
|
|
125503
|
+
const expected = deriveOmniScopedRoleName();
|
|
125504
|
+
if (sentinel.roleName !== expected) {
|
|
125505
|
+
return null;
|
|
125506
|
+
}
|
|
125507
|
+
return {
|
|
125508
|
+
username: sentinel.roleName,
|
|
125509
|
+
password: sentinel.password,
|
|
125510
|
+
roleName: sentinel.roleName
|
|
125511
|
+
};
|
|
125512
|
+
}
|
|
125513
|
+
|
|
125338
125514
|
// src/runtime-env.ts
|
|
125339
125515
|
var LEGACY_DEFAULT_DATABASE_URL = "postgresql://postgres:postgres@localhost:5432/omni";
|
|
125340
125516
|
var LEGACY_PHASE2_DATABASE_URL = "postgresql://postgres:postgres@localhost:8432/omni";
|
|
@@ -125363,21 +125539,34 @@ function resolveDatabaseUrl(serverConfig) {
|
|
|
125363
125539
|
}
|
|
125364
125540
|
return buildEmbeddedDatabaseUrl(resolvePgservePort(serverConfig));
|
|
125365
125541
|
}
|
|
125542
|
+
function applyScopedCredentials(url, creds) {
|
|
125543
|
+
if (!creds)
|
|
125544
|
+
return url;
|
|
125545
|
+
try {
|
|
125546
|
+
const parsed = new URL(url);
|
|
125547
|
+
parsed.username = encodeURIComponent(creds.username);
|
|
125548
|
+
parsed.password = encodeURIComponent(creds.password);
|
|
125549
|
+
return parsed.toString();
|
|
125550
|
+
} catch {
|
|
125551
|
+
return url;
|
|
125552
|
+
}
|
|
125553
|
+
}
|
|
125366
125554
|
function buildRuntimeEnv(serverConfig, cliConfig) {
|
|
125367
125555
|
const pgservePort = resolvePgservePort(serverConfig);
|
|
125368
125556
|
const udsActive = probeCanonicalSocketSync();
|
|
125369
125557
|
const pgHost = udsActive ? resolvePgserveSocketDir() : "";
|
|
125370
125558
|
const pgPort = udsActive ? String(CANONICAL_PG_PORT) : "";
|
|
125559
|
+
const scopedCreds = resolveOmniScopedCredentials();
|
|
125371
125560
|
return {
|
|
125372
125561
|
API_PORT: String(serverConfig.port),
|
|
125373
|
-
DATABASE_URL: resolveDatabaseUrl(serverConfig),
|
|
125562
|
+
DATABASE_URL: applyScopedCredentials(resolveDatabaseUrl(serverConfig), scopedCreds),
|
|
125374
125563
|
PGHOST: pgHost,
|
|
125375
125564
|
PGPORT: pgPort,
|
|
125376
125565
|
OMNI_API_KEY: cliConfig.apiKey ?? "",
|
|
125377
|
-
MEDIA_STORAGE_PATH:
|
|
125378
|
-
OMNI_PACKAGES_DIR:
|
|
125566
|
+
MEDIA_STORAGE_PATH: join7(serverConfig.dataDir, "media"),
|
|
125567
|
+
OMNI_PACKAGES_DIR: join7(serverConfig.dataDir, "packages"),
|
|
125379
125568
|
PGSERVE_EMBEDDED: "false",
|
|
125380
|
-
PGSERVE_DATA:
|
|
125569
|
+
PGSERVE_DATA: join7(serverConfig.dataDir, "pgserve"),
|
|
125381
125570
|
PGSERVE_PORT: String(pgservePort),
|
|
125382
125571
|
NATS_URL: "nats://localhost:4222",
|
|
125383
125572
|
NODE_ENV: serverConfig.nodeEnv,
|
|
@@ -127691,9 +127880,9 @@ function createDeadLettersCommand() {
|
|
|
127691
127880
|
}
|
|
127692
127881
|
|
|
127693
127882
|
// src/commands/doctor.ts
|
|
127694
|
-
import { existsSync as
|
|
127695
|
-
import { homedir as
|
|
127696
|
-
import { join as
|
|
127883
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
127884
|
+
import { homedir as homedir7 } from "os";
|
|
127885
|
+
import { join as join12, resolve } from "path";
|
|
127697
127886
|
init_config();
|
|
127698
127887
|
|
|
127699
127888
|
// src/health.ts
|
|
@@ -127719,14 +127908,14 @@ async function waitForHealth(port, timeoutMs = HEALTH_TIMEOUT_MS) {
|
|
|
127719
127908
|
|
|
127720
127909
|
// src/install-helpers.ts
|
|
127721
127910
|
init_config();
|
|
127722
|
-
import { existsSync as
|
|
127723
|
-
import { homedir as
|
|
127724
|
-
import { join as
|
|
127911
|
+
import { existsSync as existsSync7, readdirSync, writeFileSync as writeFileSync5 } from "fs";
|
|
127912
|
+
import { homedir as homedir5 } from "os";
|
|
127913
|
+
import { join as join9 } from "path";
|
|
127725
127914
|
|
|
127726
127915
|
// src/nats-install.ts
|
|
127727
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
127728
|
-
import { homedir as
|
|
127729
|
-
import { join as
|
|
127916
|
+
import { chmodSync as chmodSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
127917
|
+
import { homedir as homedir4 } from "os";
|
|
127918
|
+
import { join as join8 } from "path";
|
|
127730
127919
|
|
|
127731
127920
|
// ../../node_modules/.bun/ora@8.2.0/node_modules/ora/index.js
|
|
127732
127921
|
init_source();
|
|
@@ -128616,8 +128805,8 @@ function ora(options) {
|
|
|
128616
128805
|
|
|
128617
128806
|
// src/nats-install.ts
|
|
128618
128807
|
init_output();
|
|
128619
|
-
var OMNI_DIR =
|
|
128620
|
-
var NATS_BINARY_PATH =
|
|
128808
|
+
var OMNI_DIR = join8(homedir4(), ".omni");
|
|
128809
|
+
var NATS_BINARY_PATH = join8(OMNI_DIR, "nats-server");
|
|
128621
128810
|
var NATS_VERSION = "v2.12.4";
|
|
128622
128811
|
function platformInfo() {
|
|
128623
128812
|
const platform = process.platform;
|
|
@@ -128640,17 +128829,17 @@ async function downloadNats() {
|
|
|
128640
128829
|
const url = `https://github.com/nats-io/nats-server/releases/download/${NATS_VERSION}/${fileName}`;
|
|
128641
128830
|
const spinner = ora(`Downloading NATS ${NATS_VERSION} for ${info2.os}/${info2.arch}...`).start();
|
|
128642
128831
|
try {
|
|
128643
|
-
|
|
128832
|
+
mkdirSync4(OMNI_DIR, { recursive: true, mode: 448 });
|
|
128644
128833
|
const resp = await fetch(url, { signal: AbortSignal.timeout(60000) });
|
|
128645
128834
|
if (!resp.ok) {
|
|
128646
128835
|
spinner.fail(`NATS download failed: HTTP ${resp.status}`);
|
|
128647
128836
|
return false;
|
|
128648
128837
|
}
|
|
128649
|
-
const tarPath =
|
|
128650
|
-
|
|
128838
|
+
const tarPath = join8(OMNI_DIR, fileName);
|
|
128839
|
+
writeFileSync4(tarPath, Buffer.from(await resp.arrayBuffer()));
|
|
128651
128840
|
spinner.text = "Extracting NATS binary...";
|
|
128652
|
-
const tmpDir =
|
|
128653
|
-
|
|
128841
|
+
const tmpDir = join8(OMNI_DIR, "nats-tmp");
|
|
128842
|
+
mkdirSync4(tmpDir, { recursive: true });
|
|
128654
128843
|
const tar = Bun.spawn({ cmd: ["tar", "-xzf", tarPath, "-C", tmpDir], stdout: "pipe", stderr: "pipe" });
|
|
128655
128844
|
const [tarCode, tarErr] = await Promise.all([tar.exited, new Response(tar.stderr).text()]);
|
|
128656
128845
|
if (tarCode !== 0) {
|
|
@@ -128675,7 +128864,7 @@ async function downloadNats() {
|
|
|
128675
128864
|
}
|
|
128676
128865
|
}
|
|
128677
128866
|
async function ensureNats() {
|
|
128678
|
-
if (
|
|
128867
|
+
if (existsSync6(NATS_BINARY_PATH)) {
|
|
128679
128868
|
raw(` \u2713 NATS binary found at ${NATS_BINARY_PATH}`);
|
|
128680
128869
|
return;
|
|
128681
128870
|
}
|
|
@@ -128685,7 +128874,7 @@ async function ensureNats() {
|
|
|
128685
128874
|
|
|
128686
128875
|
// src/install-helpers.ts
|
|
128687
128876
|
init_output();
|
|
128688
|
-
var DEFAULT_DATA_DIR =
|
|
128877
|
+
var DEFAULT_DATA_DIR = join9(homedir5(), ".omni", "data");
|
|
128689
128878
|
async function detectReinstall(dataDirOverride) {
|
|
128690
128879
|
const hasConfig = detectHasConfig();
|
|
128691
128880
|
const hasPm2Process = await detectHasPm2Process();
|
|
@@ -128698,7 +128887,7 @@ async function detectReinstall(dataDirOverride) {
|
|
|
128698
128887
|
};
|
|
128699
128888
|
}
|
|
128700
128889
|
function detectHasConfig() {
|
|
128701
|
-
if (!
|
|
128890
|
+
if (!existsSync7(getConfigPath()))
|
|
128702
128891
|
return false;
|
|
128703
128892
|
try {
|
|
128704
128893
|
const parsed = loadConfig();
|
|
@@ -128720,7 +128909,7 @@ async function detectHasPm2Process() {
|
|
|
128720
128909
|
}
|
|
128721
128910
|
function detectHasDataDir(dataDirOverride) {
|
|
128722
128911
|
const dataDir = dataDirOverride ?? DEFAULT_DATA_DIR;
|
|
128723
|
-
if (!
|
|
128912
|
+
if (!existsSync7(dataDir))
|
|
128724
128913
|
return false;
|
|
128725
128914
|
try {
|
|
128726
128915
|
return readdirSync(dataDir).filter((e) => e !== ".DS_Store").length > 0;
|
|
@@ -128774,7 +128963,7 @@ ExecStart=/usr/bin/env omni start
|
|
|
128774
128963
|
ExecStop=/usr/bin/env omni stop
|
|
128775
128964
|
ExecReload=/usr/bin/env omni restart
|
|
128776
128965
|
Restart=on-failure
|
|
128777
|
-
PIDFile=${
|
|
128966
|
+
PIDFile=${homedir5()}/.pm2/pm2.pid
|
|
128778
128967
|
|
|
128779
128968
|
[Install]
|
|
128780
128969
|
WantedBy=multi-user.target
|
|
@@ -128785,7 +128974,7 @@ After=network.target
|
|
|
128785
128974
|
|
|
128786
128975
|
[Service]
|
|
128787
128976
|
Type=simple
|
|
128788
|
-
ExecStart="${NATS_BINARY_PATH}" -js -sd "${
|
|
128977
|
+
ExecStart="${NATS_BINARY_PATH}" -js -sd "${join9(dataDir, "nats")}"
|
|
128789
128978
|
Restart=on-failure
|
|
128790
128979
|
RestartSec=5
|
|
128791
128980
|
|
|
@@ -128793,8 +128982,8 @@ RestartSec=5
|
|
|
128793
128982
|
WantedBy=multi-user.target
|
|
128794
128983
|
`;
|
|
128795
128984
|
try {
|
|
128796
|
-
|
|
128797
|
-
|
|
128985
|
+
writeFileSync5("/etc/systemd/system/omni-nats.service", natsUnit, { mode: 420 });
|
|
128986
|
+
writeFileSync5("/etc/systemd/system/omni-api.service", apiUnit, { mode: 420 });
|
|
128798
128987
|
success("Systemd units written to /etc/systemd/system/");
|
|
128799
128988
|
raw(`
|
|
128800
128989
|
Enable with: sudo systemctl enable --now omni-nats omni-api
|
|
@@ -128808,9 +128997,9 @@ WantedBy=multi-user.target
|
|
|
128808
128997
|
init_config();
|
|
128809
128998
|
init_output();
|
|
128810
128999
|
import { spawnSync } from "child_process";
|
|
128811
|
-
import { existsSync as
|
|
128812
|
-
import { homedir as
|
|
128813
|
-
import { join as
|
|
129000
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync, statSync, writeFileSync as writeFileSync6 } from "fs";
|
|
129001
|
+
import { homedir as homedir6 } from "os";
|
|
129002
|
+
import { join as join10 } from "path";
|
|
128814
129003
|
import { gunzipSync, gzipSync } from "zlib";
|
|
128815
129004
|
async function isPgserveInstalled() {
|
|
128816
129005
|
const bin = resolvePgserveBinary();
|
|
@@ -128928,6 +129117,16 @@ async function setupCanonicalPgserve() {
|
|
|
128928
129117
|
if (port === null)
|
|
128929
129118
|
return null;
|
|
128930
129119
|
await ensureOmniDatabaseExists(port);
|
|
129120
|
+
const cutover = await ensureOmniScopedRole({
|
|
129121
|
+
socketDir: resolvePgserveSocketDir(),
|
|
129122
|
+
port,
|
|
129123
|
+
database: OMNI_DATABASE_NAME
|
|
129124
|
+
});
|
|
129125
|
+
if (cutover.status === "provisioned" || cutover.status === "refreshed") {
|
|
129126
|
+
raw(` Scoped role \`${cutover.roleName}\` ${cutover.status} \u2014 omni-api will connect as non-superuser.`);
|
|
129127
|
+
} else if (cutover.status === "skipped" && cutover.reason !== "disabled") {
|
|
129128
|
+
warn(`Scoped-role cutover skipped: ${cutover.reason} \u2014 omni-api falls back to postgres superuser.`);
|
|
129129
|
+
}
|
|
128931
129130
|
return buildOmniDatabaseUrl(port);
|
|
128932
129131
|
}
|
|
128933
129132
|
async function resolveCanonicalPgservePreference(isReinstall, cfg) {
|
|
@@ -128959,18 +129158,18 @@ async function resolveCanonicalPgservePreference(isReinstall, cfg) {
|
|
|
128959
129158
|
raw("");
|
|
128960
129159
|
return true;
|
|
128961
129160
|
}
|
|
128962
|
-
var OMNI_EMBEDDED_PGSERVE_DATA_DIR =
|
|
128963
|
-
var PGSERVE_DEFAULT_DATA_DIR =
|
|
128964
|
-
var PGSERVE_CONFIG_PATH =
|
|
128965
|
-
var OMNI_BACKUPS_DIR =
|
|
129161
|
+
var OMNI_EMBEDDED_PGSERVE_DATA_DIR = join10(homedir6(), ".omni", "data", "pgserve");
|
|
129162
|
+
var PGSERVE_DEFAULT_DATA_DIR = join10(homedir6(), ".pgserve", "data");
|
|
129163
|
+
var PGSERVE_CONFIG_PATH = join10(homedir6(), ".pgserve", "config.json");
|
|
129164
|
+
var OMNI_BACKUPS_DIR = join10(homedir6(), ".omni", "backups");
|
|
128966
129165
|
function getEmbeddedPgserveDataDir() {
|
|
128967
129166
|
return OMNI_EMBEDDED_PGSERVE_DATA_DIR;
|
|
128968
129167
|
}
|
|
128969
129168
|
function getCanonicalPgserveDataDir() {
|
|
128970
|
-
if (!
|
|
129169
|
+
if (!existsSync8(PGSERVE_CONFIG_PATH))
|
|
128971
129170
|
return PGSERVE_DEFAULT_DATA_DIR;
|
|
128972
129171
|
try {
|
|
128973
|
-
const raw2 =
|
|
129172
|
+
const raw2 = readFileSync5(PGSERVE_CONFIG_PATH, "utf8");
|
|
128974
129173
|
const parsed = JSON.parse(raw2);
|
|
128975
129174
|
return typeof parsed.dataDir === "string" && parsed.dataDir.length > 0 ? parsed.dataDir : PGSERVE_DEFAULT_DATA_DIR;
|
|
128976
129175
|
} catch {
|
|
@@ -128978,11 +129177,11 @@ function getCanonicalPgserveDataDir() {
|
|
|
128978
129177
|
}
|
|
128979
129178
|
}
|
|
128980
129179
|
function looksLikePgDataDir(path) {
|
|
128981
|
-
return
|
|
129180
|
+
return existsSync8(join10(path, "PG_VERSION")) && existsSync8(join10(path, "base"));
|
|
128982
129181
|
}
|
|
128983
129182
|
function getSnapshotPath(timestamp = new Date) {
|
|
128984
129183
|
const ts = timestamp.toISOString().replace(/[:.]/g, "-");
|
|
128985
|
-
return
|
|
129184
|
+
return join10(OMNI_BACKUPS_DIR, `embedded-migration-${ts}.sql.gz`);
|
|
128986
129185
|
}
|
|
128987
129186
|
function commandIsAvailable(cmd) {
|
|
128988
129187
|
try {
|
|
@@ -129007,7 +129206,7 @@ function pgEnvFromUrl(url) {
|
|
|
129007
129206
|
}
|
|
129008
129207
|
async function dumpEmbeddedDb(currentDatabaseUrl) {
|
|
129009
129208
|
const embeddedDir = getEmbeddedPgserveDataDir();
|
|
129010
|
-
if (!
|
|
129209
|
+
if (!existsSync8(embeddedDir)) {
|
|
129011
129210
|
return { status: "no-embedded-data", embeddedDir };
|
|
129012
129211
|
}
|
|
129013
129212
|
if (!looksLikePgDataDir(embeddedDir)) {
|
|
@@ -129018,7 +129217,7 @@ async function dumpEmbeddedDb(currentDatabaseUrl) {
|
|
|
129018
129217
|
throw new Error("pg_dump not found in PATH \u2014 install postgresql-client (apt install postgresql-client / brew install postgresql) and retry");
|
|
129019
129218
|
}
|
|
129020
129219
|
const snapshotPath = getSnapshotPath();
|
|
129021
|
-
|
|
129220
|
+
mkdirSync5(OMNI_BACKUPS_DIR, { recursive: true, mode: 448 });
|
|
129022
129221
|
raw(" Dumping embedded database via pg_dump...");
|
|
129023
129222
|
raw(` source data dir: ${embeddedDir}`);
|
|
129024
129223
|
raw(` snapshot: ${snapshotPath}`);
|
|
@@ -129034,7 +129233,7 @@ async function dumpEmbeddedDb(currentDatabaseUrl) {
|
|
|
129034
129233
|
}
|
|
129035
129234
|
const compressed = gzipSync(result.stdout);
|
|
129036
129235
|
const tmpPath = `${snapshotPath}.tmp`;
|
|
129037
|
-
|
|
129236
|
+
writeFileSync6(tmpPath, compressed, { mode: 384 });
|
|
129038
129237
|
renameSync(tmpPath, snapshotPath);
|
|
129039
129238
|
const bytes = statSync(snapshotPath).size;
|
|
129040
129239
|
raw(` snapshot size: ${formatBytes(bytes)}`);
|
|
@@ -129052,7 +129251,7 @@ async function restoreSnapshotToCanonical(dump, canonicalDatabaseUrl) {
|
|
|
129052
129251
|
raw(` snapshot: ${dump.snapshotPath}`);
|
|
129053
129252
|
raw(` canonical data dir: ${getCanonicalPgserveDataDir()}`);
|
|
129054
129253
|
raw(` canonical URL: ${canonicalDatabaseUrl}`);
|
|
129055
|
-
const compressed =
|
|
129254
|
+
const compressed = readFileSync5(dump.snapshotPath);
|
|
129056
129255
|
const sql = gunzipSync(compressed);
|
|
129057
129256
|
const result = spawnSync("psql", ["-v", "ON_ERROR_STOP=1"], {
|
|
129058
129257
|
env: { ...process.env, ...pgEnvFromUrl(canonicalDatabaseUrl) },
|
|
@@ -129083,24 +129282,24 @@ init_output();
|
|
|
129083
129282
|
|
|
129084
129283
|
// src/server-bundle.ts
|
|
129085
129284
|
init_output();
|
|
129086
|
-
import { dirname as dirname2, join as
|
|
129285
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
129087
129286
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
129088
129287
|
function getServerBundlePath() {
|
|
129089
129288
|
try {
|
|
129090
129289
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
129091
129290
|
const distDir = dirname2(thisFile);
|
|
129092
|
-
return
|
|
129291
|
+
return join11(distDir, "server", "index.js");
|
|
129093
129292
|
} catch {
|
|
129094
|
-
return
|
|
129293
|
+
return join11(process.cwd(), "dist", "server", "index.js");
|
|
129095
129294
|
}
|
|
129096
129295
|
}
|
|
129097
129296
|
function getServerLauncherPath() {
|
|
129098
129297
|
try {
|
|
129099
129298
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
129100
129299
|
const distDir = dirname2(thisFile);
|
|
129101
|
-
return
|
|
129300
|
+
return join11(distDir, "..", "bin", "omni-server");
|
|
129102
129301
|
} catch {
|
|
129103
|
-
return
|
|
129302
|
+
return join11(process.cwd(), "bin", "omni-server");
|
|
129104
129303
|
}
|
|
129105
129304
|
}
|
|
129106
129305
|
function bundleNotFoundError(bundlePath) {
|
|
@@ -129165,10 +129364,10 @@ function productionDeps() {
|
|
|
129165
129364
|
}
|
|
129166
129365
|
},
|
|
129167
129366
|
findOrphanedDataDirs: () => {
|
|
129168
|
-
const roots = [process.cwd(),
|
|
129367
|
+
const roots = [process.cwd(), join12(homedir7(), "workspace"), join12(homedir7(), "repos")];
|
|
129169
129368
|
const found = [];
|
|
129170
129369
|
for (const root of roots) {
|
|
129171
|
-
if (!
|
|
129370
|
+
if (!existsSync9(root))
|
|
129172
129371
|
continue;
|
|
129173
129372
|
try {
|
|
129174
129373
|
scanForOrphans(root, found, 0);
|
|
@@ -129276,7 +129475,7 @@ function scanForOrphans(dir, acc, depth, maxDepth = 4) {
|
|
|
129276
129475
|
for (const name of entries) {
|
|
129277
129476
|
if (name === "node_modules" || name === ".git")
|
|
129278
129477
|
continue;
|
|
129279
|
-
const full =
|
|
129478
|
+
const full = join12(dir, name);
|
|
129280
129479
|
let stats;
|
|
129281
129480
|
try {
|
|
129282
129481
|
stats = statSync2(full);
|
|
@@ -129899,7 +130098,7 @@ Safety:
|
|
|
129899
130098
|
}
|
|
129900
130099
|
|
|
129901
130100
|
// src/commands/done.ts
|
|
129902
|
-
import { existsSync as
|
|
130101
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
129903
130102
|
import { basename, extname } from "path";
|
|
129904
130103
|
|
|
129905
130104
|
// src/context.ts
|
|
@@ -130008,12 +130207,12 @@ async function handleReact(client, ctx, emoji) {
|
|
|
130008
130207
|
await closeTurn(client, "react", `Reacted ${emoji} + turn closed`);
|
|
130009
130208
|
}
|
|
130010
130209
|
async function handleMedia(client, ctx, mediaPath, caption) {
|
|
130011
|
-
if (!
|
|
130210
|
+
if (!existsSync10(mediaPath)) {
|
|
130012
130211
|
return error(`File not found: ${mediaPath}`);
|
|
130013
130212
|
}
|
|
130014
130213
|
try {
|
|
130015
130214
|
const mediaType = getMediaType(mediaPath);
|
|
130016
|
-
const buffer =
|
|
130215
|
+
const buffer = readFileSync6(mediaPath);
|
|
130017
130216
|
const base64 = buffer.toString("base64");
|
|
130018
130217
|
const filename = basename(mediaPath);
|
|
130019
130218
|
await client.messages.sendMedia({
|
|
@@ -130445,7 +130644,7 @@ function createEventsCommand() {
|
|
|
130445
130644
|
}
|
|
130446
130645
|
|
|
130447
130646
|
// src/commands/film.ts
|
|
130448
|
-
import { writeFileSync as
|
|
130647
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
130449
130648
|
import { resolve as resolvePath } from "path";
|
|
130450
130649
|
init_output();
|
|
130451
130650
|
function createFilmCommand() {
|
|
@@ -130509,7 +130708,7 @@ function createFilmCommand() {
|
|
|
130509
130708
|
const videoBuffer = Buffer.from(result.videoBase64, "base64");
|
|
130510
130709
|
if (options.output) {
|
|
130511
130710
|
const outPath = resolvePath(options.output);
|
|
130512
|
-
|
|
130711
|
+
writeFileSync7(outPath, videoBuffer);
|
|
130513
130712
|
success("Video saved", { path: outPath, sizeBytes: videoBuffer.length });
|
|
130514
130713
|
return;
|
|
130515
130714
|
}
|
|
@@ -130533,7 +130732,7 @@ function createFilmCommand() {
|
|
|
130533
130732
|
}
|
|
130534
130733
|
|
|
130535
130734
|
// src/commands/follow-up.ts
|
|
130536
|
-
import { readFileSync as
|
|
130735
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
130537
130736
|
init_output();
|
|
130538
130737
|
var VALID_SCOPES = ["agents", "instances", "chats"];
|
|
130539
130738
|
function assertScope(scope) {
|
|
@@ -130551,9 +130750,9 @@ async function resolveScopedId(scope, id) {
|
|
|
130551
130750
|
function readJsonArg(raw2) {
|
|
130552
130751
|
let body = raw2;
|
|
130553
130752
|
if (body === "-") {
|
|
130554
|
-
body =
|
|
130753
|
+
body = readFileSync7(0, "utf8");
|
|
130555
130754
|
} else if (body.startsWith("@")) {
|
|
130556
|
-
body =
|
|
130755
|
+
body = readFileSync7(body.slice(1), "utf8");
|
|
130557
130756
|
}
|
|
130558
130757
|
try {
|
|
130559
130758
|
return JSON.parse(body);
|
|
@@ -130705,8 +130904,8 @@ function createHistoryCommand() {
|
|
|
130705
130904
|
}
|
|
130706
130905
|
|
|
130707
130906
|
// src/commands/imagine.ts
|
|
130708
|
-
import { writeFileSync as
|
|
130709
|
-
import { basename as basename2, dirname as dirname3, extname as extname2, join as
|
|
130907
|
+
import { writeFileSync as writeFileSync8 } from "fs";
|
|
130908
|
+
import { basename as basename2, dirname as dirname3, extname as extname2, join as join13 } from "path";
|
|
130710
130909
|
init_output();
|
|
130711
130910
|
var ALLOWED_ASPECT_RATIOS = ["1:1", "4:3", "3:4", "16:9", "9:16", "3:2", "2:3"];
|
|
130712
130911
|
function extensionForMime(mimeType) {
|
|
@@ -130721,9 +130920,9 @@ function buildOutputPath(outputBase, index, total, mimeType) {
|
|
|
130721
130920
|
const ext = extname2(outputBase) || extensionForMime(mimeType);
|
|
130722
130921
|
const stem = basename2(outputBase, extname2(outputBase));
|
|
130723
130922
|
if (total <= 1) {
|
|
130724
|
-
return
|
|
130923
|
+
return join13(dir, `${stem}${ext}`);
|
|
130725
130924
|
}
|
|
130726
|
-
return
|
|
130925
|
+
return join13(dir, `${stem}-${index + 1}${ext}`);
|
|
130727
130926
|
}
|
|
130728
130927
|
function parseAspectRatio(value) {
|
|
130729
130928
|
if (!value)
|
|
@@ -130796,7 +130995,7 @@ function createImagineCommand() {
|
|
|
130796
130995
|
continue;
|
|
130797
130996
|
const path = buildOutputPath(options.output, i, result.images.length, image.mimeType);
|
|
130798
130997
|
try {
|
|
130799
|
-
|
|
130998
|
+
writeFileSync8(path, Buffer.from(image.base64, "base64"));
|
|
130800
130999
|
savedPaths.push(path);
|
|
130801
131000
|
} catch (err) {
|
|
130802
131001
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -130841,12 +131040,12 @@ function createImagineCommand() {
|
|
|
130841
131040
|
}
|
|
130842
131041
|
|
|
130843
131042
|
// src/commands/install.ts
|
|
130844
|
-
import { existsSync as
|
|
130845
|
-
import { homedir as
|
|
130846
|
-
import { join as
|
|
131043
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
|
|
131044
|
+
import { homedir as homedir8 } from "os";
|
|
131045
|
+
import { join as join14 } from "path";
|
|
130847
131046
|
init_config();
|
|
130848
131047
|
init_output();
|
|
130849
|
-
var DEFAULT_DATA_DIR2 =
|
|
131048
|
+
var DEFAULT_DATA_DIR2 = join14(homedir8(), ".omni", "data");
|
|
130850
131049
|
function computeDefaultDatabaseUrl() {
|
|
130851
131050
|
return buildEmbeddedDatabaseUrl();
|
|
130852
131051
|
}
|
|
@@ -130945,13 +131144,13 @@ async function startServices(cfg, forceCleanup, forceSystemd, useCanonicalPgserv
|
|
|
130945
131144
|
return false;
|
|
130946
131145
|
}
|
|
130947
131146
|
const bundlePath = getServerBundlePath();
|
|
130948
|
-
if (!
|
|
131147
|
+
if (!existsSync11(bundlePath)) {
|
|
130949
131148
|
warn(`Server bundle not found at: ${bundlePath}
|
|
130950
131149
|
Install @automagik/omni from npm: bun add -g @automagik/omni
|
|
130951
131150
|
Or build locally: make cli-build-full`);
|
|
130952
131151
|
return false;
|
|
130953
131152
|
}
|
|
130954
|
-
|
|
131153
|
+
mkdirSync6(getPm2LogDir(), { recursive: true });
|
|
130955
131154
|
await installPm2Logrotate();
|
|
130956
131155
|
const runtimeEnv = buildInstallRuntimeEnv(cfg, forceCleanup, useCanonicalPgserve);
|
|
130957
131156
|
await runPm2(["delete", PM2_PROCESSES.api]);
|
|
@@ -130969,10 +131168,10 @@ async function startServices(cfg, forceCleanup, forceSystemd, useCanonicalPgserv
|
|
|
130969
131168
|
return false;
|
|
130970
131169
|
}
|
|
130971
131170
|
apiSpinner.succeed(`${PM2_PROCESSES.api} started`);
|
|
130972
|
-
if (
|
|
131171
|
+
if (existsSync11(NATS_BINARY_PATH)) {
|
|
130973
131172
|
const natsSpinner = ora(`Starting ${PM2_PROCESSES.nats}...`).start();
|
|
130974
|
-
const natsDataDir =
|
|
130975
|
-
|
|
131173
|
+
const natsDataDir = join14(cfg.dataDir, "nats");
|
|
131174
|
+
mkdirSync6(natsDataDir, { recursive: true });
|
|
130976
131175
|
const natsArgs = buildPm2StartArgs({
|
|
130977
131176
|
kind: "nats",
|
|
130978
131177
|
script: NATS_BINARY_PATH,
|
|
@@ -132648,7 +132847,7 @@ function createLogsCommand() {
|
|
|
132648
132847
|
}
|
|
132649
132848
|
|
|
132650
132849
|
// src/commands/media.ts
|
|
132651
|
-
import { createWriteStream as createWriteStream2, existsSync as
|
|
132850
|
+
import { createWriteStream as createWriteStream2, existsSync as existsSync13, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
|
|
132652
132851
|
import { basename as basename4, dirname as dirname4, resolve as resolve2 } from "path";
|
|
132653
132852
|
import { Readable } from "stream";
|
|
132654
132853
|
import { pipeline } from "stream/promises";
|
|
@@ -132711,14 +132910,14 @@ function resolveOutputPath(outputPath, result) {
|
|
|
132711
132910
|
const resolved = resolve2(outputPath);
|
|
132712
132911
|
if (isDirHint)
|
|
132713
132912
|
return resolve2(resolved, filename);
|
|
132714
|
-
if (
|
|
132913
|
+
if (existsSync13(resolved) && statSync4(resolved).isDirectory())
|
|
132715
132914
|
return resolve2(resolved, filename);
|
|
132716
132915
|
return resolved;
|
|
132717
132916
|
}
|
|
132718
132917
|
async function downloadToFile(url, apiKey, destinationPath) {
|
|
132719
132918
|
const destDir = dirname4(destinationPath);
|
|
132720
|
-
if (!
|
|
132721
|
-
|
|
132919
|
+
if (!existsSync13(destDir))
|
|
132920
|
+
mkdirSync8(destDir, { recursive: true });
|
|
132722
132921
|
const resp = await fetch(url, {
|
|
132723
132922
|
method: "GET",
|
|
132724
132923
|
headers: apiKey ? { "x-api-key": apiKey } : undefined
|
|
@@ -133671,8 +133870,8 @@ init_output();
|
|
|
133671
133870
|
init_src();
|
|
133672
133871
|
import { execFileSync as execFileSync3, execSync } from "child_process";
|
|
133673
133872
|
import * as nodeCrypto2 from "crypto";
|
|
133674
|
-
import { existsSync as
|
|
133675
|
-
import { homedir as
|
|
133873
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
133874
|
+
import { homedir as homedir10 } from "os";
|
|
133676
133875
|
import { dirname as dirname5, resolve as resolve3 } from "path";
|
|
133677
133876
|
import { createInterface as createInterface2 } from "readline";
|
|
133678
133877
|
init_config();
|
|
@@ -133899,19 +134098,19 @@ async function pairDevice(gatewayUrl, gatewayToken, keypair, spinner) {
|
|
|
133899
134098
|
ws.close(1000, "pairing complete");
|
|
133900
134099
|
}
|
|
133901
134100
|
}
|
|
133902
|
-
var OPENCLAW_CONFIG_PATH = resolve3(
|
|
134101
|
+
var OPENCLAW_CONFIG_PATH = resolve3(homedir10(), ".openclaw", "openclaw.json");
|
|
133903
134102
|
var PLUGIN_MARKER = "plugin-openclaw/omni.ts";
|
|
133904
134103
|
function readOpenClawConfig(configPath) {
|
|
133905
|
-
if (!
|
|
134104
|
+
if (!existsSync14(configPath))
|
|
133906
134105
|
return {};
|
|
133907
|
-
const raw2 =
|
|
134106
|
+
const raw2 = readFileSync9(configPath, "utf-8").trim();
|
|
133908
134107
|
if (!raw2)
|
|
133909
134108
|
return {};
|
|
133910
134109
|
return JSON.parse(raw2);
|
|
133911
134110
|
}
|
|
133912
134111
|
function writeOpenClawConfig(configPath, config2) {
|
|
133913
|
-
|
|
133914
|
-
|
|
134112
|
+
mkdirSync9(dirname5(configPath), { recursive: true, mode: 448 });
|
|
134113
|
+
writeFileSync9(configPath, `${JSON.stringify(config2, null, 2)}
|
|
133915
134114
|
`, { mode: 384 });
|
|
133916
134115
|
}
|
|
133917
134116
|
function isPluginRegistered(config2, marker, pluginPath) {
|
|
@@ -133943,10 +134142,10 @@ function isValidUuid2(value) {
|
|
|
133943
134142
|
}
|
|
133944
134143
|
function resolvePluginPath(explicit) {
|
|
133945
134144
|
if (explicit) {
|
|
133946
|
-
return
|
|
134145
|
+
return existsSync14(explicit) ? resolve3(explicit) : null;
|
|
133947
134146
|
}
|
|
133948
134147
|
const cwdCandidate = resolve3(process.cwd(), "packages/plugin-openclaw/omni.ts");
|
|
133949
|
-
return
|
|
134148
|
+
return existsSync14(cwdCandidate) ? cwdCandidate : null;
|
|
133950
134149
|
}
|
|
133951
134150
|
function registerPlugin(config2, pluginPath, configPath) {
|
|
133952
134151
|
if (hasOpenClawCli()) {
|
|
@@ -134996,7 +135195,7 @@ function createSayCommand() {
|
|
|
134996
135195
|
}
|
|
134997
135196
|
|
|
134998
135197
|
// src/commands/see.ts
|
|
134999
|
-
import { existsSync as
|
|
135198
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
135000
135199
|
import { extname as extname4 } from "path";
|
|
135001
135200
|
init_output();
|
|
135002
135201
|
var MIME_BY_EXT = {
|
|
@@ -135028,7 +135227,7 @@ function parseMaxTokens(value) {
|
|
|
135028
135227
|
return n2;
|
|
135029
135228
|
}
|
|
135030
135229
|
function loadMedia(file) {
|
|
135031
|
-
if (!
|
|
135230
|
+
if (!existsSync15(file)) {
|
|
135032
135231
|
error(`File not found: ${file}`);
|
|
135033
135232
|
}
|
|
135034
135233
|
const stat = statSync5(file);
|
|
@@ -135039,7 +135238,7 @@ function loadMedia(file) {
|
|
|
135039
135238
|
error(`File is empty: ${file}`);
|
|
135040
135239
|
}
|
|
135041
135240
|
try {
|
|
135042
|
-
return { buffer:
|
|
135241
|
+
return { buffer: readFileSync10(file), mimeType: guessMimeType(file) };
|
|
135043
135242
|
} catch (err2) {
|
|
135044
135243
|
const message2 = err2 instanceof Error ? err2.message : "Unknown error";
|
|
135045
135244
|
return error(`Failed to read ${file}: ${message2}`);
|
|
@@ -135114,7 +135313,7 @@ function createSeeCommand() {
|
|
|
135114
135313
|
}
|
|
135115
135314
|
|
|
135116
135315
|
// src/commands/send.ts
|
|
135117
|
-
import { existsSync as
|
|
135316
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11 } from "fs";
|
|
135118
135317
|
import { basename as basename5, extname as extname5 } from "path";
|
|
135119
135318
|
init_source();
|
|
135120
135319
|
init_config();
|
|
@@ -135134,7 +135333,7 @@ function getMediaType2(path) {
|
|
|
135134
135333
|
return "document";
|
|
135135
135334
|
}
|
|
135136
135335
|
function readFileAsBase64(path) {
|
|
135137
|
-
const buffer3 =
|
|
135336
|
+
const buffer3 = readFileSync11(path);
|
|
135138
135337
|
return buffer3.toString("base64");
|
|
135139
135338
|
}
|
|
135140
135339
|
var messageSenders = {
|
|
@@ -135155,7 +135354,7 @@ var messageSenders = {
|
|
|
135155
135354
|
const { to, media } = options3;
|
|
135156
135355
|
if (!to || !media)
|
|
135157
135356
|
return;
|
|
135158
|
-
if (!
|
|
135357
|
+
if (!existsSync16(media)) {
|
|
135159
135358
|
error(`File not found: ${media}`);
|
|
135160
135359
|
return;
|
|
135161
135360
|
}
|
|
@@ -135687,9 +135886,9 @@ function pickFilename(mimeType, provider) {
|
|
|
135687
135886
|
}
|
|
135688
135887
|
|
|
135689
135888
|
// src/commands/start.ts
|
|
135690
|
-
import { existsSync as
|
|
135691
|
-
import { homedir as
|
|
135692
|
-
import { join as
|
|
135889
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync10 } from "fs";
|
|
135890
|
+
import { homedir as homedir11 } from "os";
|
|
135891
|
+
import { join as join16 } from "path";
|
|
135693
135892
|
init_config();
|
|
135694
135893
|
init_output();
|
|
135695
135894
|
var START_HEALTH_TIMEOUT_MS = 1e4;
|
|
@@ -135698,12 +135897,12 @@ async function runStart() {
|
|
|
135698
135897
|
pm2NotFoundError();
|
|
135699
135898
|
}
|
|
135700
135899
|
const bundlePath = getServerBundlePath();
|
|
135701
|
-
if (!
|
|
135900
|
+
if (!existsSync17(bundlePath)) {
|
|
135702
135901
|
bundleNotFoundError(bundlePath);
|
|
135703
135902
|
}
|
|
135704
135903
|
const serverConfig = loadServerConfig();
|
|
135705
135904
|
const apiPort = serverConfig.port;
|
|
135706
|
-
|
|
135905
|
+
mkdirSync10(getPm2LogDir(), { recursive: true });
|
|
135707
135906
|
info(`Starting ${PM2_PROCESSES.api} (port ${apiPort})...`);
|
|
135708
135907
|
const cliConfig = loadConfig();
|
|
135709
135908
|
const env2 = buildRuntimeEnv(serverConfig, cliConfig);
|
|
@@ -135719,11 +135918,11 @@ async function runStart() {
|
|
|
135719
135918
|
error(`Failed to start ${PM2_PROCESSES.api} (pm2 exit code ${apiCode})`, undefined, 1);
|
|
135720
135919
|
return;
|
|
135721
135920
|
}
|
|
135722
|
-
const natsPath =
|
|
135723
|
-
if (
|
|
135921
|
+
const natsPath = join16(homedir11(), ".omni", "nats-server");
|
|
135922
|
+
if (existsSync17(natsPath)) {
|
|
135724
135923
|
info(`Starting ${PM2_PROCESSES.nats}...`);
|
|
135725
|
-
const natsDataDir =
|
|
135726
|
-
|
|
135924
|
+
const natsDataDir = join16(serverConfig.dataDir, "nats");
|
|
135925
|
+
mkdirSync10(natsDataDir, { recursive: true });
|
|
135727
135926
|
const natsArgs = buildPm2StartArgs({
|
|
135728
135927
|
kind: "nats",
|
|
135729
135928
|
script: natsPath,
|
|
@@ -136143,8 +136342,8 @@ function createTurnsCommand() {
|
|
|
136143
136342
|
}
|
|
136144
136343
|
|
|
136145
136344
|
// src/commands/update.ts
|
|
136146
|
-
import { existsSync as
|
|
136147
|
-
import { join as
|
|
136345
|
+
import { existsSync as existsSync19 } from "fs";
|
|
136346
|
+
import { join as join18 } from "path";
|
|
136148
136347
|
import { createInterface as createInterface3 } from "readline";
|
|
136149
136348
|
init_source();
|
|
136150
136349
|
init_config();
|
|
@@ -136426,9 +136625,9 @@ async function cleanupLegacyArtifacts(skipList) {
|
|
|
136426
136625
|
init_output();
|
|
136427
136626
|
|
|
136428
136627
|
// src/update-diagnostics.ts
|
|
136429
|
-
import { existsSync as
|
|
136430
|
-
import { homedir as
|
|
136431
|
-
import { join as
|
|
136628
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
136629
|
+
import { homedir as homedir12 } from "os";
|
|
136630
|
+
import { join as join17 } from "path";
|
|
136432
136631
|
var UPDATE_DIAGNOSTICS_SCHEMA_VERSION = 1;
|
|
136433
136632
|
function createDiagnostics(args) {
|
|
136434
136633
|
const startedAt = new Date().toISOString();
|
|
@@ -136450,18 +136649,18 @@ function createDiagnostics(args) {
|
|
|
136450
136649
|
};
|
|
136451
136650
|
}
|
|
136452
136651
|
function getDiagnosticsDir() {
|
|
136453
|
-
const base = process.env.OMNI_CONFIG_DIR ??
|
|
136454
|
-
return
|
|
136652
|
+
const base = process.env.OMNI_CONFIG_DIR ?? join17(homedir12(), ".omni");
|
|
136653
|
+
return join17(base, "logs");
|
|
136455
136654
|
}
|
|
136456
136655
|
function getDiagnosticsPath(startedAt) {
|
|
136457
136656
|
const safe = startedAt.replace(/:/g, "-");
|
|
136458
|
-
return
|
|
136657
|
+
return join17(getDiagnosticsDir(), `update-diagnostics-${safe}.json`);
|
|
136459
136658
|
}
|
|
136460
136659
|
function tailFileLines(path, maxLines) {
|
|
136461
|
-
if (!
|
|
136660
|
+
if (!existsSync18(path))
|
|
136462
136661
|
return [];
|
|
136463
136662
|
try {
|
|
136464
|
-
const raw2 =
|
|
136663
|
+
const raw2 = readFileSync12(path, "utf8");
|
|
136465
136664
|
const lines = raw2.split(/\r?\n/);
|
|
136466
136665
|
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
136467
136666
|
lines.pop();
|
|
@@ -136494,10 +136693,10 @@ function writeDiagnostics(state, exitCode) {
|
|
|
136494
136693
|
const dir = getDiagnosticsDir();
|
|
136495
136694
|
const path = getDiagnosticsPath(state.startedAt);
|
|
136496
136695
|
try {
|
|
136497
|
-
if (!
|
|
136498
|
-
|
|
136696
|
+
if (!existsSync18(dir)) {
|
|
136697
|
+
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
136499
136698
|
}
|
|
136500
|
-
|
|
136699
|
+
writeFileSync10(path, `${JSON.stringify(state, null, 2)}
|
|
136501
136700
|
`, { mode: 384 });
|
|
136502
136701
|
return path;
|
|
136503
136702
|
} catch {
|
|
@@ -136668,7 +136867,7 @@ function printVerifySkippedBanner(latest) {
|
|
|
136668
136867
|
}
|
|
136669
136868
|
function detectParallelNpmGlobalInstall(deps) {
|
|
136670
136869
|
const npmRootFn = deps?.npmRoot ?? defaultNpmRoot;
|
|
136671
|
-
const existsFn = deps?.exists ??
|
|
136870
|
+
const existsFn = deps?.exists ?? existsSync19;
|
|
136672
136871
|
let root;
|
|
136673
136872
|
try {
|
|
136674
136873
|
root = npmRootFn();
|
|
@@ -136678,7 +136877,7 @@ function detectParallelNpmGlobalInstall(deps) {
|
|
|
136678
136877
|
if (root === null || root.length === 0) {
|
|
136679
136878
|
return { detected: false, skipped: "npm-not-on-path" };
|
|
136680
136879
|
}
|
|
136681
|
-
const candidate =
|
|
136880
|
+
const candidate = join18(root, "@automagik", "omni");
|
|
136682
136881
|
if (existsFn(candidate)) {
|
|
136683
136882
|
return { detected: true, path: candidate };
|
|
136684
136883
|
}
|
|
@@ -137075,9 +137274,9 @@ function createVoiceCommand() {
|
|
|
137075
137274
|
const startTime = Date.now();
|
|
137076
137275
|
let saveDir = "";
|
|
137077
137276
|
if (opts.save) {
|
|
137078
|
-
const { mkdirSync:
|
|
137277
|
+
const { mkdirSync: mkdirSync12 } = await import("fs");
|
|
137079
137278
|
saveDir = opts.save;
|
|
137080
|
-
|
|
137279
|
+
mkdirSync12(saveDir, { recursive: true });
|
|
137081
137280
|
info(`Saving audio to: ${saveDir}`);
|
|
137082
137281
|
}
|
|
137083
137282
|
ws.onopen = () => {
|
|
@@ -137301,17 +137500,17 @@ init_config();
|
|
|
137301
137500
|
init_output();
|
|
137302
137501
|
|
|
137303
137502
|
// src/manifest-pin.ts
|
|
137304
|
-
import { existsSync as
|
|
137305
|
-
import { homedir as
|
|
137306
|
-
import { join as
|
|
137503
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13, renameSync as renameSync3, writeFileSync as writeFileSync11 } from "fs";
|
|
137504
|
+
import { homedir as homedir13 } from "os";
|
|
137505
|
+
import { join as join19 } from "path";
|
|
137307
137506
|
var PACKAGE_NAME2 = "@automagik/omni";
|
|
137308
|
-
var BUN_GLOBAL_MANIFEST =
|
|
137507
|
+
var BUN_GLOBAL_MANIFEST = join19(homedir13(), ".bun", "install", "global", "package.json");
|
|
137309
137508
|
function pinManifestEntry(manifestPath, exactVersion) {
|
|
137310
|
-
if (!
|
|
137509
|
+
if (!existsSync20(manifestPath))
|
|
137311
137510
|
return false;
|
|
137312
137511
|
let manifest;
|
|
137313
137512
|
try {
|
|
137314
|
-
manifest = JSON.parse(
|
|
137513
|
+
manifest = JSON.parse(readFileSync13(manifestPath, "utf-8"));
|
|
137315
137514
|
} catch {
|
|
137316
137515
|
return false;
|
|
137317
137516
|
}
|
|
@@ -137337,7 +137536,7 @@ function pinManifestEntry(manifestPath, exactVersion) {
|
|
|
137337
137536
|
return false;
|
|
137338
137537
|
const tmp = `${manifestPath}.tmp.${process.pid}`;
|
|
137339
137538
|
try {
|
|
137340
|
-
|
|
137539
|
+
writeFileSync11(tmp, `${JSON.stringify(manifest, null, 2)}
|
|
137341
137540
|
`, { mode: 420 });
|
|
137342
137541
|
renameSync3(tmp, manifestPath);
|
|
137343
137542
|
} catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical-pgserve.d.ts","sourceRoot":"","sources":["../../src/lib/canonical-pgserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"canonical-pgserve.d.ts","sourceRoot":"","sources":["../../src/lib/canonical-pgserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA8OH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BpE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iCAAiC,CACrD,WAAW,EAAE,OAAO,EACpB,GAAG,EAAE;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAwDD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CASnD;AAuCD;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,uBAAuB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA6D5F;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,kBAAkB,EACxB,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CAAC;IAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCpE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedicated-role cutover — omni-side scoped non-superuser identity.
|
|
3
|
+
*
|
|
4
|
+
* Wish parity: mirrors genie's `src/lib/role-cutover.ts` (the
|
|
5
|
+
* `genie-dedicated-role-cutover` wish). Omni historically connected to
|
|
6
|
+
* canonical pgserve/autopg as the cluster superuser `postgres:postgres`.
|
|
7
|
+
* A bad omni migration or a compromised omni-api could therefore
|
|
8
|
+
* `DROP DATABASE genie`, exhaust the cluster, create roles, etc. —
|
|
9
|
+
* the same blast-radius problem genie solved with role-cutover.
|
|
10
|
+
*
|
|
11
|
+
* This module provisions a dedicated NON-superuser role scoped to omni's
|
|
12
|
+
* own `omni` database, persists the credential in a sentinel file, and
|
|
13
|
+
* is consumed by `buildRuntimeEnv` to rewrite `DATABASE_URL` to the
|
|
14
|
+
* scoped role at omni-api startup.
|
|
15
|
+
*
|
|
16
|
+
* Scope (MVP):
|
|
17
|
+
* - Provision `pgserve_omni_<fp12>_role` with grants scoped to `omni` DB
|
|
18
|
+
* - Generate a random password at provisioning, persist in sentinel
|
|
19
|
+
* - Idempotent: existing role → refresh grants, regenerate sentinel from
|
|
20
|
+
* password (preserves the operator-visible role identifier)
|
|
21
|
+
* - Kill switch: `OMNI_ROLE_CUTOVER=0` forces legacy `postgres`/`postgres`
|
|
22
|
+
* - Best-effort: any failure logs a warning and falls back to legacy creds
|
|
23
|
+
*
|
|
24
|
+
* Out of scope (deferred to a follow-up wish, matching genie's groups):
|
|
25
|
+
* - Advisory-lock concurrency guards (multi-host simultaneous installs)
|
|
26
|
+
* - Full event sink with audit log + sentinel rotation
|
|
27
|
+
* - Per-tenant fingerprint stability handling beyond package.json
|
|
28
|
+
* - doctor.ts validation that the scoped role is in active use
|
|
29
|
+
* - Migration of pre-cutover objects' ownership to the scoped role
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Compute the scoped role name for an omni install. Deterministic — same
|
|
33
|
+
* install always produces the same role name across reruns.
|
|
34
|
+
*/
|
|
35
|
+
export declare function deriveOmniScopedRoleName(): string;
|
|
36
|
+
export interface OmniRoleCutoverSentinel {
|
|
37
|
+
/** The provisioned role name (matches deriveOmniScopedRoleName output). */
|
|
38
|
+
roleName: string;
|
|
39
|
+
/** Database the role is scoped to (always `omni`). */
|
|
40
|
+
database: string;
|
|
41
|
+
/** Generated password (alphanumeric). */
|
|
42
|
+
password: string;
|
|
43
|
+
/** ISO timestamp of provisioning. */
|
|
44
|
+
provisionedAt: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function readOmniCutoverSentinel(): OmniRoleCutoverSentinel | null;
|
|
47
|
+
export declare function clearOmniCutoverSentinel(): void;
|
|
48
|
+
export declare function isOmniRoleCutoverEnabled(): boolean;
|
|
49
|
+
export interface EnsureOmniScopedRoleOptions {
|
|
50
|
+
/** Path to autopg's canonical Unix socket dir (e.g. /run/user/1000/pgserve). */
|
|
51
|
+
socketDir: string;
|
|
52
|
+
/** Canonical port (default 5432). */
|
|
53
|
+
port?: number;
|
|
54
|
+
/** Database to scope grants to (default 'omni'). */
|
|
55
|
+
database?: string;
|
|
56
|
+
/** Force-enable bypassing the OMNI_ROLE_CUTOVER kill switch (tests). */
|
|
57
|
+
enabled?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export type EnsureOmniScopedRoleResult = {
|
|
60
|
+
status: 'provisioned' | 'refreshed';
|
|
61
|
+
roleName: string;
|
|
62
|
+
} | {
|
|
63
|
+
status: 'skipped';
|
|
64
|
+
reason: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Idempotently provision omni's scoped role + grants on canonical pgserve.
|
|
68
|
+
*
|
|
69
|
+
* Side effects:
|
|
70
|
+
* - CREATE ROLE if not exists (or ALTER PASSWORD if exists to keep sentinel in sync)
|
|
71
|
+
* - GRANTs scoped to the omni database
|
|
72
|
+
* - Sentinel written at ~/.omni/scoped-role.json
|
|
73
|
+
*
|
|
74
|
+
* Failure mode: best-effort. Any psql / spawn failure returns
|
|
75
|
+
* `{ status: 'skipped', reason }` and emits a stderr warn. omni-api
|
|
76
|
+
* continues with the legacy `postgres:postgres` path.
|
|
77
|
+
*/
|
|
78
|
+
export declare function ensureOmniScopedRole(opts: EnsureOmniScopedRoleOptions): Promise<EnsureOmniScopedRoleResult>;
|
|
79
|
+
export interface ScopedCredentialOverride {
|
|
80
|
+
username: string;
|
|
81
|
+
password: string;
|
|
82
|
+
roleName: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Returns the scoped-role credentials to override `postgres:postgres` with
|
|
86
|
+
* at runtime, or null when cutover is disabled / sentinel missing / kill
|
|
87
|
+
* switch active. Pure: no DB roundtrip.
|
|
88
|
+
*
|
|
89
|
+
* Consumed by buildRuntimeEnv when constructing DATABASE_URL.
|
|
90
|
+
*/
|
|
91
|
+
export declare function resolveOmniScopedCredentials(): ScopedCredentialOverride | null;
|
|
92
|
+
//# sourceMappingURL=role-cutover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-cutover.d.ts","sourceRoot":"","sources":["../../src/lib/role-cutover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA+BH;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAUjD;AAqBD,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;CACvB;AAUD,wBAAgB,uBAAuB,IAAI,uBAAuB,GAAG,IAAI,CAkBxE;AASD,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAMD,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAWD,MAAM,WAAW,2BAA2B;IAC1C,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAyEjH;AA0CD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,IAAI,wBAAwB,GAAG,IAAI,CAc9E"}
|
package/dist/runtime-env.d.ts
CHANGED
|
@@ -101,6 +101,15 @@ export declare function resolvePgservePort(serverConfig: ServerConfig): number;
|
|
|
101
101
|
* This function never reads `process.env.DATABASE_URL`.
|
|
102
102
|
*/
|
|
103
103
|
export declare function resolveDatabaseUrl(serverConfig: ServerConfig): string;
|
|
104
|
+
/**
|
|
105
|
+
* Replace the userinfo in a postgresql:// URL with the scoped-role
|
|
106
|
+
* credentials. Returns the URL unchanged when `creds` is null. Pure;
|
|
107
|
+
* URL-safe via WHATWG URL.
|
|
108
|
+
*/
|
|
109
|
+
export declare function applyScopedCredentials(url: string, creds: {
|
|
110
|
+
username: string;
|
|
111
|
+
password: string;
|
|
112
|
+
} | null): string;
|
|
104
113
|
/**
|
|
105
114
|
* Build the complete runtime env for the omni-api process.
|
|
106
115
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-env.d.ts","sourceRoot":"","sources":["../src/runtime-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-env.d.ts","sourceRoot":"","sources":["../src/runtime-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASxD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IAQrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAQF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,0EAA0E;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,GAAE,MAA6B,GAAG,MAAM,CAE3F;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,MAAM,CAKxD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAMrE;AAgBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAiBrE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,MAAM,CAWhH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAqCzF"}
|
package/dist/server/index.js
CHANGED
|
@@ -230137,7 +230137,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
230137
230137
|
var require_package8 = __commonJS((exports, module) => {
|
|
230138
230138
|
module.exports = {
|
|
230139
230139
|
name: "@omni/api",
|
|
230140
|
-
version: "2.260520.
|
|
230140
|
+
version: "2.260520.11",
|
|
230141
230141
|
type: "module",
|
|
230142
230142
|
exports: {
|
|
230143
230143
|
".": {
|