openmates 0.18.0-alpha.7 → 0.18.0-alpha.9
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/{chunk-2H5LTEHD.js → chunk-34UCC43K.js} +528 -391
- package/dist/{cli-BLFXUeuE.d.ts → cli-CZoCLxkk.d.ts} +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -8437,7 +8437,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8437
8437
|
if (!userId) {
|
|
8438
8438
|
throw new Error("Could not resolve current user id for local connected-account connector.");
|
|
8439
8439
|
}
|
|
8440
|
-
const { createHash:
|
|
8440
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
8441
8441
|
const accountId = randomUUID6();
|
|
8442
8442
|
const masterKey = this.getMasterKeyBytes();
|
|
8443
8443
|
const encryptLocalConnectorValue = async (value) => {
|
|
@@ -8446,9 +8446,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8446
8446
|
};
|
|
8447
8447
|
const payload = {
|
|
8448
8448
|
id: accountId,
|
|
8449
|
-
hashed_user_id:
|
|
8449
|
+
hashed_user_id: createHash16("sha256").update(userId).digest("hex"),
|
|
8450
8450
|
encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
|
|
8451
|
-
provider_type_hash:
|
|
8451
|
+
provider_type_hash: createHash16("sha256").update(input.provider_id).digest("hex"),
|
|
8452
8452
|
encrypted_account_label: await encryptLocalConnectorValue(input.label),
|
|
8453
8453
|
encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
|
|
8454
8454
|
encrypted_app_permissions: await encryptLocalConnectorValue({
|
|
@@ -9947,8 +9947,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
9947
9947
|
);
|
|
9948
9948
|
}
|
|
9949
9949
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
9950
|
-
const { createHash:
|
|
9951
|
-
const hashedEmbedId =
|
|
9950
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
9951
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
9952
9952
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
9953
9953
|
cache,
|
|
9954
9954
|
masterKey,
|
|
@@ -10056,7 +10056,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
10056
10056
|
async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
|
|
10057
10057
|
if (visited.has(embedId)) return null;
|
|
10058
10058
|
visited.add(embedId);
|
|
10059
|
-
const { createHash:
|
|
10059
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
10060
10060
|
const masterKeyEntry = cache.embedKeys.find(
|
|
10061
10061
|
(ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
|
|
10062
10062
|
);
|
|
@@ -10073,7 +10073,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
10073
10073
|
if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
|
|
10074
10074
|
const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
|
|
10075
10075
|
const owningChat = cache.chats.find((c) => {
|
|
10076
|
-
const chatHash =
|
|
10076
|
+
const chatHash = createHash16("sha256").update(String(c.details.id ?? "")).digest("hex");
|
|
10077
10077
|
return chatHash === hashedChatId;
|
|
10078
10078
|
});
|
|
10079
10079
|
if (owningChat) {
|
|
@@ -10102,7 +10102,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
10102
10102
|
const parentFullId = String(
|
|
10103
10103
|
parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
|
|
10104
10104
|
);
|
|
10105
|
-
const parentHashedId =
|
|
10105
|
+
const parentHashedId = createHash16("sha256").update(parentFullId).digest("hex");
|
|
10106
10106
|
const parentKey = await this.resolveEmbedKey(
|
|
10107
10107
|
cache,
|
|
10108
10108
|
masterKey,
|
|
@@ -12841,6 +12841,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
12841
12841
|
if (Number.isSafeInteger(context.limit) && context.limit !== void 0 && context.limit > 0) {
|
|
12842
12842
|
params.set("limit", String(context.limit));
|
|
12843
12843
|
}
|
|
12844
|
+
if (context.newestFirst) params.set("newest_first", "true");
|
|
12844
12845
|
const query = params.toString();
|
|
12845
12846
|
const response = await this.http.get(
|
|
12846
12847
|
`/v1/user-tasks/${encodeURIComponent(taskId)}/activity${query ? `?${query}` : ""}`,
|
|
@@ -14136,7 +14137,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
14136
14137
|
const session = this.requireSession();
|
|
14137
14138
|
const masterKey = base64ToBytes(session.masterKeyExportedB64);
|
|
14138
14139
|
const cache = await this.ensureSynced();
|
|
14139
|
-
const { createHash:
|
|
14140
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14140
14141
|
const embed = cache.embeds.find(
|
|
14141
14142
|
(e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
|
|
14142
14143
|
);
|
|
@@ -14144,7 +14145,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
14144
14145
|
throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
|
|
14145
14146
|
}
|
|
14146
14147
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
14147
|
-
const hashedEmbedId =
|
|
14148
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
14148
14149
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
14149
14150
|
cache,
|
|
14150
14151
|
masterKey,
|
|
@@ -14252,8 +14253,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
14252
14253
|
if (!embed) {
|
|
14253
14254
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
14254
14255
|
}
|
|
14255
|
-
const { createHash:
|
|
14256
|
-
const hashedEmbedId =
|
|
14256
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14257
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
14257
14258
|
const embedKey = await this.resolveEmbedKey(
|
|
14258
14259
|
cache,
|
|
14259
14260
|
masterKey,
|
|
@@ -14369,8 +14370,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
14369
14370
|
if (!embed) {
|
|
14370
14371
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
14371
14372
|
}
|
|
14372
|
-
const { createHash:
|
|
14373
|
-
const hashedEmbedId =
|
|
14373
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14374
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
14374
14375
|
const embedKey = await this.resolveEmbedKey(
|
|
14375
14376
|
cache,
|
|
14376
14377
|
masterKey,
|
|
@@ -14837,9 +14838,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
14837
14838
|
chats.splice(0, chats.length, ...reconciledChats);
|
|
14838
14839
|
if (totalChatCount === 0) totalChatCount = chats.length;
|
|
14839
14840
|
if (reconciliation.deleted_chat_ids?.length) {
|
|
14840
|
-
const { createHash:
|
|
14841
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14841
14842
|
const deletedHashes = new Set(
|
|
14842
|
-
reconciliation.deleted_chat_ids.map((id) =>
|
|
14843
|
+
reconciliation.deleted_chat_ids.map((id) => createHash16("sha256").update(id).digest("hex"))
|
|
14843
14844
|
);
|
|
14844
14845
|
const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
|
|
14845
14846
|
const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
|
|
@@ -20224,12 +20225,12 @@ var ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS = [
|
|
|
20224
20225
|
];
|
|
20225
20226
|
async function writeAccountExportArchive(bundle, flags = {}) {
|
|
20226
20227
|
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
20227
|
-
const { join:
|
|
20228
|
+
const { join: join13, dirname: dirname11 } = await import("path");
|
|
20228
20229
|
const exportId = safeArchiveSegment(String(bundle.export.export_id ?? `export-${Date.now()}`));
|
|
20229
20230
|
const archiveFormat = flags.format === "directory" ? "directory" : "zip";
|
|
20230
20231
|
const password = typeof flags.password === "string" && flags.password.length > 0 ? flags.password : null;
|
|
20231
20232
|
if (password && archiveFormat !== "zip") throw new Error("Password-protected account export is only supported for zip format.");
|
|
20232
|
-
const outputPath = typeof flags.output === "string" ? flags.output :
|
|
20233
|
+
const outputPath = typeof flags.output === "string" ? flags.output : join13(process.cwd(), archiveFormat === "zip" ? `openmates-account-export-${exportId}${password ? ".zip.enc" : ".zip"}` : `openmates-account-export-${exportId}`);
|
|
20233
20234
|
const zip = archiveFormat === "zip" ? new JSZip3() : null;
|
|
20234
20235
|
const writtenFiles = [];
|
|
20235
20236
|
async function writeArchiveText(relativePath, content) {
|
|
@@ -20240,7 +20241,7 @@ async function writeAccountExportArchive(bundle, flags = {}) {
|
|
|
20240
20241
|
return;
|
|
20241
20242
|
}
|
|
20242
20243
|
const stagingDir = outputPath;
|
|
20243
|
-
const fullPath =
|
|
20244
|
+
const fullPath = join13(stagingDir, relativePath);
|
|
20244
20245
|
await mkdir2(dirname11(fullPath), { recursive: true });
|
|
20245
20246
|
await writeFile2(fullPath, content, "utf-8");
|
|
20246
20247
|
writtenFiles.push(relativePath);
|
|
@@ -24391,10 +24392,115 @@ var OpenMatesBenchmark = class {
|
|
|
24391
24392
|
// src/cli.ts
|
|
24392
24393
|
import { createInterface as createInterface5 } from "readline/promises";
|
|
24393
24394
|
import { stdin as stdin3, stdout as stdout2 } from "process";
|
|
24394
|
-
|
|
24395
|
+
|
|
24396
|
+
// src/taskActivityHistory.ts
|
|
24397
|
+
async function readActivityHistory(options) {
|
|
24398
|
+
const { query } = options;
|
|
24399
|
+
const maxEntries = options.maxEntries ?? Infinity;
|
|
24400
|
+
if (options.maxEntries !== void 0 && (!Number.isSafeInteger(maxEntries) || maxEntries < 1 || maxEntries > 200)) {
|
|
24401
|
+
throw new Error("--max-entries must be an integer from 1 to 200");
|
|
24402
|
+
}
|
|
24403
|
+
if (query !== void 0 && !query.trim()) throw new Error("Activity search requires a non-empty --query");
|
|
24404
|
+
const pageSize = options.pageSize ?? 100;
|
|
24405
|
+
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 200) throw new Error("--limit must be from 1 to 200");
|
|
24406
|
+
const entries = [];
|
|
24407
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24408
|
+
let cursor = options.cursor;
|
|
24409
|
+
let matched = 0;
|
|
24410
|
+
do {
|
|
24411
|
+
const page = await options.page(cursor, query === void 0 ? Math.min(pageSize, maxEntries - entries.length) : pageSize);
|
|
24412
|
+
for (const entry of page.entries) {
|
|
24413
|
+
if (query === void 0 || (entry.message || "").toLocaleLowerCase().includes(query.toLocaleLowerCase())) {
|
|
24414
|
+
matched++;
|
|
24415
|
+
if (entries.length < maxEntries) entries.push(entry);
|
|
24416
|
+
}
|
|
24417
|
+
}
|
|
24418
|
+
cursor = page.next_cursor || void 0;
|
|
24419
|
+
if (cursor && seen.has(cursor)) throw new Error("Activity pagination repeated a cursor");
|
|
24420
|
+
if (cursor) seen.add(cursor);
|
|
24421
|
+
} while (cursor && (query !== void 0 || entries.length < maxEntries));
|
|
24422
|
+
return { entries, next_cursor: cursor || null, truncated: Boolean(cursor) || matched > entries.length, matched };
|
|
24423
|
+
}
|
|
24424
|
+
|
|
24425
|
+
// src/taskActivityDelivery.ts
|
|
24426
|
+
import { createHash as createHash12 } from "crypto";
|
|
24427
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, renameSync as renameSync2, writeFileSync as writeFileSync4, rmSync as rmSync3, existsSync as existsSync7 } from "fs";
|
|
24428
|
+
import { join as join4 } from "path";
|
|
24429
|
+
import lockfile2 from "proper-lockfile";
|
|
24430
|
+
function activityDeliveryStore(scope, stateDir2 = resolveStateDir()) {
|
|
24431
|
+
const directory = join4(stateDir2, "task-activity-delivery", createHash12("sha256").update(scope).digest("hex"));
|
|
24432
|
+
mkdirSync4(directory, { recursive: true, mode: 448 });
|
|
24433
|
+
const pendingDirectory = join4(directory, "pending");
|
|
24434
|
+
const acknowledgedDirectory = join4(directory, "acknowledged");
|
|
24435
|
+
mkdirSync4(pendingDirectory, { recursive: true, mode: 448 });
|
|
24436
|
+
mkdirSync4(acknowledgedDirectory, { recursive: true, mode: 448 });
|
|
24437
|
+
const save = (path2, record) => {
|
|
24438
|
+
const temporary = `${path2}.${process.pid}.tmp`;
|
|
24439
|
+
writeFileSync4(temporary, JSON.stringify(record), { mode: 384 });
|
|
24440
|
+
renameSync2(temporary, path2);
|
|
24441
|
+
};
|
|
24442
|
+
const send = async (path2, record, create) => {
|
|
24443
|
+
if (!record.acknowledged) {
|
|
24444
|
+
const acknowledged = await create(record.input);
|
|
24445
|
+
if (acknowledged.entry_id !== record.input.entry_id) throw new Error("Activity acknowledgement identity mismatch");
|
|
24446
|
+
record.acknowledged = acknowledged;
|
|
24447
|
+
save(join4(acknowledgedDirectory, `${record.input.entry_id}.json`), record);
|
|
24448
|
+
rmSync3(path2, { force: true });
|
|
24449
|
+
}
|
|
24450
|
+
return record.acknowledged;
|
|
24451
|
+
};
|
|
24452
|
+
return {
|
|
24453
|
+
async deliver(id, build, create) {
|
|
24454
|
+
if (!/^[a-f0-9]{64}$/.test(id)) throw new Error("--delivery-id must be a SHA-256 hex identifier");
|
|
24455
|
+
const release2 = await lockfile2.lock(directory, { retries: { retries: 3, minTimeout: 100, maxTimeout: 500 }, stale: 12e4 });
|
|
24456
|
+
try {
|
|
24457
|
+
const acknowledgedPath = join4(acknowledgedDirectory, `${id}.json`);
|
|
24458
|
+
const path2 = existsSync7(acknowledgedPath) ? acknowledgedPath : join4(pendingDirectory, `${id}.json`);
|
|
24459
|
+
let record;
|
|
24460
|
+
try {
|
|
24461
|
+
record = JSON.parse(readFileSync6(path2, "utf8"));
|
|
24462
|
+
} catch (error) {
|
|
24463
|
+
if (error.code !== "ENOENT") throw error;
|
|
24464
|
+
record = { input: await build() };
|
|
24465
|
+
save(path2, record);
|
|
24466
|
+
}
|
|
24467
|
+
return await send(path2, record, create);
|
|
24468
|
+
} finally {
|
|
24469
|
+
await release2();
|
|
24470
|
+
}
|
|
24471
|
+
},
|
|
24472
|
+
async flush(create) {
|
|
24473
|
+
const release2 = await lockfile2.lock(directory, { retries: 0, stale: 12e4 });
|
|
24474
|
+
try {
|
|
24475
|
+
const names = readdirSync2(pendingDirectory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name));
|
|
24476
|
+
let pending = names.length;
|
|
24477
|
+
let attempted = 0;
|
|
24478
|
+
for (const name of names) {
|
|
24479
|
+
const path2 = join4(pendingDirectory, name);
|
|
24480
|
+
const record = JSON.parse(readFileSync6(path2, "utf8"));
|
|
24481
|
+
if (record.acknowledged) continue;
|
|
24482
|
+
if (attempted >= 3) break;
|
|
24483
|
+
attempted++;
|
|
24484
|
+
try {
|
|
24485
|
+
await send(path2, record, create);
|
|
24486
|
+
pending--;
|
|
24487
|
+
} catch {
|
|
24488
|
+
break;
|
|
24489
|
+
}
|
|
24490
|
+
}
|
|
24491
|
+
return { pending, attempted };
|
|
24492
|
+
} finally {
|
|
24493
|
+
await release2();
|
|
24494
|
+
}
|
|
24495
|
+
}
|
|
24496
|
+
};
|
|
24497
|
+
}
|
|
24498
|
+
|
|
24499
|
+
// src/cli.ts
|
|
24500
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, realpathSync as realpathSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
24395
24501
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24396
24502
|
import { basename as basename4, dirname as dirname10 } from "path";
|
|
24397
|
-
import { createHash as
|
|
24503
|
+
import { createHash as createHash15, randomBytes as randomBytes9, randomUUID as randomUUID15 } from "crypto";
|
|
24398
24504
|
import { arch as arch2, platform as platform3 } from "os";
|
|
24399
24505
|
import { parse as parseYaml } from "yaml";
|
|
24400
24506
|
import WebSocket2 from "ws";
|
|
@@ -26573,12 +26679,12 @@ function formatTs(ts) {
|
|
|
26573
26679
|
|
|
26574
26680
|
// src/server.ts
|
|
26575
26681
|
import { execFileSync as execFileSync4, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
|
|
26576
|
-
import { createHash as
|
|
26577
|
-
import { appendFileSync, chmodSync as chmodSync5, closeSync as closeSync2, copyFileSync as copyFileSync2, cpSync, existsSync as
|
|
26682
|
+
import { createHash as createHash13, randomBytes as randomBytes8, randomUUID as randomUUID11 } from "crypto";
|
|
26683
|
+
import { appendFileSync, chmodSync as chmodSync5, closeSync as closeSync2, copyFileSync as copyFileSync2, cpSync, existsSync as existsSync10, mkdirSync as mkdirSync7, mkdtempSync, openSync as openSync2, readFileSync as readFileSync8, readSync, readdirSync as readdirSync5, rmSync as rmSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
26578
26684
|
import { createInterface as createInterface3 } from "readline";
|
|
26579
26685
|
import { createInterface as createPromptInterface } from "readline/promises";
|
|
26580
26686
|
import { homedir as homedir6 } from "os";
|
|
26581
|
-
import { dirname as dirname6, join as
|
|
26687
|
+
import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
|
|
26582
26688
|
|
|
26583
26689
|
// src/branding.ts
|
|
26584
26690
|
var OPENMATES_WORDMARK = "OPENMATES";
|
|
@@ -26614,33 +26720,33 @@ function renderSupportStartReminder() {
|
|
|
26614
26720
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
26615
26721
|
import {
|
|
26616
26722
|
copyFileSync,
|
|
26617
|
-
existsSync as
|
|
26618
|
-
mkdirSync as
|
|
26619
|
-
readdirSync as
|
|
26620
|
-
rmSync as
|
|
26723
|
+
existsSync as existsSync8,
|
|
26724
|
+
mkdirSync as mkdirSync5,
|
|
26725
|
+
readdirSync as readdirSync3,
|
|
26726
|
+
rmSync as rmSync4,
|
|
26621
26727
|
symlinkSync
|
|
26622
26728
|
} from "fs";
|
|
26623
26729
|
import { createRequire as createRequire3 } from "module";
|
|
26624
|
-
import { dirname as dirname3, join as
|
|
26625
|
-
var UI_PACKAGE_RELATIVE_PATH =
|
|
26626
|
-
var GENERATED_LOCALES_RELATIVE_PATH =
|
|
26627
|
-
var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH =
|
|
26730
|
+
import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
|
|
26731
|
+
var UI_PACKAGE_RELATIVE_PATH = join5("frontend", "packages", "ui");
|
|
26732
|
+
var GENERATED_LOCALES_RELATIVE_PATH = join5(UI_PACKAGE_RELATIVE_PATH, "src", "i18n", "locales");
|
|
26733
|
+
var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH = join5(UI_PACKAGE_RELATIVE_PATH, "scripts", "build-translations.js");
|
|
26628
26734
|
var REQUIRED_LOCALE_JSON = "en.json";
|
|
26629
26735
|
function sourceInstallLocalesPath(rootPath) {
|
|
26630
|
-
return
|
|
26736
|
+
return join5(rootPath, GENERATED_LOCALES_RELATIVE_PATH);
|
|
26631
26737
|
}
|
|
26632
26738
|
function ensureSourceInstallTranslations(installPath, sourcePath) {
|
|
26633
26739
|
const localeDir = sourceInstallLocalesPath(installPath);
|
|
26634
|
-
const requiredLocalePath =
|
|
26635
|
-
if (
|
|
26740
|
+
const requiredLocalePath = join5(localeDir, REQUIRED_LOCALE_JSON);
|
|
26741
|
+
if (existsSync8(requiredLocalePath)) {
|
|
26636
26742
|
return { status: "already_present", localeDir, copiedFiles: 0 };
|
|
26637
26743
|
}
|
|
26638
26744
|
const copiedFiles = copyGeneratedLocaleJson(sourcePath, installPath);
|
|
26639
|
-
if (
|
|
26745
|
+
if (existsSync8(requiredLocalePath)) {
|
|
26640
26746
|
return { status: "copied", localeDir, copiedFiles };
|
|
26641
26747
|
}
|
|
26642
26748
|
runBuildTranslationsScript(installPath);
|
|
26643
|
-
if (!
|
|
26749
|
+
if (!existsSync8(requiredLocalePath)) {
|
|
26644
26750
|
throw new Error(`Generated locale JSON was not created at ${requiredLocalePath}.`);
|
|
26645
26751
|
}
|
|
26646
26752
|
return { status: "built", localeDir, copiedFiles };
|
|
@@ -26651,31 +26757,31 @@ function copyGeneratedLocaleJson(sourcePath, installPath) {
|
|
|
26651
26757
|
const targetRoot = resolve4(installPath);
|
|
26652
26758
|
if (sourceRoot === targetRoot) return 0;
|
|
26653
26759
|
const sourceLocaleDir = sourceInstallLocalesPath(sourceRoot);
|
|
26654
|
-
if (!
|
|
26760
|
+
if (!existsSync8(join5(sourceLocaleDir, REQUIRED_LOCALE_JSON))) return 0;
|
|
26655
26761
|
const targetLocaleDir = sourceInstallLocalesPath(targetRoot);
|
|
26656
|
-
|
|
26762
|
+
mkdirSync5(targetLocaleDir, { recursive: true });
|
|
26657
26763
|
let copiedFiles = 0;
|
|
26658
|
-
for (const entry of
|
|
26764
|
+
for (const entry of readdirSync3(sourceLocaleDir, { withFileTypes: true })) {
|
|
26659
26765
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
26660
|
-
copyFileSync(
|
|
26766
|
+
copyFileSync(join5(sourceLocaleDir, entry.name), join5(targetLocaleDir, entry.name));
|
|
26661
26767
|
copiedFiles += 1;
|
|
26662
26768
|
}
|
|
26663
26769
|
return copiedFiles;
|
|
26664
26770
|
}
|
|
26665
26771
|
function runBuildTranslationsScript(installPath) {
|
|
26666
|
-
const uiPackageDir =
|
|
26667
|
-
const buildScript =
|
|
26668
|
-
if (!
|
|
26772
|
+
const uiPackageDir = join5(installPath, UI_PACKAGE_RELATIVE_PATH);
|
|
26773
|
+
const buildScript = join5(installPath, BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH);
|
|
26774
|
+
if (!existsSync8(buildScript)) {
|
|
26669
26775
|
throw new Error(`Translation build script not found at ${buildScript}.`);
|
|
26670
26776
|
}
|
|
26671
26777
|
const requireFromCli = createRequire3(import.meta.url);
|
|
26672
26778
|
const yamlPackageDir = dirname3(requireFromCli.resolve("yaml/package.json"));
|
|
26673
|
-
const nodeModulesDir =
|
|
26674
|
-
const yamlLink =
|
|
26675
|
-
const hadNodeModulesDir =
|
|
26676
|
-
const hadYamlPackage =
|
|
26779
|
+
const nodeModulesDir = join5(uiPackageDir, "node_modules");
|
|
26780
|
+
const yamlLink = join5(nodeModulesDir, "yaml");
|
|
26781
|
+
const hadNodeModulesDir = existsSync8(nodeModulesDir);
|
|
26782
|
+
const hadYamlPackage = existsSync8(yamlLink);
|
|
26677
26783
|
if (!hadYamlPackage) {
|
|
26678
|
-
|
|
26784
|
+
mkdirSync5(nodeModulesDir, { recursive: true });
|
|
26679
26785
|
symlinkSync(yamlPackageDir, yamlLink, process.platform === "win32" ? "junction" : "dir");
|
|
26680
26786
|
}
|
|
26681
26787
|
try {
|
|
@@ -26684,15 +26790,15 @@ function runBuildTranslationsScript(installPath) {
|
|
|
26684
26790
|
stdio: "inherit"
|
|
26685
26791
|
});
|
|
26686
26792
|
} finally {
|
|
26687
|
-
if (!hadYamlPackage)
|
|
26688
|
-
if (!hadNodeModulesDir &&
|
|
26689
|
-
|
|
26793
|
+
if (!hadYamlPackage) rmSync4(yamlLink, { recursive: true, force: true });
|
|
26794
|
+
if (!hadNodeModulesDir && existsSync8(nodeModulesDir) && readdirSync3(nodeModulesDir).length === 0) {
|
|
26795
|
+
rmSync4(nodeModulesDir, { recursive: true, force: true });
|
|
26690
26796
|
}
|
|
26691
26797
|
}
|
|
26692
26798
|
}
|
|
26693
26799
|
|
|
26694
26800
|
// src/serverPlanning.ts
|
|
26695
|
-
import { dirname as dirname4, join as
|
|
26801
|
+
import { dirname as dirname4, join as join6, resolve as resolve5 } from "path";
|
|
26696
26802
|
var CORE_WORKER_SERVICES = [
|
|
26697
26803
|
"task-worker",
|
|
26698
26804
|
"user-init-worker",
|
|
@@ -26902,10 +27008,10 @@ function parseServerRole(value) {
|
|
|
26902
27008
|
throw new Error(`Unsupported server role '${value}'. Use core, upload, or preview.`);
|
|
26903
27009
|
}
|
|
26904
27010
|
function defaultOpenMatesCloudOverlayPath(openMatesPath) {
|
|
26905
|
-
return resolve5(
|
|
27011
|
+
return resolve5(join6(dirname4(resolve5(openMatesPath)), OPENMATESCLOUD_OVERLAY_DIR));
|
|
26906
27012
|
}
|
|
26907
27013
|
function defaultOpenMatesCloudComposeFile(overlayPath) {
|
|
26908
|
-
return resolve5(
|
|
27014
|
+
return resolve5(join6(overlayPath, OPENMATESCLOUD_COMPOSE_FILE));
|
|
26909
27015
|
}
|
|
26910
27016
|
function planOpenMatesCloudOverlay(input) {
|
|
26911
27017
|
const deploymentMode = input.deploymentMode ?? "self_host";
|
|
@@ -27287,12 +27393,12 @@ function shouldAutoInstallRuntimeMonitoringServices(env) {
|
|
|
27287
27393
|
|
|
27288
27394
|
// src/serverBackupArchive.ts
|
|
27289
27395
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
27290
|
-
import { chmodSync as chmodSync3, lstatSync, readdirSync as
|
|
27396
|
+
import { chmodSync as chmodSync3, lstatSync, readdirSync as readdirSync4, renameSync as renameSync3, rmSync as rmSync5 } from "fs";
|
|
27291
27397
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
27292
27398
|
function assertRegularBackupTree(path2) {
|
|
27293
27399
|
const stat2 = lstatSync(path2);
|
|
27294
27400
|
if (stat2.isDirectory()) {
|
|
27295
|
-
for (const entry of
|
|
27401
|
+
for (const entry of readdirSync4(path2)) assertRegularBackupTree(`${path2}/${entry}`);
|
|
27296
27402
|
return;
|
|
27297
27403
|
}
|
|
27298
27404
|
if (!stat2.isFile() || stat2.nlink !== 1) {
|
|
@@ -27306,11 +27412,11 @@ function publishServerBackupArchive(sourceDir, archivePath, options = {}) {
|
|
|
27306
27412
|
try {
|
|
27307
27413
|
execFileSync3(options.tarCommand ?? "tar", ["-czf", temporaryArchivePath, "-C", sourceDir, "."], { stdio: "pipe" });
|
|
27308
27414
|
chmodSync3(temporaryArchivePath, 384);
|
|
27309
|
-
|
|
27415
|
+
renameSync3(temporaryArchivePath, archivePath);
|
|
27310
27416
|
chmodSync3(archivePath, 384);
|
|
27311
27417
|
} finally {
|
|
27312
27418
|
process.umask(previousUmask);
|
|
27313
|
-
|
|
27419
|
+
rmSync5(temporaryArchivePath, { force: true });
|
|
27314
27420
|
}
|
|
27315
27421
|
}
|
|
27316
27422
|
|
|
@@ -27908,16 +28014,16 @@ function signRuntimeWebhookPayload(payload, secret, timestamp, eventId) {
|
|
|
27908
28014
|
|
|
27909
28015
|
// src/serverUpdateState.ts
|
|
27910
28016
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
27911
|
-
import { chmodSync as chmodSync4, closeSync, existsSync as
|
|
27912
|
-
import { dirname as dirname5, join as
|
|
28017
|
+
import { chmodSync as chmodSync4, closeSync, existsSync as existsSync9, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
28018
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
27913
28019
|
function serverUpdateStatusFile(installPath, role) {
|
|
27914
|
-
return
|
|
28020
|
+
return join7(installPath, ".openmates", `${role}-update-status.json`);
|
|
27915
28021
|
}
|
|
27916
28022
|
function readServerUpdateStatus(installPath, role) {
|
|
27917
28023
|
const filePath = serverUpdateStatusFile(installPath, role);
|
|
27918
|
-
if (!
|
|
28024
|
+
if (!existsSync9(filePath)) return {};
|
|
27919
28025
|
try {
|
|
27920
|
-
const parsed = JSON.parse(
|
|
28026
|
+
const parsed = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
27921
28027
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27922
28028
|
return { statusReadError: "invalid_update_status" };
|
|
27923
28029
|
}
|
|
@@ -27928,23 +28034,23 @@ function readServerUpdateStatus(installPath, role) {
|
|
|
27928
28034
|
}
|
|
27929
28035
|
function writeServerUpdateStatus(installPath, role, status) {
|
|
27930
28036
|
const filePath = serverUpdateStatusFile(installPath, role);
|
|
27931
|
-
|
|
28037
|
+
mkdirSync6(dirname5(filePath), { recursive: true, mode: 448 });
|
|
27932
28038
|
const temporaryPath = `${filePath}.${process.pid}.${randomBytes7(4).toString("hex")}.tmp`;
|
|
27933
|
-
|
|
28039
|
+
writeFileSync5(temporaryPath, `${JSON.stringify({ role, updated_at: (/* @__PURE__ */ new Date()).toISOString(), ...status }, null, 2)}
|
|
27934
28040
|
`, { mode: 384 });
|
|
27935
|
-
|
|
28041
|
+
renameSync4(temporaryPath, filePath);
|
|
27936
28042
|
chmodSync4(filePath, 384);
|
|
27937
28043
|
}
|
|
27938
28044
|
function acquireServerUpdateLock(installPath) {
|
|
27939
|
-
const stateDir2 =
|
|
27940
|
-
const lockPath =
|
|
27941
|
-
|
|
28045
|
+
const stateDir2 = join7(installPath, ".openmates");
|
|
28046
|
+
const lockPath = join7(stateDir2, "server-update.lock");
|
|
28047
|
+
mkdirSync6(stateDir2, { recursive: true, mode: 448 });
|
|
27942
28048
|
let descriptor;
|
|
27943
28049
|
try {
|
|
27944
28050
|
descriptor = openSync(lockPath, "wx", 384);
|
|
27945
28051
|
} catch (error) {
|
|
27946
28052
|
if (error.code !== "EEXIST") throw error;
|
|
27947
|
-
const lockOwner =
|
|
28053
|
+
const lockOwner = readFileSync7(lockPath, "utf8").trim();
|
|
27948
28054
|
const lockPid = Number.parseInt(lockOwner.split(":", 1)[0] ?? "", 10);
|
|
27949
28055
|
let stale = false;
|
|
27950
28056
|
try {
|
|
@@ -27956,11 +28062,11 @@ function acquireServerUpdateLock(installPath) {
|
|
|
27956
28062
|
throw new Error(stale ? "A stale OpenMates server update lock exists for this installation; verify no update is running, then remove .openmates/server-update.lock explicitly." : "Another OpenMates server update is already running for this installation.");
|
|
27957
28063
|
}
|
|
27958
28064
|
const ownershipToken = `${process.pid}:${randomBytes7(16).toString("hex")}`;
|
|
27959
|
-
|
|
28065
|
+
writeFileSync5(descriptor, ownershipToken);
|
|
27960
28066
|
return () => {
|
|
27961
28067
|
closeSync(descriptor);
|
|
27962
|
-
if (
|
|
27963
|
-
|
|
28068
|
+
if (existsSync9(lockPath) && readFileSync7(lockPath, "utf8").trim() === ownershipToken) {
|
|
28069
|
+
rmSync6(lockPath);
|
|
27964
28070
|
}
|
|
27965
28071
|
};
|
|
27966
28072
|
}
|
|
@@ -28132,22 +28238,22 @@ function containsBoundedWebResult(value) {
|
|
|
28132
28238
|
}
|
|
28133
28239
|
|
|
28134
28240
|
// src/server.ts
|
|
28135
|
-
var SOURCE_COMPOSE_FILE =
|
|
28241
|
+
var SOURCE_COMPOSE_FILE = join8("backend", "core", "docker-compose.yml");
|
|
28136
28242
|
var ROLE_IMAGE_COMPOSE_FILES = {
|
|
28137
|
-
core:
|
|
28138
|
-
upload:
|
|
28139
|
-
preview:
|
|
28243
|
+
core: join8("backend", "core", "docker-compose.selfhost.yml"),
|
|
28244
|
+
upload: join8("backend", "upload", "docker-compose.yml"),
|
|
28245
|
+
preview: join8("backend", "preview", "docker-compose.preview.yml")
|
|
28140
28246
|
};
|
|
28141
28247
|
var ROLE_TEMPLATE_FILES = {
|
|
28142
|
-
core:
|
|
28143
|
-
upload:
|
|
28144
|
-
preview:
|
|
28248
|
+
core: join8("core", "docker-compose.selfhost.yml"),
|
|
28249
|
+
upload: join8("upload", "docker-compose.yml"),
|
|
28250
|
+
preview: join8("preview", "docker-compose.preview.yml")
|
|
28145
28251
|
};
|
|
28146
|
-
var CORE_NO_WEBAPP_TEMPLATE_FILE =
|
|
28147
|
-
var CORE_PROMTAIL_CONFIG_FILE =
|
|
28148
|
-
var CORE_ALERTMANAGER_CONFIG_FILE =
|
|
28149
|
-
var COMPOSE_OVERRIDE2 =
|
|
28150
|
-
var DEFAULT_INSTALL_PATH =
|
|
28252
|
+
var CORE_NO_WEBAPP_TEMPLATE_FILE = join8("core", "docker-compose.no-webapp.yml");
|
|
28253
|
+
var CORE_PROMTAIL_CONFIG_FILE = join8("backend", "core", "monitoring", "promtail", "promtail-config.yaml");
|
|
28254
|
+
var CORE_ALERTMANAGER_CONFIG_FILE = join8("backend", "core", "monitoring", "alertmanager", "alertmanager.yml");
|
|
28255
|
+
var COMPOSE_OVERRIDE2 = join8("backend", "core", "docker-compose.override.yml");
|
|
28256
|
+
var DEFAULT_INSTALL_PATH = join8(homedir6(), "openmates");
|
|
28151
28257
|
var REPO_URL = "https://github.com/glowingkitty/OpenMates.git";
|
|
28152
28258
|
var ROLE_PROVENANCE_SERVICE = {
|
|
28153
28259
|
core: "api",
|
|
@@ -28237,8 +28343,8 @@ scrape_configs:
|
|
|
28237
28343
|
- labels:
|
|
28238
28344
|
level: log_level
|
|
28239
28345
|
`;
|
|
28240
|
-
var BACKEND_CONFIG_FILE =
|
|
28241
|
-
var IMAGE_RUNTIME_CONFIG_FILE =
|
|
28346
|
+
var BACKEND_CONFIG_FILE = join8("backend", "config", "backend_config.yml");
|
|
28347
|
+
var IMAGE_RUNTIME_CONFIG_FILE = join8("config", "backend_config.yml");
|
|
28242
28348
|
var LOCAL_AI_MODELS_FILE = "local-ai-models.yml";
|
|
28243
28349
|
var OFF_BY_DEFAULT_FEATURES = /* @__PURE__ */ new Map([
|
|
28244
28350
|
["embed:code:application", "Application previews are still unstable"],
|
|
@@ -28374,9 +28480,9 @@ function runInteractive(cmd, args, cwd) {
|
|
|
28374
28480
|
});
|
|
28375
28481
|
}
|
|
28376
28482
|
function beginEngineeringRuntimeOperation(installPath, operationType, services) {
|
|
28377
|
-
const manager =
|
|
28378
|
-
const sharedConfig =
|
|
28379
|
-
if (!
|
|
28483
|
+
const manager = join8(installPath, "scripts", "engineering_control_plane.py");
|
|
28484
|
+
const sharedConfig = join8(homedir6(), ".config", "openmates", "engineering-control-plane.env");
|
|
28485
|
+
if (!existsSync10(manager) || !existsSync10(sharedConfig)) return null;
|
|
28380
28486
|
const requestedBy = process.env.OPENCODE_SESSION_ID || `openmates-cli:${process.pid}`;
|
|
28381
28487
|
const args = [
|
|
28382
28488
|
manager,
|
|
@@ -28400,7 +28506,7 @@ function beginEngineeringRuntimeOperation(installPath, operationType, services)
|
|
|
28400
28506
|
return parsed.operation_key;
|
|
28401
28507
|
}
|
|
28402
28508
|
function finishEngineeringRuntimeOperation(installPath, operationKey, status) {
|
|
28403
|
-
const manager =
|
|
28509
|
+
const manager = join8(installPath, "scripts", "engineering_control_plane.py");
|
|
28404
28510
|
execFileSync4(
|
|
28405
28511
|
"python3",
|
|
28406
28512
|
[manager, "operation", "update", "--operation-key", operationKey, "--status", status],
|
|
@@ -28429,8 +28535,8 @@ function loadConfigForInstallPath(installPath) {
|
|
|
28429
28535
|
}
|
|
28430
28536
|
function getInstallMode(installPath, config = loadConfigForInstallPath(installPath)) {
|
|
28431
28537
|
if (config?.installMode) return config.installMode;
|
|
28432
|
-
if (
|
|
28433
|
-
if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) =>
|
|
28538
|
+
if (existsSync10(join8(installPath, SOURCE_COMPOSE_FILE))) return "source";
|
|
28539
|
+
if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync10(join8(installPath, composeFile)))) return "image";
|
|
28434
28540
|
return "source";
|
|
28435
28541
|
}
|
|
28436
28542
|
function getServerRole(flags, config) {
|
|
@@ -28561,26 +28667,26 @@ function composeArgs(installPath, withOverrides, installMode = getInstallMode(in
|
|
|
28561
28667
|
withOverrides,
|
|
28562
28668
|
installMode,
|
|
28563
28669
|
role,
|
|
28564
|
-
overrideExists:
|
|
28670
|
+
overrideExists: existsSync10(join8(installPath, COMPOSE_OVERRIDE2)),
|
|
28565
28671
|
deploymentMode,
|
|
28566
28672
|
overlayPath,
|
|
28567
28673
|
overlayComposeFile,
|
|
28568
|
-
overlayExists: deploymentMode === "official_cloud" &&
|
|
28674
|
+
overlayExists: deploymentMode === "official_cloud" && existsSync10(resolvedOverlayPath) && existsSync10(overlayComposeFile)
|
|
28569
28675
|
});
|
|
28570
28676
|
}
|
|
28571
28677
|
function ensureGitWorkDirEnv(installPath) {
|
|
28572
|
-
const envPath =
|
|
28573
|
-
if (!
|
|
28574
|
-
const content =
|
|
28678
|
+
const envPath = join8(installPath, ".env");
|
|
28679
|
+
if (!existsSync10(envPath)) return;
|
|
28680
|
+
const content = readFileSync8(envPath, "utf-8");
|
|
28575
28681
|
const lineRegex = /^GIT_WORK_DIR=.*$/m;
|
|
28576
28682
|
const value = `GIT_WORK_DIR=${installPath}`;
|
|
28577
28683
|
if (lineRegex.test(content)) {
|
|
28578
28684
|
const next = content.replace(lineRegex, value);
|
|
28579
|
-
if (next !== content)
|
|
28685
|
+
if (next !== content) writeFileSync6(envPath, next);
|
|
28580
28686
|
return;
|
|
28581
28687
|
}
|
|
28582
28688
|
const separator2 = content.endsWith("\n") ? "" : "\n";
|
|
28583
|
-
|
|
28689
|
+
writeFileSync6(envPath, `${content}${separator2}${value}
|
|
28584
28690
|
`);
|
|
28585
28691
|
}
|
|
28586
28692
|
function requireDocker() {
|
|
@@ -28601,7 +28707,7 @@ function requireGit() {
|
|
|
28601
28707
|
}
|
|
28602
28708
|
function getPackageVersion() {
|
|
28603
28709
|
try {
|
|
28604
|
-
const packageJson = JSON.parse(
|
|
28710
|
+
const packageJson = JSON.parse(readFileSync8(new URL("../package.json", import.meta.url), "utf-8"));
|
|
28605
28711
|
return packageJson.version ?? "";
|
|
28606
28712
|
} catch {
|
|
28607
28713
|
return "";
|
|
@@ -28694,20 +28800,20 @@ async function fetchText(url) {
|
|
|
28694
28800
|
return response.text();
|
|
28695
28801
|
}
|
|
28696
28802
|
function packagedTemplatePath(role) {
|
|
28697
|
-
return
|
|
28803
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
|
|
28698
28804
|
}
|
|
28699
28805
|
function packagedNoWebappTemplatePath() {
|
|
28700
|
-
return
|
|
28806
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", CORE_NO_WEBAPP_TEMPLATE_FILE);
|
|
28701
28807
|
}
|
|
28702
28808
|
function packagedCaddyTemplatePath(role) {
|
|
28703
|
-
return
|
|
28809
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
|
|
28704
28810
|
}
|
|
28705
28811
|
function packagedCoreAlertmanagerTemplatePath() {
|
|
28706
|
-
return
|
|
28812
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", "core", "monitoring", "alertmanager", "alertmanager.yml");
|
|
28707
28813
|
}
|
|
28708
28814
|
function readOfficialCloudNoWebappComposeTemplate() {
|
|
28709
28815
|
const packaged = packagedNoWebappTemplatePath();
|
|
28710
|
-
if (
|
|
28816
|
+
if (existsSync10(packaged)) return readFileSync8(packaged, "utf-8");
|
|
28711
28817
|
return [
|
|
28712
28818
|
"# backend/core/docker-compose.no-webapp.yml",
|
|
28713
28819
|
"#",
|
|
@@ -28722,51 +28828,51 @@ function readOfficialCloudNoWebappComposeTemplate() {
|
|
|
28722
28828
|
}
|
|
28723
28829
|
function ensureOfficialCloudNoWebappComposeFile(installPath, deploymentMode, role) {
|
|
28724
28830
|
if (deploymentMode !== "official_cloud" || role !== "core") return;
|
|
28725
|
-
const filePath =
|
|
28726
|
-
if (
|
|
28727
|
-
|
|
28728
|
-
|
|
28831
|
+
const filePath = join8(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE);
|
|
28832
|
+
if (existsSync10(filePath)) return;
|
|
28833
|
+
mkdirSync7(dirname6(filePath), { recursive: true });
|
|
28834
|
+
writeFileSync6(filePath, readOfficialCloudNoWebappComposeTemplate());
|
|
28729
28835
|
}
|
|
28730
28836
|
function fileHash(path2) {
|
|
28731
|
-
if (!
|
|
28732
|
-
return
|
|
28837
|
+
if (!existsSync10(path2)) return null;
|
|
28838
|
+
return createHash13("sha256").update(readFileSync8(path2)).digest("hex");
|
|
28733
28839
|
}
|
|
28734
28840
|
async function loadSelfHostComposeTemplate(templateRef, role) {
|
|
28735
28841
|
const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
|
|
28736
28842
|
if (templateDir) {
|
|
28737
|
-
return
|
|
28843
|
+
return readFileSync8(join8(resolve6(templateDir), ROLE_TEMPLATE_FILES[role]), "utf-8");
|
|
28738
28844
|
}
|
|
28739
28845
|
const overrideUrl = process.env.OPENMATES_SELFHOST_COMPOSE_URL;
|
|
28740
28846
|
if (overrideUrl) {
|
|
28741
28847
|
return fetchText(overrideUrl);
|
|
28742
28848
|
}
|
|
28743
28849
|
const packaged = packagedTemplatePath(role);
|
|
28744
|
-
if (
|
|
28850
|
+
if (existsSync10(packaged)) return readFileSync8(packaged, "utf-8");
|
|
28745
28851
|
return fetchText(`https://raw.githubusercontent.com/glowingkitty/OpenMates/${templateRef}/${ROLE_IMAGE_COMPOSE_FILES[role]}`);
|
|
28746
28852
|
}
|
|
28747
28853
|
async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
|
|
28748
|
-
const roleDir =
|
|
28749
|
-
const vaultConfigDir =
|
|
28750
|
-
|
|
28751
|
-
|
|
28752
|
-
|
|
28854
|
+
const roleDir = join8(installPath, "backend", role === "core" ? "core" : role);
|
|
28855
|
+
const vaultConfigDir = join8(roleDir, "vault", "config");
|
|
28856
|
+
mkdirSync7(vaultConfigDir, { recursive: true });
|
|
28857
|
+
mkdirSync7(join8(installPath, "config", "providers"), { recursive: true });
|
|
28858
|
+
writeFileSync6(join8(installPath, ROLE_IMAGE_COMPOSE_FILES[role]), await loadSelfHostComposeTemplate(templateRefForImageTag(imageTag, getPackageVersion()), role));
|
|
28753
28859
|
if (role === "core") {
|
|
28754
|
-
|
|
28860
|
+
writeFileSync6(join8(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE), readOfficialCloudNoWebappComposeTemplate());
|
|
28755
28861
|
}
|
|
28756
28862
|
if (role === "core") {
|
|
28757
|
-
const promtailConfigPath =
|
|
28758
|
-
|
|
28759
|
-
|
|
28863
|
+
const promtailConfigPath = join8(installPath, CORE_PROMTAIL_CONFIG_FILE);
|
|
28864
|
+
mkdirSync7(dirname6(promtailConfigPath), { recursive: true });
|
|
28865
|
+
writeFileSync6(promtailConfigPath, SELFHOST_PROMTAIL_CONFIG_TEMPLATE);
|
|
28760
28866
|
const alertmanagerTemplatePath = packagedCoreAlertmanagerTemplatePath();
|
|
28761
|
-
if (!
|
|
28762
|
-
const alertmanagerConfigPath =
|
|
28763
|
-
|
|
28867
|
+
if (!existsSync10(alertmanagerTemplatePath)) throw new Error(`Packaged Alertmanager config not found: ${alertmanagerTemplatePath}`);
|
|
28868
|
+
const alertmanagerConfigPath = join8(installPath, CORE_ALERTMANAGER_CONFIG_FILE);
|
|
28869
|
+
mkdirSync7(dirname6(alertmanagerConfigPath), { recursive: true });
|
|
28764
28870
|
copyFileSync2(alertmanagerTemplatePath, alertmanagerConfigPath);
|
|
28765
28871
|
}
|
|
28766
|
-
|
|
28872
|
+
writeFileSync6(join8(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
|
|
28767
28873
|
ensureImageRuntimeConfig(installPath);
|
|
28768
|
-
const envPath =
|
|
28769
|
-
let envContent =
|
|
28874
|
+
const envPath = join8(installPath, ".env");
|
|
28875
|
+
let envContent = existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : MINIMAL_ENV_TEMPLATE;
|
|
28770
28876
|
envContent = setEnvIfEmpty(envContent, "DATABASE_ADMIN_PASSWORD", randomHex(12));
|
|
28771
28877
|
envContent = setEnvIfEmpty(envContent, "DATABASE_PASSWORD", randomHex(12));
|
|
28772
28878
|
envContent = setEnvIfEmpty(envContent, "DIRECTUS_TOKEN", randomHex(32));
|
|
@@ -28778,13 +28884,13 @@ async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
|
|
|
28778
28884
|
envContent = setEnvVar(envContent, "OPENMATES_IMAGE_TAG", imageTag);
|
|
28779
28885
|
envContent = setEnvVar(envContent, "OPENMATES_IMAGE_REGISTRY", DEFAULT_IMAGE_REGISTRY);
|
|
28780
28886
|
envContent = setEnvVar(envContent, "GIT_WORK_DIR", installPath);
|
|
28781
|
-
|
|
28887
|
+
writeFileSync6(envPath, envContent.endsWith("\n") ? envContent : `${envContent}
|
|
28782
28888
|
`);
|
|
28783
28889
|
}
|
|
28784
28890
|
function getImageTagFromEnv(installPath, config) {
|
|
28785
|
-
const envPath =
|
|
28786
|
-
if (
|
|
28787
|
-
const envTag = getEnvVar(
|
|
28891
|
+
const envPath = join8(installPath, ".env");
|
|
28892
|
+
if (existsSync10(envPath)) {
|
|
28893
|
+
const envTag = getEnvVar(readFileSync8(envPath, "utf-8"), "OPENMATES_IMAGE_TAG");
|
|
28788
28894
|
if (envTag) return envTag;
|
|
28789
28895
|
}
|
|
28790
28896
|
return config?.imageTag ?? "";
|
|
@@ -28817,8 +28923,8 @@ async function waitForServerHealth(installPath, role = "core", options = {}) {
|
|
|
28817
28923
|
}
|
|
28818
28924
|
throw new Error(`Updated ${role} server did not pass health checks in time. Tried ${healthUrl}.`);
|
|
28819
28925
|
}
|
|
28820
|
-
const envPath =
|
|
28821
|
-
const envContent =
|
|
28926
|
+
const envPath = join8(installPath, ".env");
|
|
28927
|
+
const envContent = existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "";
|
|
28822
28928
|
const urls = deriveSelfHostCliUrls(envContent);
|
|
28823
28929
|
const apiHealthUrl = `${trailingSlashTrimmed(urls.apiUrl)}/health`;
|
|
28824
28930
|
const appUrl = trailingSlashTrimmed(urls.appUrl);
|
|
@@ -28841,9 +28947,9 @@ function defaultCloneBranchForVersion(version) {
|
|
|
28841
28947
|
return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : null;
|
|
28842
28948
|
}
|
|
28843
28949
|
function hasLlmCredentials(envPath) {
|
|
28844
|
-
if (!
|
|
28950
|
+
if (!existsSync10(envPath)) return false;
|
|
28845
28951
|
if (hasLocalAiModels(dirname6(envPath))) return true;
|
|
28846
|
-
const content =
|
|
28952
|
+
const content = readFileSync8(envPath, "utf-8");
|
|
28847
28953
|
for (const line of content.split("\n")) {
|
|
28848
28954
|
const trimmed = line.trim();
|
|
28849
28955
|
if (trimmed.startsWith("#") || !trimmed) continue;
|
|
@@ -28858,8 +28964,8 @@ function hasLlmCredentials(envPath) {
|
|
|
28858
28964
|
return false;
|
|
28859
28965
|
}
|
|
28860
28966
|
function warnIfMissingLlmCredentials(installPath) {
|
|
28861
|
-
const envPath =
|
|
28862
|
-
if (!
|
|
28967
|
+
const envPath = join8(installPath, ".env");
|
|
28968
|
+
if (!existsSync10(envPath)) {
|
|
28863
28969
|
console.error(
|
|
28864
28970
|
"No .env file found. Run 'openmates server install' first, or create .env from .env.example."
|
|
28865
28971
|
);
|
|
@@ -28884,22 +28990,22 @@ function printJson(data) {
|
|
|
28884
28990
|
console.log(JSON.stringify(data, null, 2));
|
|
28885
28991
|
}
|
|
28886
28992
|
function localModelsOverlayPath(installPath, installMode = getInstallMode(installPath)) {
|
|
28887
|
-
if (installMode === "source") return
|
|
28888
|
-
return
|
|
28993
|
+
if (installMode === "source") return join8(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
|
|
28994
|
+
return join8(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
|
|
28889
28995
|
}
|
|
28890
28996
|
function imageBackendConfigPath(installPath) {
|
|
28891
|
-
return
|
|
28997
|
+
return join8(installPath, IMAGE_RUNTIME_CONFIG_FILE);
|
|
28892
28998
|
}
|
|
28893
28999
|
function ensureImageRuntimeConfig(installPath) {
|
|
28894
29000
|
const configPath = imageBackendConfigPath(installPath);
|
|
28895
|
-
if (
|
|
28896
|
-
|
|
28897
|
-
|
|
29001
|
+
if (existsSync10(configPath)) return;
|
|
29002
|
+
mkdirSync7(dirname6(configPath), { recursive: true });
|
|
29003
|
+
writeFileSync6(configPath, renderFeatureOverrides({ enabled: [], disabled: [] }));
|
|
28898
29004
|
}
|
|
28899
29005
|
function readLocalModelOverlay(path2) {
|
|
28900
|
-
if (!
|
|
29006
|
+
if (!existsSync10(path2)) return { providers: [] };
|
|
28901
29007
|
try {
|
|
28902
|
-
const parsed = JSON.parse(
|
|
29008
|
+
const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
|
|
28903
29009
|
return { providers: Array.isArray(parsed.providers) ? parsed.providers : [] };
|
|
28904
29010
|
} catch (error) {
|
|
28905
29011
|
throw new Error(
|
|
@@ -28908,15 +29014,15 @@ function readLocalModelOverlay(path2) {
|
|
|
28908
29014
|
}
|
|
28909
29015
|
}
|
|
28910
29016
|
function writeLocalModelOverlay(path2, overlay) {
|
|
28911
|
-
|
|
28912
|
-
|
|
29017
|
+
mkdirSync7(dirname6(path2), { recursive: true });
|
|
29018
|
+
writeFileSync6(path2, `${JSON.stringify(overlay, null, 2)}
|
|
28913
29019
|
`);
|
|
28914
29020
|
}
|
|
28915
29021
|
function hasLocalAiModels(installPath) {
|
|
28916
|
-
const imagePath =
|
|
28917
|
-
const sourcePath =
|
|
29022
|
+
const imagePath = join8(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
|
|
29023
|
+
const sourcePath = join8(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
|
|
28918
29024
|
return [imagePath, sourcePath].some((path2) => {
|
|
28919
|
-
if (!
|
|
29025
|
+
if (!existsSync10(path2)) return false;
|
|
28920
29026
|
try {
|
|
28921
29027
|
return readLocalModelOverlay(path2).providers.some((provider) => provider.models.length > 0);
|
|
28922
29028
|
} catch {
|
|
@@ -28957,10 +29063,10 @@ function nowStamp() {
|
|
|
28957
29063
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28958
29064
|
}
|
|
28959
29065
|
function backupRoot(installPath) {
|
|
28960
|
-
return
|
|
29066
|
+
return join8(installPath, "backups");
|
|
28961
29067
|
}
|
|
28962
29068
|
function roleBackupDir(installPath, role) {
|
|
28963
|
-
return
|
|
29069
|
+
return join8(backupRoot(installPath), role);
|
|
28964
29070
|
}
|
|
28965
29071
|
function persistQuickServerTestOutcome(installPath, role, outcome) {
|
|
28966
29072
|
const current = readServerUpdateStatus(installPath, role);
|
|
@@ -29109,15 +29215,15 @@ function sourceUpdateMetadata(installPath) {
|
|
|
29109
29215
|
};
|
|
29110
29216
|
}
|
|
29111
29217
|
function copyIfExists(source, destination) {
|
|
29112
|
-
if (!
|
|
29113
|
-
|
|
29218
|
+
if (!existsSync10(source)) return;
|
|
29219
|
+
mkdirSync7(dirname6(destination), { recursive: true });
|
|
29114
29220
|
cpSync(source, destination, { recursive: true, force: true });
|
|
29115
29221
|
}
|
|
29116
29222
|
function readEnvMap(installPath) {
|
|
29117
|
-
const envPath =
|
|
29118
|
-
if (!
|
|
29223
|
+
const envPath = join8(installPath, ".env");
|
|
29224
|
+
if (!existsSync10(envPath)) return {};
|
|
29119
29225
|
const values = {};
|
|
29120
|
-
for (const line of
|
|
29226
|
+
for (const line of readFileSync8(envPath, "utf-8").split("\n")) {
|
|
29121
29227
|
const trimmed = line.trim();
|
|
29122
29228
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
29123
29229
|
const eqIdx = trimmed.indexOf("=");
|
|
@@ -29127,22 +29233,22 @@ function readEnvMap(installPath) {
|
|
|
29127
29233
|
return values;
|
|
29128
29234
|
}
|
|
29129
29235
|
function envPathForInstall(installPath) {
|
|
29130
|
-
return
|
|
29236
|
+
return join8(installPath, ".env");
|
|
29131
29237
|
}
|
|
29132
29238
|
function readEnvContent(installPath) {
|
|
29133
29239
|
const envPath = envPathForInstall(installPath);
|
|
29134
|
-
return
|
|
29240
|
+
return existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "";
|
|
29135
29241
|
}
|
|
29136
29242
|
function pruneEnvBackups(installPath) {
|
|
29137
|
-
const backups =
|
|
29243
|
+
const backups = readdirSync5(installPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith(ENV_BACKUP_PREFIX)).map((entry) => entry.name).sort();
|
|
29138
29244
|
for (const backup of backups.slice(0, Math.max(0, backups.length - ENV_BACKUP_RETENTION_COUNT))) {
|
|
29139
|
-
|
|
29245
|
+
rmSync7(join8(installPath, backup), { force: true });
|
|
29140
29246
|
}
|
|
29141
29247
|
}
|
|
29142
29248
|
function backupEnvFile(installPath) {
|
|
29143
29249
|
const envPath = envPathForInstall(installPath);
|
|
29144
|
-
if (!
|
|
29145
|
-
const backupPath =
|
|
29250
|
+
if (!existsSync10(envPath)) return null;
|
|
29251
|
+
const backupPath = join8(installPath, `${ENV_BACKUP_PREFIX}${nowStamp()}`);
|
|
29146
29252
|
copyFileSync2(envPath, backupPath);
|
|
29147
29253
|
chmodSync5(backupPath, 384);
|
|
29148
29254
|
pruneEnvBackups(installPath);
|
|
@@ -29150,8 +29256,8 @@ function backupEnvFile(installPath) {
|
|
|
29150
29256
|
}
|
|
29151
29257
|
function writeEnvContent(installPath, content) {
|
|
29152
29258
|
const envPath = envPathForInstall(installPath);
|
|
29153
|
-
|
|
29154
|
-
|
|
29259
|
+
mkdirSync7(dirname6(envPath), { recursive: true });
|
|
29260
|
+
writeFileSync6(envPath, content, { mode: 384 });
|
|
29155
29261
|
chmodSync5(envPath, 384);
|
|
29156
29262
|
}
|
|
29157
29263
|
function writeDeploymentModeEnv(installPath, deploymentMode, overlayPath) {
|
|
@@ -29166,7 +29272,7 @@ function writeDeploymentModeEnv(installPath, deploymentMode, overlayPath) {
|
|
|
29166
29272
|
}
|
|
29167
29273
|
const resolvedOverlayPath = resolve6(overlayPath ?? defaultOpenMatesCloudOverlayPath(installPath));
|
|
29168
29274
|
const overlayComposeFile = defaultOpenMatesCloudComposeFile(resolvedOverlayPath);
|
|
29169
|
-
if (!
|
|
29275
|
+
if (!existsSync10(resolvedOverlayPath) || !existsSync10(overlayComposeFile)) {
|
|
29170
29276
|
throw new Error(
|
|
29171
29277
|
`OpenMatesCloud official-cloud mode requires an overlay checkout with ${overlayComposeFile}. Pass --openmatescloud-path <dir> or create the sibling OpenMatesCloud repository.`
|
|
29172
29278
|
);
|
|
@@ -29363,7 +29469,7 @@ function formatSecretPreflight(preflight) {
|
|
|
29363
29469
|
return parts.join("; ");
|
|
29364
29470
|
}
|
|
29365
29471
|
function hashFile(path2) {
|
|
29366
|
-
const hash =
|
|
29472
|
+
const hash = createHash13("sha256");
|
|
29367
29473
|
const fd = openSync2(path2, "r");
|
|
29368
29474
|
const buffer = Buffer.allocUnsafe(CHECKSUM_BUFFER_BYTES);
|
|
29369
29475
|
try {
|
|
@@ -29380,8 +29486,8 @@ function hashFile(path2) {
|
|
|
29380
29486
|
function writeChecksums(rootDir) {
|
|
29381
29487
|
const lines = [];
|
|
29382
29488
|
const walk = (dir) => {
|
|
29383
|
-
for (const entry of
|
|
29384
|
-
const path2 =
|
|
29489
|
+
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
29490
|
+
const path2 = join8(dir, entry.name);
|
|
29385
29491
|
if (entry.isDirectory()) {
|
|
29386
29492
|
walk(path2);
|
|
29387
29493
|
continue;
|
|
@@ -29392,13 +29498,13 @@ function writeChecksums(rootDir) {
|
|
|
29392
29498
|
}
|
|
29393
29499
|
};
|
|
29394
29500
|
walk(rootDir);
|
|
29395
|
-
|
|
29501
|
+
writeFileSync6(join8(rootDir, "checksums.sha256"), `${lines.sort().join("\n")}
|
|
29396
29502
|
`);
|
|
29397
29503
|
}
|
|
29398
29504
|
function verifyChecksums(rootDir) {
|
|
29399
|
-
const checksumsPath =
|
|
29400
|
-
if (!
|
|
29401
|
-
for (const line of
|
|
29505
|
+
const checksumsPath = join8(rootDir, "checksums.sha256");
|
|
29506
|
+
if (!existsSync10(checksumsPath)) throw new Error("Backup archive is missing checksums.sha256.");
|
|
29507
|
+
for (const line of readFileSync8(checksumsPath, "utf-8").split("\n")) {
|
|
29402
29508
|
const trimmed = line.trim();
|
|
29403
29509
|
if (!trimmed) continue;
|
|
29404
29510
|
const match = trimmed.match(/^([a-f0-9]{64}) {2}(.+)$/);
|
|
@@ -29407,8 +29513,8 @@ function verifyChecksums(rootDir) {
|
|
|
29407
29513
|
if (relative3.startsWith("/") || relative3.split(/[\\/]/).includes("..")) {
|
|
29408
29514
|
throw new Error(`Unsafe checksum path in backup archive: ${relative3}`);
|
|
29409
29515
|
}
|
|
29410
|
-
const filePath =
|
|
29411
|
-
if (!
|
|
29516
|
+
const filePath = join8(rootDir, relative3);
|
|
29517
|
+
if (!existsSync10(filePath)) throw new Error(`Backup archive is missing checksummed file: ${relative3}`);
|
|
29412
29518
|
const actual = hashFile(filePath);
|
|
29413
29519
|
if (actual !== match[1]) throw new Error(`Backup checksum mismatch for ${relative3}.`);
|
|
29414
29520
|
}
|
|
@@ -29416,28 +29522,28 @@ function verifyChecksums(rootDir) {
|
|
|
29416
29522
|
function createServerBackup(installPath, role, options = {}) {
|
|
29417
29523
|
const plan = planBackup({ role, includeObservability: options.includeObservability });
|
|
29418
29524
|
const backupDir = roleBackupDir(installPath, role);
|
|
29419
|
-
|
|
29420
|
-
const archivePath = options.output ? resolve6(options.output) :
|
|
29421
|
-
|
|
29422
|
-
const tempDir = mkdtempSync(
|
|
29525
|
+
mkdirSync7(backupDir, { recursive: true, mode: 448 });
|
|
29526
|
+
const archivePath = options.output ? resolve6(options.output) : join8(backupDir, options.preUpdate ? `latest-pre-update-${role}.tar.gz` : `openmates-${role}-${nowStamp()}.tar.gz`);
|
|
29527
|
+
mkdirSync7(dirname6(archivePath), { recursive: true, mode: 448 });
|
|
29528
|
+
const tempDir = mkdtempSync(join8(backupDir, ".tmp-"));
|
|
29423
29529
|
const env = readEnvMap(installPath);
|
|
29424
29530
|
const manifest = {
|
|
29425
29531
|
role,
|
|
29426
29532
|
backup_format_version: BACKUP_FORMAT_VERSION,
|
|
29427
29533
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29428
29534
|
cli_version: getPackageVersion(),
|
|
29429
|
-
image_tag: getEnvVar(
|
|
29535
|
+
image_tag: getEnvVar(existsSync10(join8(installPath, ".env")) ? readFileSync8(join8(installPath, ".env"), "utf-8") : "", "OPENMATES_IMAGE_TAG"),
|
|
29430
29536
|
recovery_scope: role === "core" ? "database-and-runtime-only" : "runtime-only",
|
|
29431
29537
|
include_observability: false,
|
|
29432
|
-
contents: plan.contents.filter((content) => content !== "runtime-env" ||
|
|
29538
|
+
contents: plan.contents.filter((content) => content !== "runtime-env" || existsSync10(join8(installPath, ".env"))).filter((content) => content !== "runtime-config" || existsSync10(join8(installPath, "config")))
|
|
29433
29539
|
};
|
|
29434
29540
|
try {
|
|
29435
|
-
copyIfExists(
|
|
29436
|
-
copyIfExists(
|
|
29541
|
+
copyIfExists(join8(installPath, ".env"), join8(tempDir, "runtime", ".env"));
|
|
29542
|
+
copyIfExists(join8(installPath, "config"), join8(tempDir, "runtime", "config"));
|
|
29437
29543
|
if (role === "core") {
|
|
29438
29544
|
const databaseUser = env.DATABASE_USERNAME || "directus";
|
|
29439
29545
|
const databaseName = env.DATABASE_NAME || "directus";
|
|
29440
|
-
const dumpFile = openSync2(
|
|
29546
|
+
const dumpFile = openSync2(join8(tempDir, "postgres.sql"), "w");
|
|
29441
29547
|
try {
|
|
29442
29548
|
const result = spawnSync(
|
|
29443
29549
|
"docker",
|
|
@@ -29452,35 +29558,35 @@ function createServerBackup(installPath, role, options = {}) {
|
|
|
29452
29558
|
closeSync2(dumpFile);
|
|
29453
29559
|
}
|
|
29454
29560
|
}
|
|
29455
|
-
|
|
29561
|
+
writeFileSync6(join8(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
29456
29562
|
`, { mode: 384 });
|
|
29457
29563
|
writeChecksums(tempDir);
|
|
29458
29564
|
publishServerBackupArchive(tempDir, archivePath);
|
|
29459
29565
|
return archivePath;
|
|
29460
29566
|
} finally {
|
|
29461
|
-
|
|
29567
|
+
rmSync7(tempDir, { recursive: true, force: true });
|
|
29462
29568
|
}
|
|
29463
29569
|
}
|
|
29464
29570
|
function restoreServerBackup(installPath, role, file) {
|
|
29465
29571
|
const archivePath = resolve6(file);
|
|
29466
|
-
if (!
|
|
29467
|
-
|
|
29468
|
-
const tempDir = mkdtempSync(
|
|
29572
|
+
if (!existsSync10(archivePath)) throw new Error(`Backup file not found: ${archivePath}`);
|
|
29573
|
+
mkdirSync7(roleBackupDir(installPath, role), { recursive: true, mode: 448 });
|
|
29574
|
+
const tempDir = mkdtempSync(join8(roleBackupDir(installPath, role), ".restore-"));
|
|
29469
29575
|
const env = readEnvMap(installPath);
|
|
29470
29576
|
try {
|
|
29471
29577
|
execSync(`tar -xzf ${shellQuote(archivePath)} -C ${shellQuote(tempDir)}`, { stdio: "pipe" });
|
|
29472
29578
|
verifyChecksums(tempDir);
|
|
29473
|
-
const manifestPath =
|
|
29474
|
-
if (!
|
|
29475
|
-
const manifest = JSON.parse(
|
|
29579
|
+
const manifestPath = join8(tempDir, "manifest.json");
|
|
29580
|
+
if (!existsSync10(manifestPath)) throw new Error("Backup archive is missing manifest.json.");
|
|
29581
|
+
const manifest = JSON.parse(readFileSync8(manifestPath, "utf-8"));
|
|
29476
29582
|
if (manifest.role !== role) throw new Error(`Backup role '${manifest.role}' does not match requested role '${role}'.`);
|
|
29477
29583
|
if (role === "core" && manifest.backup_format_version === BACKUP_FORMAT_VERSION && manifest.recovery_scope !== "full-core") {
|
|
29478
29584
|
throw new Error("This backup records database and runtime state only; refusing unsafe full core restore.");
|
|
29479
29585
|
}
|
|
29480
|
-
copyIfExists(
|
|
29481
|
-
copyIfExists(
|
|
29482
|
-
const postgresDump =
|
|
29483
|
-
if (role === "core" &&
|
|
29586
|
+
copyIfExists(join8(tempDir, "runtime", ".env"), join8(installPath, ".env"));
|
|
29587
|
+
copyIfExists(join8(tempDir, "runtime", "config"), join8(installPath, "config"));
|
|
29588
|
+
const postgresDump = join8(tempDir, "postgres.sql");
|
|
29589
|
+
if (role === "core" && existsSync10(postgresDump)) {
|
|
29484
29590
|
const databaseUser = env.DATABASE_USERNAME || "directus";
|
|
29485
29591
|
const databaseName = env.DATABASE_NAME || "directus";
|
|
29486
29592
|
const dumpFile = openSync2(postgresDump, "r");
|
|
@@ -29497,7 +29603,7 @@ function restoreServerBackup(installPath, role, file) {
|
|
|
29497
29603
|
}
|
|
29498
29604
|
}
|
|
29499
29605
|
} finally {
|
|
29500
|
-
|
|
29606
|
+
rmSync7(tempDir, { recursive: true, force: true });
|
|
29501
29607
|
}
|
|
29502
29608
|
}
|
|
29503
29609
|
function restoreStopServices(installPath, withOverrides, installMode, role) {
|
|
@@ -29766,11 +29872,11 @@ async function serverRegister(flags) {
|
|
|
29766
29872
|
const installPath = resolveServerPath(
|
|
29767
29873
|
typeof flags.path === "string" ? flags : { ...flags, path: process.cwd() }
|
|
29768
29874
|
);
|
|
29769
|
-
const envPath =
|
|
29770
|
-
if (!
|
|
29771
|
-
throw new Error(`Source Compose file not found: ${
|
|
29875
|
+
const envPath = join8(installPath, ".env");
|
|
29876
|
+
if (!existsSync10(join8(installPath, SOURCE_COMPOSE_FILE))) {
|
|
29877
|
+
throw new Error(`Source Compose file not found: ${join8(installPath, SOURCE_COMPOSE_FILE)}`);
|
|
29772
29878
|
}
|
|
29773
|
-
if (!
|
|
29879
|
+
if (!existsSync10(envPath)) {
|
|
29774
29880
|
throw new Error(`Runtime env file not found: ${envPath}`);
|
|
29775
29881
|
}
|
|
29776
29882
|
const gitRoot = resolve6(exec("git rev-parse --show-toplevel", installPath));
|
|
@@ -29792,7 +29898,7 @@ async function serverRegister(flags) {
|
|
|
29792
29898
|
}
|
|
29793
29899
|
const overlayPath = getOpenMatesCloudOverlayPath(flags, null) ?? readEnvMap(installPath).OPENMATES_CLOUD_OVERLAY_PATH ?? defaultOpenMatesCloudOverlayPath(installPath);
|
|
29794
29900
|
const overlayComposeFile = defaultOpenMatesCloudComposeFile(overlayPath);
|
|
29795
|
-
if (deploymentMode === "official_cloud" && (!
|
|
29901
|
+
if (deploymentMode === "official_cloud" && (!existsSync10(overlayPath) || !existsSync10(overlayComposeFile))) {
|
|
29796
29902
|
throw new Error(`OpenMatesCloud overlay path is required for official-cloud mode: ${overlayPath}`);
|
|
29797
29903
|
}
|
|
29798
29904
|
const runtimePlan = planServerRuntime({
|
|
@@ -29802,7 +29908,7 @@ async function serverRegister(flags) {
|
|
|
29802
29908
|
includeWebapp: deploymentMode !== "official_cloud"
|
|
29803
29909
|
});
|
|
29804
29910
|
const defaultServices = hasServiceFilter(flags) ? selectedComposeServices(role, flags) : runtimePlan.defaultServices;
|
|
29805
|
-
const cliUrls = deriveSelfHostCliUrls(
|
|
29911
|
+
const cliUrls = deriveSelfHostCliUrls(readFileSync8(envPath, "utf-8"));
|
|
29806
29912
|
const config = {
|
|
29807
29913
|
installPath,
|
|
29808
29914
|
installedAt: existing?.installPath === installPath ? existing.installedAt : Date.now(),
|
|
@@ -29844,27 +29950,27 @@ async function serverInstall(flags) {
|
|
|
29844
29950
|
withAlerts: flags["with-alerts"] === true,
|
|
29845
29951
|
includeWebapp: deploymentMode !== "official_cloud"
|
|
29846
29952
|
});
|
|
29847
|
-
if (
|
|
29953
|
+
if (existsSync10(join8(installPath, SOURCE_COMPOSE_FILE)) || Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync10(join8(installPath, composeFile)))) {
|
|
29848
29954
|
console.error(`OpenMates already exists at ${installPath}.`);
|
|
29849
29955
|
console.error("Use 'openmates server update' to update, or choose a different --path.");
|
|
29850
29956
|
process.exit(1);
|
|
29851
29957
|
}
|
|
29852
29958
|
if (!fromSource) {
|
|
29853
29959
|
requireDocker();
|
|
29854
|
-
|
|
29960
|
+
mkdirSync7(installPath, { recursive: true });
|
|
29855
29961
|
if (typeof flags["env-path"] === "string") {
|
|
29856
29962
|
const envSource = resolve6(flags["env-path"]);
|
|
29857
|
-
if (!
|
|
29963
|
+
if (!existsSync10(envSource)) {
|
|
29858
29964
|
throw new Error(`Env file not found: ${envSource}`);
|
|
29859
29965
|
}
|
|
29860
|
-
copyFileSync2(envSource,
|
|
29966
|
+
copyFileSync2(envSource, join8(installPath, ".env"));
|
|
29861
29967
|
console.error(`Copied ${envSource} to ${installPath}/.env`);
|
|
29862
29968
|
}
|
|
29863
29969
|
const imageTag = typeof flags["image-tag"] === "string" ? flags["image-tag"] : getDefaultImageTag();
|
|
29864
29970
|
console.error(`Preparing OpenMates image-mode install at ${installPath}...`);
|
|
29865
29971
|
await writeImageModeRuntimeFiles(installPath, imageTag, role);
|
|
29866
29972
|
const overlay2 = writeDeploymentModeEnv(installPath, deploymentMode, requestedOverlayPath);
|
|
29867
|
-
const cliUrls2 = deriveSelfHostCliUrls(
|
|
29973
|
+
const cliUrls2 = deriveSelfHostCliUrls(readFileSync8(join8(installPath, ".env"), "utf-8"));
|
|
29868
29974
|
try {
|
|
29869
29975
|
exec("docker network create openmates", installPath);
|
|
29870
29976
|
} catch {
|
|
@@ -29891,7 +29997,7 @@ async function serverInstall(flags) {
|
|
|
29891
29997
|
if (flags.json === true) {
|
|
29892
29998
|
printJson({ command: "install", status: "success", path: installPath, mode: "image", imageTag });
|
|
29893
29999
|
} else {
|
|
29894
|
-
const firstInvite = getEnvVar(
|
|
30000
|
+
const firstInvite = getEnvVar(readFileSync8(join8(installPath, ".env"), "utf-8"), "SELF_HOST_FIRST_INVITE_CODE");
|
|
29895
30001
|
console.log(`
|
|
29896
30002
|
OpenMates installed at ${installPath}`);
|
|
29897
30003
|
console.log(`Mode: image (${DEFAULT_IMAGE_REGISTRY}, tag ${imageTag})`);
|
|
@@ -29929,10 +30035,10 @@ CLI default API: ${cliUrls2.apiUrl}`);
|
|
|
29929
30035
|
}
|
|
29930
30036
|
if (typeof flags["env-path"] === "string") {
|
|
29931
30037
|
const envSource = resolve6(flags["env-path"]);
|
|
29932
|
-
if (!
|
|
30038
|
+
if (!existsSync10(envSource)) {
|
|
29933
30039
|
throw new Error(`Env file not found: ${envSource}`);
|
|
29934
30040
|
}
|
|
29935
|
-
copyFileSync2(envSource,
|
|
30041
|
+
copyFileSync2(envSource, join8(installPath, ".env"));
|
|
29936
30042
|
console.error(`Copied ${envSource} to ${installPath}/.env`);
|
|
29937
30043
|
}
|
|
29938
30044
|
const translationResult = ensureSourceInstallTranslations(installPath, sourcePath);
|
|
@@ -29951,9 +30057,9 @@ CLI default API: ${cliUrls2.apiUrl}`);
|
|
|
29951
30057
|
console.error("Setup script failed. Check the output above for details.");
|
|
29952
30058
|
process.exit(setupCode);
|
|
29953
30059
|
}
|
|
29954
|
-
const envPath =
|
|
30060
|
+
const envPath = join8(installPath, ".env");
|
|
29955
30061
|
const overlay = writeDeploymentModeEnv(installPath, deploymentMode, requestedOverlayPath);
|
|
29956
|
-
const cliUrls = deriveSelfHostCliUrls(
|
|
30062
|
+
const cliUrls = deriveSelfHostCliUrls(existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "");
|
|
29957
30063
|
saveServerConfig({
|
|
29958
30064
|
installPath,
|
|
29959
30065
|
installedAt: Date.now(),
|
|
@@ -29994,15 +30100,15 @@ async function installContinuousUpdateService(flags) {
|
|
|
29994
30100
|
const channel = typeof flags.channel === "string" ? flags.channel : config?.imageChannel ?? "main";
|
|
29995
30101
|
const window = typeof flags.window === "string" ? flags.window : "02:00-04:00 UTC";
|
|
29996
30102
|
const plan = planContinuousUpdateService({ role, channel, window });
|
|
29997
|
-
const servicePath =
|
|
29998
|
-
const timerPath =
|
|
30103
|
+
const servicePath = join8("/etc", "systemd", "system", plan.serviceName);
|
|
30104
|
+
const timerPath = join8("/etc", "systemd", "system", plan.timerName);
|
|
29999
30105
|
if (flags["dry-run"] === true || flags.json === true) {
|
|
30000
30106
|
printJson({ command: "update install-service", status: "planned", role, servicePath, timerPath, unit: plan.unit, timer: plan.timer });
|
|
30001
30107
|
return;
|
|
30002
30108
|
}
|
|
30003
30109
|
try {
|
|
30004
|
-
|
|
30005
|
-
|
|
30110
|
+
writeFileSync6(servicePath, plan.unit, { mode: 420 });
|
|
30111
|
+
writeFileSync6(timerPath, plan.timer, { mode: 420 });
|
|
30006
30112
|
execSync("systemctl daemon-reload", { stdio: "pipe" });
|
|
30007
30113
|
execSync(`systemctl enable --now ${shellQuote(plan.timerName)}`, { stdio: "pipe" });
|
|
30008
30114
|
} catch (error) {
|
|
@@ -30013,13 +30119,13 @@ async function installContinuousUpdateService(flags) {
|
|
|
30013
30119
|
console.log(`Installed ${plan.timerName}.`);
|
|
30014
30120
|
}
|
|
30015
30121
|
function runRuntimeVerification(installPath, role, config) {
|
|
30016
|
-
const envText =
|
|
30122
|
+
const envText = existsSync10(join8(installPath, ".env")) ? readFileSync8(join8(installPath, ".env"), "utf-8") : "";
|
|
30017
30123
|
const mode = resolveRuntimeDeploymentMode({
|
|
30018
30124
|
envText,
|
|
30019
|
-
overlayExists:
|
|
30125
|
+
overlayExists: existsSync10(config?.openMatesCloudOverlayPath ?? defaultOpenMatesCloudOverlayPath(installPath))
|
|
30020
30126
|
});
|
|
30021
|
-
const backupPath =
|
|
30022
|
-
const plan = planRuntimeVerification({ role, deploymentMode: mode.effectiveMode, hasVerifiedBackup:
|
|
30127
|
+
const backupPath = join8(roleBackupDir(installPath, role), `latest-pre-update-${role}.tar.gz`);
|
|
30128
|
+
const plan = planRuntimeVerification({ role, deploymentMode: mode.effectiveMode, hasVerifiedBackup: existsSync10(backupPath) });
|
|
30023
30129
|
const verifierService = { core: "api", upload: "app-uploads", preview: "preview" }[role];
|
|
30024
30130
|
const installMode = getInstallMode(installPath, config);
|
|
30025
30131
|
const withOverrides = config?.composeProfile === "full";
|
|
@@ -30226,10 +30332,10 @@ async function installRuntimeMonitoringServices(installPath, role) {
|
|
|
30226
30332
|
[operationalPlan.watchdogTimerName, operationalPlan.watchdogTimer]
|
|
30227
30333
|
] : []
|
|
30228
30334
|
].filter((entry) => Boolean(entry[0] && entry[1]));
|
|
30229
|
-
const reportStatePath =
|
|
30230
|
-
const reportMetricsDir =
|
|
30231
|
-
const startedMetricPath =
|
|
30232
|
-
const successMetricPath =
|
|
30335
|
+
const reportStatePath = join8(installPath, ".openmates", "runtime-health", `${role}-operational-report.json`);
|
|
30336
|
+
const reportMetricsDir = join8(installPath, ".openmates", "runtime-health", "metrics");
|
|
30337
|
+
const startedMetricPath = join8(reportMetricsDir, "operational-report-started.prom");
|
|
30338
|
+
const successMetricPath = join8(reportMetricsDir, "operational-report.prom");
|
|
30233
30339
|
const operationalUnitNames = [
|
|
30234
30340
|
operationalPlan.digestServiceName,
|
|
30235
30341
|
operationalPlan.digestTimerName,
|
|
@@ -30237,18 +30343,18 @@ async function installRuntimeMonitoringServices(installPath, role) {
|
|
|
30237
30343
|
operationalPlan.watchdogTimerName
|
|
30238
30344
|
];
|
|
30239
30345
|
if (operationalPlan.scheduleEnabled) {
|
|
30240
|
-
|
|
30241
|
-
|
|
30242
|
-
if (!
|
|
30243
|
-
|
|
30346
|
+
mkdirSync7(dirname6(reportStatePath), { recursive: true, mode: 448 });
|
|
30347
|
+
mkdirSync7(reportMetricsDir, { recursive: true, mode: 448 });
|
|
30348
|
+
if (!existsSync10(reportStatePath)) {
|
|
30349
|
+
writeFileSync6(
|
|
30244
30350
|
reportStatePath,
|
|
30245
30351
|
`${JSON.stringify({ incidentOpen: false, monitoringStartedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
30246
30352
|
`,
|
|
30247
30353
|
{ mode: 384 }
|
|
30248
30354
|
);
|
|
30249
30355
|
}
|
|
30250
|
-
if (!
|
|
30251
|
-
|
|
30356
|
+
if (!existsSync10(startedMetricPath)) {
|
|
30357
|
+
writeFileSync6(
|
|
30252
30358
|
startedMetricPath,
|
|
30253
30359
|
`operational_report_monitoring_started_timestamp_seconds{environment="${operationalPlan.environment}"} ${Math.floor(Date.now() / 1e3)}
|
|
30254
30360
|
`,
|
|
@@ -30256,17 +30362,17 @@ async function installRuntimeMonitoringServices(installPath, role) {
|
|
|
30256
30362
|
);
|
|
30257
30363
|
}
|
|
30258
30364
|
} else {
|
|
30259
|
-
const installedOperationalUnits = operationalUnitNames.filter((name) =>
|
|
30365
|
+
const installedOperationalUnits = operationalUnitNames.filter((name) => existsSync10(join8("/etc", "systemd", "system", name)));
|
|
30260
30366
|
const installedOperationalTimers = installedOperationalUnits.filter((name) => name.endsWith(".timer"));
|
|
30261
30367
|
if (installedOperationalTimers.length) {
|
|
30262
30368
|
execFileSync4("systemctl", ["disable", "--now", ...installedOperationalTimers], { stdio: "pipe" });
|
|
30263
30369
|
}
|
|
30264
|
-
for (const name of installedOperationalUnits)
|
|
30370
|
+
for (const name of installedOperationalUnits) rmSync7(join8("/etc", "systemd", "system", name));
|
|
30265
30371
|
for (const path2 of [reportStatePath, startedMetricPath, successMetricPath]) {
|
|
30266
|
-
if (
|
|
30372
|
+
if (existsSync10(path2)) rmSync7(path2);
|
|
30267
30373
|
}
|
|
30268
30374
|
}
|
|
30269
|
-
for (const [name, content] of files)
|
|
30375
|
+
for (const [name, content] of files) writeFileSync6(join8("/etc", "systemd", "system", name), content, { mode: 420 });
|
|
30270
30376
|
execSync("systemctl daemon-reload", { stdio: "pipe" });
|
|
30271
30377
|
const timers = files.filter(([name]) => name.endsWith(".timer")).map(([name]) => name);
|
|
30272
30378
|
execSync(`systemctl enable --now ${timers.map(shellQuote).join(" ")}`, { stdio: "pipe" });
|
|
@@ -30288,10 +30394,10 @@ async function serverUpdate(client, rest, flags) {
|
|
|
30288
30394
|
const filePath = serverUpdateStatusFile(installPath2, role2);
|
|
30289
30395
|
const status = readServerUpdateStatus(installPath2, role2);
|
|
30290
30396
|
if (flags.json === true) {
|
|
30291
|
-
printJson(
|
|
30397
|
+
printJson(existsSync10(filePath) ? status : { role: role2, status: "unknown" });
|
|
30292
30398
|
return;
|
|
30293
30399
|
}
|
|
30294
|
-
if (!
|
|
30400
|
+
if (!existsSync10(filePath)) {
|
|
30295
30401
|
console.log(`No update status recorded for ${role2}.`);
|
|
30296
30402
|
return;
|
|
30297
30403
|
}
|
|
@@ -30477,7 +30583,7 @@ async function serverUpdate(client, rest, flags) {
|
|
|
30477
30583
|
try {
|
|
30478
30584
|
await autoInstallRuntimeMonitoringServices(installPath, role);
|
|
30479
30585
|
} catch (error) {
|
|
30480
|
-
const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${
|
|
30586
|
+
const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${join8(roleBackupDir(installPath, role), safetyPlan.backupName)}` : null;
|
|
30481
30587
|
writeServerUpdateStatus(installPath, role, {
|
|
30482
30588
|
status: "degraded",
|
|
30483
30589
|
targetImageTag: target.tag,
|
|
@@ -30803,11 +30909,11 @@ async function serverMonitoring(rest, flags) {
|
|
|
30803
30909
|
const state2 = await readOperationalReportState(installPath, role);
|
|
30804
30910
|
const nextState2 = accepted ? { ...state2, incidentOpen: false, incidentOpenedAt: void 0, lastAcceptedReportAt: (/* @__PURE__ */ new Date()).toISOString() } : state2;
|
|
30805
30911
|
await writeOperationalReportState(installPath, role, nextState2);
|
|
30806
|
-
const metricsDir =
|
|
30807
|
-
|
|
30912
|
+
const metricsDir = join8(installPath, ".openmates", "runtime-health", "metrics");
|
|
30913
|
+
mkdirSync7(metricsDir, { recursive: true, mode: 448 });
|
|
30808
30914
|
if (accepted) {
|
|
30809
|
-
|
|
30810
|
-
|
|
30915
|
+
writeFileSync6(
|
|
30916
|
+
join8(metricsDir, "operational-report.prom"),
|
|
30811
30917
|
`operational_report_last_success_timestamp_seconds{environment="${output2.receipts[0]?.environment ?? environment}"} ${Math.floor(Date.now() / 1e3)}
|
|
30812
30918
|
`,
|
|
30813
30919
|
{ mode: 384 }
|
|
@@ -30825,11 +30931,11 @@ async function serverMonitoring(rest, flags) {
|
|
|
30825
30931
|
destinationSource: receipt.destination_source,
|
|
30826
30932
|
fallbackUsed: receipt.fallback_used
|
|
30827
30933
|
}));
|
|
30828
|
-
const receiptDir =
|
|
30829
|
-
|
|
30934
|
+
const receiptDir = join8(installPath, ".openmates", "runtime-health", "receipts");
|
|
30935
|
+
mkdirSync7(receiptDir, { recursive: true, mode: 448 });
|
|
30830
30936
|
const receiptMonth = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
|
|
30831
30937
|
for (const receipt of redactedReceipts) {
|
|
30832
|
-
appendFileSync(
|
|
30938
|
+
appendFileSync(join8(receiptDir, `${receiptMonth}.jsonl`), `${JSON.stringify(receipt)}
|
|
30833
30939
|
`, { mode: 384 });
|
|
30834
30940
|
}
|
|
30835
30941
|
const displayed = { command: "monitoring digest", reportId: output2.report_id, reportSha256: output2.report_sha256, deliveryState: output2.delivery_state, receipts: redactedReceipts };
|
|
@@ -31033,14 +31139,14 @@ async function serverFeatures(rest, flags) {
|
|
|
31033
31139
|
const installPath = resolveServerPath(flags);
|
|
31034
31140
|
const installMode = getInstallMode(installPath);
|
|
31035
31141
|
if (installMode === "image") ensureImageRuntimeConfig(installPath);
|
|
31036
|
-
const configPath = installMode === "image" ? imageBackendConfigPath(installPath) :
|
|
31037
|
-
if (!
|
|
31142
|
+
const configPath = installMode === "image" ? imageBackendConfigPath(installPath) : join8(installPath, BACKEND_CONFIG_FILE);
|
|
31143
|
+
if (!existsSync10(configPath)) {
|
|
31038
31144
|
throw new Error(`Backend config not found at ${configPath}. Run 'openmates server install' first or pass --path <dir>.`);
|
|
31039
31145
|
}
|
|
31040
|
-
const content =
|
|
31146
|
+
const content = readFileSync8(configPath, "utf-8");
|
|
31041
31147
|
const overrides = parseFeatureOverrides(content);
|
|
31042
31148
|
const writeOverrides = (nextOverrides) => {
|
|
31043
|
-
|
|
31149
|
+
writeFileSync6(configPath, updateFeatureOverridesContent(content, nextOverrides));
|
|
31044
31150
|
console.log(`Updated ${configPath}`);
|
|
31045
31151
|
console.log("Restart the server for feature changes to take effect: openmates server restart");
|
|
31046
31152
|
};
|
|
@@ -31273,7 +31379,7 @@ async function serverBackup(rest, flags) {
|
|
|
31273
31379
|
const role = getServerRole(flags, config);
|
|
31274
31380
|
if (action === "list") {
|
|
31275
31381
|
const dir = roleBackupDir(installPath, role);
|
|
31276
|
-
const files =
|
|
31382
|
+
const files = existsSync10(dir) ? readdirSync5(dir).filter((item) => item.endsWith(".tar.gz")).sort() : [];
|
|
31277
31383
|
if (flags.json === true) {
|
|
31278
31384
|
printJson({ role, backupDir: dir, files });
|
|
31279
31385
|
return;
|
|
@@ -31283,7 +31389,7 @@ async function serverBackup(rest, flags) {
|
|
|
31283
31389
|
console.log(" none");
|
|
31284
31390
|
return;
|
|
31285
31391
|
}
|
|
31286
|
-
for (const file of files) console.log(` ${
|
|
31392
|
+
for (const file of files) console.log(` ${join8(dir, file)}`);
|
|
31287
31393
|
return;
|
|
31288
31394
|
}
|
|
31289
31395
|
requireDocker();
|
|
@@ -31368,7 +31474,7 @@ async function serverUninstall(flags) {
|
|
|
31368
31474
|
} catch {
|
|
31369
31475
|
}
|
|
31370
31476
|
try {
|
|
31371
|
-
|
|
31477
|
+
rmSync7(installPath, { recursive: true, force: true });
|
|
31372
31478
|
console.error(` Removed installation directory: ${installPath}`);
|
|
31373
31479
|
} catch (e) {
|
|
31374
31480
|
console.error(` Could not remove ${installPath}: ${e instanceof Error ? e.message : e}`);
|
|
@@ -31443,7 +31549,7 @@ async function serverCaddy(rest, flags) {
|
|
|
31443
31549
|
const role = getServerRole(flags, config);
|
|
31444
31550
|
const appliedPath = typeof flags.config === "string" ? flags.config : "/etc/caddy/Caddyfile";
|
|
31445
31551
|
const templatePath = packagedCaddyTemplatePath(role);
|
|
31446
|
-
if (!
|
|
31552
|
+
if (!existsSync10(templatePath)) throw new Error(`Packaged Caddy template not found: ${templatePath}`);
|
|
31447
31553
|
const plan = planCaddyCommand({ role, action, appliedPath });
|
|
31448
31554
|
const payload = {
|
|
31449
31555
|
...plan,
|
|
@@ -31462,7 +31568,7 @@ async function serverCaddy(rest, flags) {
|
|
|
31462
31568
|
return;
|
|
31463
31569
|
}
|
|
31464
31570
|
if (action === "diff") {
|
|
31465
|
-
if (!
|
|
31571
|
+
if (!existsSync10(appliedPath)) throw new Error(`Applied Caddyfile not found: ${appliedPath}`);
|
|
31466
31572
|
try {
|
|
31467
31573
|
execSync(`diff -u ${shellQuote(appliedPath)} ${shellQuote(templatePath)}`, { stdio: "inherit" });
|
|
31468
31574
|
} catch (error) {
|
|
@@ -31483,9 +31589,9 @@ WARNING: This will replace ${appliedPath} with the packaged ${role} Caddyfile.`)
|
|
|
31483
31589
|
}
|
|
31484
31590
|
}
|
|
31485
31591
|
const backupPath = `${appliedPath}.openmates-backup-${nowStamp()}`;
|
|
31486
|
-
const hadExistingCaddyfile =
|
|
31592
|
+
const hadExistingCaddyfile = existsSync10(appliedPath);
|
|
31487
31593
|
try {
|
|
31488
|
-
|
|
31594
|
+
mkdirSync7(dirname6(appliedPath), { recursive: true });
|
|
31489
31595
|
if (hadExistingCaddyfile) copyFileSync2(appliedPath, backupPath);
|
|
31490
31596
|
copyFileSync2(templatePath, appliedPath);
|
|
31491
31597
|
execSync("systemctl reload caddy", { stdio: "inherit" });
|
|
@@ -50610,6 +50716,12 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
50610
50716
|
}
|
|
50611
50717
|
},
|
|
50612
50718
|
calendar: {
|
|
50719
|
+
list_calendars: {
|
|
50720
|
+
text: "List Calendars",
|
|
50721
|
+
description: {
|
|
50722
|
+
text: "List calendars, timezones, and access roles in a connected account."
|
|
50723
|
+
}
|
|
50724
|
+
},
|
|
50613
50725
|
get_events: {
|
|
50614
50726
|
text: "Search",
|
|
50615
50727
|
description: {
|
|
@@ -56799,6 +56911,9 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
56799
56911
|
generating_title: {
|
|
56800
56912
|
text: "Processing..."
|
|
56801
56913
|
},
|
|
56914
|
+
selecting_mate_and_model: {
|
|
56915
|
+
text: "Selecting mate & AI model..."
|
|
56916
|
+
},
|
|
56802
56917
|
selecting_mate: {
|
|
56803
56918
|
text: "Selecting optimal mate..."
|
|
56804
56919
|
},
|
|
@@ -70616,10 +70731,10 @@ function buildAssistantFeedbackDecision(rating) {
|
|
|
70616
70731
|
}
|
|
70617
70732
|
|
|
70618
70733
|
// src/benchmark.ts
|
|
70619
|
-
import { createHash as
|
|
70620
|
-
import { existsSync as
|
|
70734
|
+
import { createHash as createHash14, randomUUID as randomUUID12 } from "crypto";
|
|
70735
|
+
import { existsSync as existsSync11, mkdtempSync as mkdtempSync2, readFileSync as readFileSync9, readdirSync as readdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
70621
70736
|
import { tmpdir } from "os";
|
|
70622
|
-
import { dirname as dirname7, join as
|
|
70737
|
+
import { dirname as dirname7, join as join9, resolve as resolve8 } from "path";
|
|
70623
70738
|
import { fileURLToPath } from "url";
|
|
70624
70739
|
var DEFAULT_JUDGE_MODEL = "google/gemini-3-flash-preview";
|
|
70625
70740
|
var DEFAULT_PROMPT_BUDGET_MODEL = "google/gemini-3.5-flash";
|
|
@@ -71505,7 +71620,7 @@ async function buildPromptWithAttachments(client, benchmarkCase, model, imagePat
|
|
|
71505
71620
|
${attachment.messageSuffix}`, embeds: attachment.embeds };
|
|
71506
71621
|
}
|
|
71507
71622
|
async function prepareImageAttachment(client, imagePath) {
|
|
71508
|
-
if (!
|
|
71623
|
+
if (!existsSync11(imagePath)) throw new Error(`Benchmark image not found: ${imagePath}`);
|
|
71509
71624
|
const processed = processFiles([imagePath], null);
|
|
71510
71625
|
if (processed.blocked.length > 0 || processed.errors.length > 0 || processed.embeds.length === 0) {
|
|
71511
71626
|
const reason = [...processed.blocked, ...processed.errors].map((entry) => entry.error).join("; ") || "no image embed produced";
|
|
@@ -71785,10 +71900,10 @@ function loadProviderPricing() {
|
|
|
71785
71900
|
const providersDir = findProvidersDir();
|
|
71786
71901
|
const pricing = /* @__PURE__ */ new Map();
|
|
71787
71902
|
if (!providersDir) return pricing;
|
|
71788
|
-
for (const fileName of
|
|
71903
|
+
for (const fileName of readdirSync6(providersDir)) {
|
|
71789
71904
|
if (!fileName.endsWith(".yml")) continue;
|
|
71790
|
-
const filePath =
|
|
71791
|
-
const text =
|
|
71905
|
+
const filePath = join9(providersDir, fileName);
|
|
71906
|
+
const text = readFileSync9(filePath, "utf-8");
|
|
71792
71907
|
const provider = parseProviderId(text) ?? fileName.replace(/\.yml$/, "");
|
|
71793
71908
|
for (const modelPricing of parseModelPricing(text, provider)) {
|
|
71794
71909
|
pricing.set(`${modelPricing.provider}/${modelPricing.modelId}`, modelPricing);
|
|
@@ -71842,10 +71957,10 @@ function findProvidersDir() {
|
|
|
71842
71957
|
const currentFile = fileURLToPath(import.meta.url);
|
|
71843
71958
|
let current = dirname7(currentFile);
|
|
71844
71959
|
for (let index = 0; index < 8; index += 1) {
|
|
71845
|
-
const candidate =
|
|
71846
|
-
if (
|
|
71847
|
-
const parentCandidate =
|
|
71848
|
-
if (
|
|
71960
|
+
const candidate = join9(current, "backend", "providers");
|
|
71961
|
+
if (existsSync11(candidate)) return candidate;
|
|
71962
|
+
const parentCandidate = join9(current, "..", "..", "backend", "providers");
|
|
71963
|
+
if (existsSync11(parentCandidate)) return resolve8(parentCandidate);
|
|
71849
71964
|
const next = dirname7(current);
|
|
71850
71965
|
if (next === current) break;
|
|
71851
71966
|
current = next;
|
|
@@ -72050,21 +72165,21 @@ function hashPromptBudgetCorpus(cases) {
|
|
|
72050
72165
|
return stableHash(cases.map((benchmarkCase) => ({ id: benchmarkCase.id, turns: benchmarkCase.turns })));
|
|
72051
72166
|
}
|
|
72052
72167
|
function stableHash(value) {
|
|
72053
|
-
return
|
|
72168
|
+
return createHash14("sha256").update(JSON.stringify(value)).digest("hex");
|
|
72054
72169
|
}
|
|
72055
72170
|
function defaultImageFixturePath() {
|
|
72056
|
-
const fixtureDir =
|
|
72057
|
-
const fixturePath =
|
|
72058
|
-
if (
|
|
72059
|
-
const tempDir = mkdtempSync2(
|
|
72060
|
-
const tempPath =
|
|
72061
|
-
|
|
72171
|
+
const fixtureDir = join9(dirname7(fileURLToPath(import.meta.url)), "..", "fixtures");
|
|
72172
|
+
const fixturePath = join9(fixtureDir, "brandenburger-tor.png");
|
|
72173
|
+
if (existsSync11(fixturePath)) return fixturePath;
|
|
72174
|
+
const tempDir = mkdtempSync2(join9(tmpdir(), "openmates-benchmark-"));
|
|
72175
|
+
const tempPath = join9(tempDir, "brandenburger-tor.svg");
|
|
72176
|
+
writeFileSync7(tempPath, FIXTURE_IMAGE_SVG, "utf-8");
|
|
72062
72177
|
return tempPath;
|
|
72063
72178
|
}
|
|
72064
72179
|
function writeBenchmarkResult(result, flags, output) {
|
|
72065
72180
|
const json = `${JSON.stringify(result, null, 2)}
|
|
72066
72181
|
`;
|
|
72067
|
-
if (output)
|
|
72182
|
+
if (output) writeFileSync7(output, json, "utf-8");
|
|
72068
72183
|
if (flags.json === true || output) {
|
|
72069
72184
|
process.stdout.write(json);
|
|
72070
72185
|
return;
|
|
@@ -72088,7 +72203,7 @@ function writeBenchmarkResult(result, flags, output) {
|
|
|
72088
72203
|
function writePromptBudgetResult(result, flags, output) {
|
|
72089
72204
|
const json = `${JSON.stringify(result, null, 2)}
|
|
72090
72205
|
`;
|
|
72091
|
-
if (output)
|
|
72206
|
+
if (output) writeFileSync7(output, json, "utf-8");
|
|
72092
72207
|
if (flags.json === true || output) {
|
|
72093
72208
|
process.stdout.write(json);
|
|
72094
72209
|
return;
|
|
@@ -72114,7 +72229,7 @@ function writePromptBudgetResult(result, flags, output) {
|
|
|
72114
72229
|
function writePromptBudgetComparison(result, flags, output) {
|
|
72115
72230
|
const json = `${JSON.stringify(result, null, 2)}
|
|
72116
72231
|
`;
|
|
72117
|
-
if (output)
|
|
72232
|
+
if (output) writeFileSync7(output, json, "utf-8");
|
|
72118
72233
|
if (flags.json === true || output) {
|
|
72119
72234
|
process.stdout.write(json);
|
|
72120
72235
|
return;
|
|
@@ -72164,7 +72279,7 @@ function comparePromptBudgetArtifacts(flags) {
|
|
|
72164
72279
|
};
|
|
72165
72280
|
}
|
|
72166
72281
|
function readPromptBudgetResult(path2) {
|
|
72167
|
-
const parsed = JSON.parse(
|
|
72282
|
+
const parsed = JSON.parse(readFileSync9(path2, "utf-8"));
|
|
72168
72283
|
if (!parsed || typeof parsed !== "object") throw new Error(`Invalid prompt-budget artifact: ${path2}`);
|
|
72169
72284
|
return parsed;
|
|
72170
72285
|
}
|
|
@@ -73565,19 +73680,19 @@ import {
|
|
|
73565
73680
|
chmodSync as chmodSync6,
|
|
73566
73681
|
closeSync as closeSync3,
|
|
73567
73682
|
constants,
|
|
73568
|
-
existsSync as
|
|
73683
|
+
existsSync as existsSync12,
|
|
73569
73684
|
fstatSync,
|
|
73570
|
-
mkdirSync as
|
|
73685
|
+
mkdirSync as mkdirSync8,
|
|
73571
73686
|
openSync as openSync3,
|
|
73572
|
-
readFileSync as
|
|
73573
|
-
readdirSync as
|
|
73687
|
+
readFileSync as readFileSync10,
|
|
73688
|
+
readdirSync as readdirSync7,
|
|
73574
73689
|
readSync as readSync2,
|
|
73575
73690
|
realpathSync,
|
|
73576
73691
|
statSync as statSync2,
|
|
73577
|
-
writeFileSync as
|
|
73692
|
+
writeFileSync as writeFileSync8
|
|
73578
73693
|
} from "fs";
|
|
73579
73694
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
73580
|
-
import { join as
|
|
73695
|
+
import { join as join10, resolve as resolve9, relative as relative2 } from "path";
|
|
73581
73696
|
|
|
73582
73697
|
// src/projectFileRisk.ts
|
|
73583
73698
|
var SECRET_OR_ENVIRONMENT_PATTERNS = [
|
|
@@ -73700,7 +73815,7 @@ function resolveRemoteAccessRoots(pathFlag, cwd = process.cwd()) {
|
|
|
73700
73815
|
const roots = [];
|
|
73701
73816
|
for (const value of requested) {
|
|
73702
73817
|
const candidate = resolve9(cwd, value);
|
|
73703
|
-
if (!
|
|
73818
|
+
if (!existsSync12(candidate) || !statSync2(candidate).isDirectory()) {
|
|
73704
73819
|
throw new Error(`Remote source path does not exist or is not a directory: ${candidate}`);
|
|
73705
73820
|
}
|
|
73706
73821
|
const canonical = realpathSync(candidate);
|
|
@@ -73714,7 +73829,7 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
73714
73829
|
const visit = (directory, approvedRoot) => {
|
|
73715
73830
|
let entries;
|
|
73716
73831
|
try {
|
|
73717
|
-
entries =
|
|
73832
|
+
entries = readdirSync7(directory, { withFileTypes: true });
|
|
73718
73833
|
} catch (error) {
|
|
73719
73834
|
const code = error.code;
|
|
73720
73835
|
if (code === "EACCES" || code === "EPERM") {
|
|
@@ -73728,7 +73843,7 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
73728
73843
|
}
|
|
73729
73844
|
for (const entry of entries) {
|
|
73730
73845
|
if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
|
|
73731
|
-
visit(
|
|
73846
|
+
visit(join10(directory, entry.name), approvedRoot);
|
|
73732
73847
|
}
|
|
73733
73848
|
};
|
|
73734
73849
|
for (const root of roots) visit(root, root);
|
|
@@ -73738,8 +73853,8 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
73738
73853
|
};
|
|
73739
73854
|
}
|
|
73740
73855
|
function remoteAccessSourceType(rootPath) {
|
|
73741
|
-
const gitMarker =
|
|
73742
|
-
return
|
|
73856
|
+
const gitMarker = join10(rootPath, ".git");
|
|
73857
|
+
return existsSync12(gitMarker) ? "local_git_repository" : "local_folder";
|
|
73743
73858
|
}
|
|
73744
73859
|
function listRemoteAccessDirectory(options) {
|
|
73745
73860
|
const root = realpathSync(options.sourceRoot);
|
|
@@ -73752,9 +73867,9 @@ function listRemoteAccessDirectory(options) {
|
|
|
73752
73867
|
const entries = [];
|
|
73753
73868
|
let omitted = 0;
|
|
73754
73869
|
let excluded = 0;
|
|
73755
|
-
for (const entry of
|
|
73756
|
-
const entryPath = relative2(root,
|
|
73757
|
-
if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, entryPath) || classifyProjectFileRisk(entryPath, options.userProtectedPatterns ?? []).isHighRisk || entry.isFile() && isBinaryFile(
|
|
73870
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
73871
|
+
const entryPath = relative2(root, join10(directory, entry.name)).replace(/\\/g, "/");
|
|
73872
|
+
if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, entryPath) || classifyProjectFileRisk(entryPath, options.userProtectedPatterns ?? []).isHighRisk || entry.isFile() && isBinaryFile(join10(directory, entry.name))) {
|
|
73758
73873
|
excluded += 1;
|
|
73759
73874
|
continue;
|
|
73760
73875
|
}
|
|
@@ -73982,7 +74097,7 @@ function remoteAccessOperationErrorCode(error) {
|
|
|
73982
74097
|
return "operation_failed";
|
|
73983
74098
|
}
|
|
73984
74099
|
function isGitIgnoredPath(sourceRoot, relativePath) {
|
|
73985
|
-
if (!
|
|
74100
|
+
if (!existsSync12(join10(sourceRoot, ".git"))) return false;
|
|
73986
74101
|
const result = spawnSync2("git", ["check-ignore", "--quiet", "--", relativePath], {
|
|
73987
74102
|
cwd: sourceRoot,
|
|
73988
74103
|
stdio: "ignore"
|
|
@@ -74012,13 +74127,13 @@ function waitForAbort2(signal, timeoutMs) {
|
|
|
74012
74127
|
}
|
|
74013
74128
|
function resolveRemoteCachePath(sourceId, homeDirectory = homedir7()) {
|
|
74014
74129
|
assertSafeSourceId(sourceId);
|
|
74015
|
-
return
|
|
74130
|
+
return join10(homeDirectory, ".openmates", "remote-cache", sourceId);
|
|
74016
74131
|
}
|
|
74017
74132
|
function startRemoteAccessSource(input) {
|
|
74018
74133
|
assertSafeSourceId(input.sourceId);
|
|
74019
74134
|
const homeDirectory = input.homeDirectory ?? homedir7();
|
|
74020
74135
|
const requestedRootPath = resolve9(input.rootPath);
|
|
74021
|
-
if (!
|
|
74136
|
+
if (!existsSync12(requestedRootPath) || !statSync2(requestedRootPath).isDirectory()) {
|
|
74022
74137
|
throw new Error(`Remote source path does not exist or is not a directory: ${requestedRootPath}`);
|
|
74023
74138
|
}
|
|
74024
74139
|
const rootPath = realpathSync(requestedRootPath);
|
|
@@ -74036,14 +74151,14 @@ function startRemoteAccessSource(input) {
|
|
|
74036
74151
|
};
|
|
74037
74152
|
const sources = listRemoteAccessSources(homeDirectory).filter((entry) => entry.sourceId !== input.sourceId);
|
|
74038
74153
|
saveRemoteAccessSources([...sources, source], homeDirectory);
|
|
74039
|
-
|
|
74154
|
+
mkdirSync8(source.cachePath, { recursive: true, mode: 448 });
|
|
74040
74155
|
return source;
|
|
74041
74156
|
}
|
|
74042
74157
|
function listRemoteAccessSources(homeDirectory = homedir7()) {
|
|
74043
74158
|
const filePath = remoteAccessStorePath(homeDirectory);
|
|
74044
|
-
if (!
|
|
74159
|
+
if (!existsSync12(filePath)) return [];
|
|
74045
74160
|
try {
|
|
74046
|
-
const parsed = JSON.parse(
|
|
74161
|
+
const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
74047
74162
|
if (!Array.isArray(parsed.sources)) {
|
|
74048
74163
|
throw new Error("Remote source store is missing the sources array");
|
|
74049
74164
|
}
|
|
@@ -74100,13 +74215,13 @@ function searchRemoteSourceWithoutRg(query, sourceRoot, maxResults, userProtecte
|
|
|
74100
74215
|
const directory = directories.pop();
|
|
74101
74216
|
let entries;
|
|
74102
74217
|
try {
|
|
74103
|
-
entries =
|
|
74218
|
+
entries = readdirSync7(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
74104
74219
|
} catch {
|
|
74105
74220
|
excluded += 1;
|
|
74106
74221
|
continue;
|
|
74107
74222
|
}
|
|
74108
74223
|
for (const entry of entries) {
|
|
74109
|
-
const absolutePath =
|
|
74224
|
+
const absolutePath = join10(directory, entry.name);
|
|
74110
74225
|
const relativePath = relative2(root, absolutePath).replace(/\\/g, "/");
|
|
74111
74226
|
if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, relativePath) || classifyProjectFileRisk(relativePath, userProtectedPatterns).isHighRisk) {
|
|
74112
74227
|
excluded += 1;
|
|
@@ -74275,7 +74390,7 @@ function assertInsideRoot(sourceRoot, path2) {
|
|
|
74275
74390
|
}
|
|
74276
74391
|
function openedDescriptorPath(descriptor, requestedPath) {
|
|
74277
74392
|
const procPath = `/proc/self/fd/${descriptor}`;
|
|
74278
|
-
if (
|
|
74393
|
+
if (existsSync12(procPath)) return realpathSync(procPath);
|
|
74279
74394
|
return realpathSync(requestedPath);
|
|
74280
74395
|
}
|
|
74281
74396
|
function isBinaryFile(path2) {
|
|
@@ -74307,14 +74422,14 @@ function normalizeBound(value, maximum, label) {
|
|
|
74307
74422
|
return result;
|
|
74308
74423
|
}
|
|
74309
74424
|
function remoteAccessStorePath(homeDirectory) {
|
|
74310
|
-
return
|
|
74425
|
+
return join10(homeDirectory, ".openmates", "remote-sources.json");
|
|
74311
74426
|
}
|
|
74312
74427
|
function saveRemoteAccessSources(sources, homeDirectory) {
|
|
74313
74428
|
const filePath = remoteAccessStorePath(homeDirectory);
|
|
74314
|
-
const stateDir2 =
|
|
74315
|
-
|
|
74429
|
+
const stateDir2 = join10(homeDirectory, ".openmates");
|
|
74430
|
+
mkdirSync8(stateDir2, { recursive: true, mode: 448 });
|
|
74316
74431
|
chmodSync6(stateDir2, 448);
|
|
74317
|
-
|
|
74432
|
+
writeFileSync8(filePath, `${JSON.stringify({ sources }, null, 2)}
|
|
74318
74433
|
`, { mode: 384 });
|
|
74319
74434
|
chmodSync6(filePath, 384);
|
|
74320
74435
|
}
|
|
@@ -74522,13 +74637,13 @@ function remoteErrorMessage(code) {
|
|
|
74522
74637
|
|
|
74523
74638
|
// src/selfUpdate.ts
|
|
74524
74639
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
74525
|
-
import { readFileSync as
|
|
74640
|
+
import { readFileSync as readFileSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, renameSync as renameSync5 } from "fs";
|
|
74526
74641
|
import { homedir as homedir8 } from "os";
|
|
74527
|
-
import { dirname as dirname8, basename as basename3, join as
|
|
74642
|
+
import { dirname as dirname8, basename as basename3, join as join11 } from "path";
|
|
74528
74643
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
74529
74644
|
var PACKAGE_NAME = "openmates";
|
|
74530
74645
|
function channelFile() {
|
|
74531
|
-
return
|
|
74646
|
+
return join11(homedir8(), ".openmates", "updates.json");
|
|
74532
74647
|
}
|
|
74533
74648
|
function parseChannel(value) {
|
|
74534
74649
|
if (value === "dev") return "dev";
|
|
@@ -74537,7 +74652,7 @@ function parseChannel(value) {
|
|
|
74537
74652
|
}
|
|
74538
74653
|
function savedChannel() {
|
|
74539
74654
|
try {
|
|
74540
|
-
return parseChannel(JSON.parse(
|
|
74655
|
+
return parseChannel(JSON.parse(readFileSync11(channelFile(), "utf8")).channel);
|
|
74541
74656
|
} catch (error) {
|
|
74542
74657
|
if (error.code === "ENOENT") {
|
|
74543
74658
|
return getCliPackageVersion().includes("-") ? "dev" : "stable";
|
|
@@ -74548,10 +74663,10 @@ function savedChannel() {
|
|
|
74548
74663
|
function persistSelfUpdateChannel(plan) {
|
|
74549
74664
|
if (plan.dryRun || !plan.persistChannel) return;
|
|
74550
74665
|
const path2 = channelFile();
|
|
74551
|
-
|
|
74666
|
+
mkdirSync9(dirname8(path2), { recursive: true, mode: 448 });
|
|
74552
74667
|
const temporary = `${path2}.${process.pid}.tmp`;
|
|
74553
|
-
|
|
74554
|
-
|
|
74668
|
+
writeFileSync9(temporary, JSON.stringify({ channel: plan.channel }) + "\n", { mode: 384 });
|
|
74669
|
+
renameSync5(temporary, path2);
|
|
74555
74670
|
}
|
|
74556
74671
|
function installedNpmPrefix(moduleUrl = import.meta.url, platform4 = process.platform) {
|
|
74557
74672
|
const packageRoot = dirname8(dirname8(fileURLToPath2(moduleUrl)));
|
|
@@ -74583,7 +74698,7 @@ function compareVersions(left, right) {
|
|
|
74583
74698
|
var SAFE_TARGET_RE = /^[a-zA-Z0-9._@+~:-]+$/;
|
|
74584
74699
|
function getCliPackageVersion() {
|
|
74585
74700
|
try {
|
|
74586
|
-
const packageJson = JSON.parse(
|
|
74701
|
+
const packageJson = JSON.parse(readFileSync11(new URL("../package.json", import.meta.url), "utf-8"));
|
|
74587
74702
|
return packageJson.version ?? "unknown";
|
|
74588
74703
|
} catch {
|
|
74589
74704
|
return "unknown";
|
|
@@ -74707,7 +74822,7 @@ function commandName(name) {
|
|
|
74707
74822
|
}
|
|
74708
74823
|
|
|
74709
74824
|
// src/workRecovery.ts
|
|
74710
|
-
import { mkdirSync as
|
|
74825
|
+
import { mkdirSync as mkdirSync10, renameSync as renameSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
74711
74826
|
import { dirname as dirname9 } from "path";
|
|
74712
74827
|
import { stringify } from "yaml";
|
|
74713
74828
|
async function projectPlanAssumption(plan, masterKey, assumption) {
|
|
@@ -74887,18 +75002,18 @@ function collectMismatchPaths(left, right, path2, mismatches) {
|
|
|
74887
75002
|
}
|
|
74888
75003
|
function writeWorkRecoveryAtomically(path2, document) {
|
|
74889
75004
|
validateWorkRecoveryDocument(document);
|
|
74890
|
-
|
|
75005
|
+
mkdirSync10(dirname9(path2), { recursive: true, mode: 448 });
|
|
74891
75006
|
const temporaryPath = `${path2}.tmp`;
|
|
74892
|
-
|
|
74893
|
-
|
|
75007
|
+
writeFileSync10(temporaryPath, stringify(document, { sortMapEntries: true, lineWidth: 0 }), { encoding: "utf8", mode: 384 });
|
|
75008
|
+
renameSync6(temporaryPath, path2);
|
|
74894
75009
|
}
|
|
74895
75010
|
|
|
74896
75011
|
// src/revolutBusinessCertificate.ts
|
|
74897
75012
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
74898
75013
|
import { createSign, randomUUID as randomUUID14 } from "crypto";
|
|
74899
|
-
import { chmodSync as chmodSync7, existsSync as
|
|
75014
|
+
import { chmodSync as chmodSync7, existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync12 } from "fs";
|
|
74900
75015
|
import { homedir as homedir9 } from "os";
|
|
74901
|
-
import { join as
|
|
75016
|
+
import { join as join12, resolve as resolve10 } from "path";
|
|
74902
75017
|
var REVOLUT_BUSINESS_CERTIFICATE_DOCS_URL = "https://developer.revolut.com/docs/guides/manage-accounts/get-started/make-your-first-api-request#generate-a-private-and-a-public-certificate";
|
|
74903
75018
|
var REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI = "https://app.dev.openmates.org/oauth/revolut-business/callback";
|
|
74904
75019
|
var REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI = "https://openmates.org/oauth/revolut-business/callback";
|
|
@@ -74908,7 +75023,7 @@ var REVOLUT_BUSINESS_AUDIENCE = "https://revolut.com";
|
|
|
74908
75023
|
var CERTIFICATE_DAYS = "1825";
|
|
74909
75024
|
var RSA_BITS = "2048";
|
|
74910
75025
|
function defaultRevolutBusinessCertificateDir(environment) {
|
|
74911
|
-
return
|
|
75026
|
+
return join12(homedir9(), ".openmates", "revolut-business", environment);
|
|
74912
75027
|
}
|
|
74913
75028
|
function defaultRevolutBusinessRedirectUri(environment) {
|
|
74914
75029
|
return environment === "sandbox" ? REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI : REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI;
|
|
@@ -74931,7 +75046,7 @@ async function exchangeRevolutBusinessAuthorizationCode(options) {
|
|
|
74931
75046
|
if (!clientId) throw new Error("Missing Revolut Business client ID.");
|
|
74932
75047
|
const code = extractRevolutBusinessAuthorizationCode(options.codeOrRedirectUrl);
|
|
74933
75048
|
if (!code) throw new Error("Missing Revolut Business authorization code.");
|
|
74934
|
-
const privateKeyPem = options.privateKeyPem ??
|
|
75049
|
+
const privateKeyPem = options.privateKeyPem ?? readFileSync12(options.privateKeyPath ?? join12(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
|
|
74935
75050
|
const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
|
|
74936
75051
|
const clientAssertion = generateRevolutBusinessClientAssertion({ clientId, privateKeyPem, redirectUri });
|
|
74937
75052
|
const response = await fetch(environment === "sandbox" ? "https://sandbox-b2b.revolut.com/api/1.0/auth/token" : "https://b2b.revolut.com/api/1.0/auth/token", {
|
|
@@ -74984,11 +75099,11 @@ function generateRevolutBusinessCertificate(options = {}) {
|
|
|
74984
75099
|
const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
|
|
74985
75100
|
if (!title) throw new Error("Revolut certificate title must not be empty.");
|
|
74986
75101
|
if (!redirectUri.startsWith("https://")) throw new Error("Revolut OAuth redirect URI must start with https://.");
|
|
74987
|
-
|
|
75102
|
+
mkdirSync11(outputDir, { recursive: true, mode: 448 });
|
|
74988
75103
|
chmodSync7(outputDir, 448);
|
|
74989
|
-
const privateKeyPath =
|
|
74990
|
-
const publicCertificatePath =
|
|
74991
|
-
if (!options.overwrite && (
|
|
75104
|
+
const privateKeyPath = join12(outputDir, "privatecert.pem");
|
|
75105
|
+
const publicCertificatePath = join12(outputDir, "publiccert.cer");
|
|
75106
|
+
if (!options.overwrite && (existsSync13(privateKeyPath) || existsSync13(publicCertificatePath))) {
|
|
74992
75107
|
throw new Error(`Revolut certificate files already exist in ${outputDir}. Re-run with --overwrite to replace them.`);
|
|
74993
75108
|
}
|
|
74994
75109
|
try {
|
|
@@ -75022,7 +75137,7 @@ function generateRevolutBusinessCertificate(options = {}) {
|
|
|
75022
75137
|
outputDir,
|
|
75023
75138
|
privateKeyPath,
|
|
75024
75139
|
publicCertificatePath,
|
|
75025
|
-
publicCertificatePem:
|
|
75140
|
+
publicCertificatePem: readFileSync12(publicCertificatePath, "utf8").trim()
|
|
75026
75141
|
};
|
|
75027
75142
|
}
|
|
75028
75143
|
function escapeOpenSslSubjectValue(value) {
|
|
@@ -75559,35 +75674,55 @@ async function handleTasks(client, subcommand, rest, flags, redactor) {
|
|
|
75559
75674
|
const action = rest[0] ?? "list";
|
|
75560
75675
|
const task = await requiredResolvedTask(client, masterKey, rest[1], scope, `activity ${action}`);
|
|
75561
75676
|
const context = { teamId: scope.teamId, personal: scope.personal };
|
|
75562
|
-
|
|
75563
|
-
|
|
75564
|
-
|
|
75565
|
-
|
|
75566
|
-
|
|
75567
|
-
|
|
75568
|
-
|
|
75569
|
-
|
|
75570
|
-
|
|
75571
|
-
|
|
75572
|
-
|
|
75573
|
-
|
|
75574
|
-
|
|
75575
|
-
|
|
75576
|
-
|
|
75677
|
+
const actor = flags["as-assignee"] === true ? "assignee" : "user";
|
|
75678
|
+
const delivery = () => activityDeliveryStore(JSON.stringify([
|
|
75679
|
+
client.getSession().apiUrl,
|
|
75680
|
+
client.getSession().hashedEmail,
|
|
75681
|
+
scope.teamId || "personal",
|
|
75682
|
+
task.taskId,
|
|
75683
|
+
actor
|
|
75684
|
+
]));
|
|
75685
|
+
const createActivity = (input) => client.createUserTaskActivity(task.taskId, input, {
|
|
75686
|
+
...context,
|
|
75687
|
+
...actor === "assignee" ? { actorMode: "assignee" } : {}
|
|
75688
|
+
});
|
|
75689
|
+
if (action === "flush") {
|
|
75690
|
+
printJson2(await delivery().flush(createActivity));
|
|
75691
|
+
return;
|
|
75692
|
+
}
|
|
75693
|
+
if (action === "list" || action === "search") {
|
|
75694
|
+
const pending = flags["flush-pending"] === true ? await delivery().flush(createActivity) : void 0;
|
|
75695
|
+
const history = await readActivityHistory({
|
|
75696
|
+
maxEntries: typeof flags["max-entries"] === "string" ? Number(flags["max-entries"]) : action === "search" ? 200 : void 0,
|
|
75697
|
+
pageSize: typeof flags.limit === "string" ? Number(flags.limit) : 100,
|
|
75698
|
+
cursor: typeof flags.cursor === "string" ? flags.cursor : void 0,
|
|
75699
|
+
...action === "search" ? { query: typeof flags.query === "string" ? flags.query : "" } : {},
|
|
75700
|
+
page: async (cursor, limit) => {
|
|
75701
|
+
const page = await client.listUserTaskActivity(task.taskId, {
|
|
75702
|
+
...context,
|
|
75703
|
+
cursor,
|
|
75704
|
+
limit,
|
|
75705
|
+
newestFirst: flags["newest-first"] === true
|
|
75706
|
+
});
|
|
75707
|
+
return { entries: await decryptTaskActivityEntries(task, masterKey, page.entries), next_cursor: page.next_cursor };
|
|
75708
|
+
}
|
|
75709
|
+
});
|
|
75710
|
+
if (flags.json === true) printJson2({ ...history, entries: history.entries.map(taskActivityToJson), delivery: pending });
|
|
75711
|
+
else {
|
|
75712
|
+
console.log(renderTaskActivityList(history.entries));
|
|
75713
|
+
if (history.truncated) console.log(`Activity history truncated. Search all history: openmates tasks activity search ${task.shortId} --query "text" --max-entries 200`);
|
|
75714
|
+
if (history.next_cursor) console.log(`Next cursor: ${history.next_cursor}`);
|
|
75715
|
+
}
|
|
75577
75716
|
return;
|
|
75578
75717
|
}
|
|
75579
75718
|
if (action === "add") {
|
|
75580
75719
|
const message = typeof flags.message === "string" ? flags.message : rest.slice(2).join(" ");
|
|
75581
75720
|
if (!message.trim()) throw new Error("Missing comment. Usage: openmates tasks activity add <task-id> --message <text>");
|
|
75582
|
-
const
|
|
75583
|
-
const
|
|
75584
|
-
|
|
75585
|
-
|
|
75586
|
-
|
|
75587
|
-
...context,
|
|
75588
|
-
...flags["as-assignee"] === true ? { actorMode: "assignee" } : {}
|
|
75589
|
-
})
|
|
75590
|
-
);
|
|
75721
|
+
const deliveryId = typeof flags["delivery-id"] === "string" ? flags["delivery-id"] : void 0;
|
|
75722
|
+
const build = () => buildCreateTaskActivityInput(task, masterKey, { message, ...deliveryId ? { entryId: deliveryId } : {} });
|
|
75723
|
+
const record = deliveryId ? await delivery().deliver(deliveryId, build, createActivity) : await createActivity(await build());
|
|
75724
|
+
const entry = await decryptTaskActivityEntry(task, masterKey, record);
|
|
75725
|
+
if (entry.message !== message) throw new Error("Activity delivery id already belongs to a different milestone message");
|
|
75591
75726
|
if (flags.json === true) printJson2({ entry: taskActivityToJson(entry) });
|
|
75592
75727
|
else console.log(renderTaskActivityList([entry]));
|
|
75593
75728
|
return;
|
|
@@ -75604,7 +75739,7 @@ async function handleTasks(client, subcommand, rest, flags, redactor) {
|
|
|
75604
75739
|
else console.log(renderTaskActivityList([entry]));
|
|
75605
75740
|
return;
|
|
75606
75741
|
}
|
|
75607
|
-
throw new Error("Usage: openmates tasks activity list|add|delete <task-id> [<entry-id>] [--message <text>]");
|
|
75742
|
+
throw new Error("Usage: openmates tasks activity list|search|add|flush|delete <task-id> [<entry-id>] [--message <text>]");
|
|
75608
75743
|
}
|
|
75609
75744
|
if (rest[0] === "add-to-project") {
|
|
75610
75745
|
rejectRemoteCopyFlags(flags);
|
|
@@ -76540,7 +76675,7 @@ async function handlePlans(client, subcommand, rest, flags, redactor) {
|
|
|
76540
76675
|
if (action === "diff") throw new Error("Revision diffs are reviewed in the authenticated web review page; use submit-for-review to print its URL.");
|
|
76541
76676
|
if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
|
|
76542
76677
|
const snapshot = JSON.stringify(planToJson(plan));
|
|
76543
|
-
const revision = await client.submitPlanRevision(plan.planId, { fingerprint:
|
|
76678
|
+
const revision = await client.submitPlanRevision(plan.planId, { fingerprint: createHash15("sha256").update(snapshot).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot), created_at: nowSeconds3() });
|
|
76544
76679
|
const reviewUrl = `${deriveAppUrl(client.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
|
|
76545
76680
|
if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
|
|
76546
76681
|
else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
|
|
@@ -77096,8 +77231,8 @@ async function handleWorkRecovery(client, subcommand, rest, flags) {
|
|
|
77096
77231
|
}
|
|
77097
77232
|
if (subcommand === "validate" || subcommand === "restore") {
|
|
77098
77233
|
const path2 = requiredStringFlag(flags.file ?? rest[0], "--file <projection.yml>");
|
|
77099
|
-
if (!
|
|
77100
|
-
const document = parseYaml(
|
|
77234
|
+
if (!existsSync14(path2)) throw new Error(`Recovery projection does not exist: ${path2}`);
|
|
77235
|
+
const document = parseYaml(readFileSync13(path2, "utf8"));
|
|
77101
77236
|
validateWorkRecoveryDocument(document);
|
|
77102
77237
|
if (subcommand === "validate") {
|
|
77103
77238
|
if (flags.json === true) printJson2({ valid: true });
|
|
@@ -77445,7 +77580,7 @@ async function handleProjects(client, subcommand, rest, flags, redactor) {
|
|
|
77445
77580
|
last_opened_at: timestamp,
|
|
77446
77581
|
key_wrappers: wrapping.teamId ? [{
|
|
77447
77582
|
key_type: "team",
|
|
77448
|
-
hashed_team_id:
|
|
77583
|
+
hashed_team_id: createHash15("sha256").update(wrapping.teamId).digest("hex"),
|
|
77449
77584
|
team_key_epoch: 1,
|
|
77450
77585
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
77451
77586
|
wrapper_version: 1,
|
|
@@ -77928,7 +78063,7 @@ async function handleTeams(client, subcommand, rest, flags) {
|
|
|
77928
78063
|
if (action === "get") {
|
|
77929
78064
|
const output = requiredStringFlag(flags.output ?? flags.file ?? rest[2], "--output <path>");
|
|
77930
78065
|
const image = await client.getTeamProfileImage(teamId);
|
|
77931
|
-
|
|
78066
|
+
writeFileSync11(output, image.data, { mode: 384 });
|
|
77932
78067
|
if (flags.json === true) printJson2({ output, content_type: image.contentType, size_bytes: image.data.byteLength });
|
|
77933
78068
|
else console.log(`Team profile image written: ${output}`);
|
|
77934
78069
|
return;
|
|
@@ -78087,7 +78222,7 @@ async function handleTeams(client, subcommand, rest, flags) {
|
|
|
78087
78222
|
const output = requiredStringFlag(flags.output, "--output <path>");
|
|
78088
78223
|
const result = await client.exportTeamData(teamId);
|
|
78089
78224
|
const artifact = result.artifact && typeof result.artifact === "object" ? result.artifact : result;
|
|
78090
|
-
|
|
78225
|
+
writeFileSync11(output, `${JSON.stringify(artifact, null, 2)}
|
|
78091
78226
|
`, { encoding: "utf-8", mode: 384 });
|
|
78092
78227
|
if (flags.json === true) printJson2({ ...result, output });
|
|
78093
78228
|
else console.log(`Team export written: ${output}`);
|
|
@@ -78095,7 +78230,7 @@ async function handleTeams(client, subcommand, rest, flags) {
|
|
|
78095
78230
|
}
|
|
78096
78231
|
if (subcommand === "import") {
|
|
78097
78232
|
const file = requiredStringFlag(flags.file ?? rest[0], "--file <path>");
|
|
78098
|
-
const artifact = JSON.parse(
|
|
78233
|
+
const artifact = JSON.parse(readFileSync13(file, "utf-8"));
|
|
78099
78234
|
let teamId = typeof flags.team === "string" ? flags.team : typeof flags["destination-team"] === "string" ? flags["destination-team"] : void 0;
|
|
78100
78235
|
if (!teamId && typeof flags["new-team-name"] === "string") {
|
|
78101
78236
|
const team = await client.createTeam({ name: flags["new-team-name"] });
|
|
@@ -78592,7 +78727,7 @@ async function createEncryptedRemoteAccessProject(client, masterKey, name, conte
|
|
|
78592
78727
|
last_opened_at: timestamp,
|
|
78593
78728
|
key_wrappers: wrapping.teamId ? [{
|
|
78594
78729
|
key_type: "team",
|
|
78595
|
-
hashed_team_id:
|
|
78730
|
+
hashed_team_id: createHash15("sha256").update(wrapping.teamId).digest("hex"),
|
|
78596
78731
|
team_key_epoch: 1,
|
|
78597
78732
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
78598
78733
|
wrapper_version: 1,
|
|
@@ -79392,16 +79527,16 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79392
79527
|
}
|
|
79393
79528
|
}
|
|
79394
79529
|
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
79395
|
-
const { join:
|
|
79530
|
+
const { join: join13 } = await import("path");
|
|
79396
79531
|
if (useZip) {
|
|
79397
|
-
const tmpDir =
|
|
79532
|
+
const tmpDir = join13(outputDir, `.${filenameBase}_tmp`);
|
|
79398
79533
|
await mkdir2(tmpDir, { recursive: true });
|
|
79399
|
-
await writeFile2(
|
|
79400
|
-
await writeFile2(
|
|
79534
|
+
await writeFile2(join13(tmpDir, `${filenameBase}.yml`), yamlContent);
|
|
79535
|
+
await writeFile2(join13(tmpDir, `${filenameBase}.md`), mdContent);
|
|
79401
79536
|
if (codeEmbeds.length > 0) {
|
|
79402
79537
|
for (const ce of codeEmbeds) {
|
|
79403
79538
|
const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
|
|
79404
|
-
const fullPath =
|
|
79539
|
+
const fullPath = join13(tmpDir, "code", fpath);
|
|
79405
79540
|
await mkdir2(fullPath.substring(0, fullPath.lastIndexOf("/")), {
|
|
79406
79541
|
recursive: true
|
|
79407
79542
|
});
|
|
@@ -79409,13 +79544,13 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79409
79544
|
}
|
|
79410
79545
|
}
|
|
79411
79546
|
if (transcriptEmbeds.length > 0) {
|
|
79412
|
-
const tDir =
|
|
79547
|
+
const tDir = join13(tmpDir, "transcripts");
|
|
79413
79548
|
await mkdir2(tDir, { recursive: true });
|
|
79414
79549
|
for (const te of transcriptEmbeds) {
|
|
79415
|
-
await writeFile2(
|
|
79550
|
+
await writeFile2(join13(tDir, te.filename), te.content);
|
|
79416
79551
|
}
|
|
79417
79552
|
}
|
|
79418
|
-
const zipPath =
|
|
79553
|
+
const zipPath = join13(outputDir, `${filenameBase}.zip`);
|
|
79419
79554
|
const { execSync: execSync2 } = await import("child_process");
|
|
79420
79555
|
try {
|
|
79421
79556
|
execSync2(`cd "${tmpDir}" && zip -r "${zipPath}" .`, { stdio: "pipe" });
|
|
@@ -79430,17 +79565,17 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79430
79565
|
);
|
|
79431
79566
|
}
|
|
79432
79567
|
} else {
|
|
79433
|
-
const chatDir =
|
|
79568
|
+
const chatDir = join13(outputDir, filenameBase);
|
|
79434
79569
|
await mkdir2(chatDir, { recursive: true });
|
|
79435
79570
|
const written = [];
|
|
79436
|
-
await writeFile2(
|
|
79571
|
+
await writeFile2(join13(chatDir, `${filenameBase}.yml`), yamlContent);
|
|
79437
79572
|
written.push(`${filenameBase}.yml`);
|
|
79438
|
-
await writeFile2(
|
|
79573
|
+
await writeFile2(join13(chatDir, `${filenameBase}.md`), mdContent);
|
|
79439
79574
|
written.push(`${filenameBase}.md`);
|
|
79440
79575
|
if (codeEmbeds.length > 0) {
|
|
79441
79576
|
for (const ce of codeEmbeds) {
|
|
79442
79577
|
const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
|
|
79443
|
-
const fullPath =
|
|
79578
|
+
const fullPath = join13(chatDir, "code", fpath);
|
|
79444
79579
|
await mkdir2(fullPath.substring(0, fullPath.lastIndexOf("/")), {
|
|
79445
79580
|
recursive: true
|
|
79446
79581
|
});
|
|
@@ -79449,10 +79584,10 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79449
79584
|
}
|
|
79450
79585
|
}
|
|
79451
79586
|
if (transcriptEmbeds.length > 0) {
|
|
79452
|
-
const tDir =
|
|
79587
|
+
const tDir = join13(chatDir, "transcripts");
|
|
79453
79588
|
await mkdir2(tDir, { recursive: true });
|
|
79454
79589
|
for (const te of transcriptEmbeds) {
|
|
79455
|
-
await writeFile2(
|
|
79590
|
+
await writeFile2(join13(tDir, te.filename), te.content);
|
|
79456
79591
|
written.push(`transcripts/${te.filename}`);
|
|
79457
79592
|
}
|
|
79458
79593
|
}
|
|
@@ -79488,7 +79623,7 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79488
79623
|
printJson2({
|
|
79489
79624
|
chat_id: chat.id,
|
|
79490
79625
|
title: chat.title,
|
|
79491
|
-
output_dir: useZip ?
|
|
79626
|
+
output_dir: useZip ? join13(outputDir, `${filenameBase}.zip`) : join13(outputDir, filenameBase),
|
|
79492
79627
|
files,
|
|
79493
79628
|
code_embeds: codeEmbeds.length,
|
|
79494
79629
|
transcript_embeds: transcriptEmbeds.length
|
|
@@ -79808,7 +79943,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
79808
79943
|
const messages = rawMessages.map((message) => {
|
|
79809
79944
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
79810
79945
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
79811
|
-
const hashedMessageId = messageId ?
|
|
79946
|
+
const hashedMessageId = messageId ? createHash15("sha256").update(messageId).digest("hex") : null;
|
|
79812
79947
|
const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
|
|
79813
79948
|
return {
|
|
79814
79949
|
id: messageId,
|
|
@@ -80014,7 +80149,7 @@ async function handleWorkflows(client, subcommand, rest, flags, redactor) {
|
|
|
80014
80149
|
if (subcommand === "validate") {
|
|
80015
80150
|
const file = typeof flags.file === "string" ? flags.file : "";
|
|
80016
80151
|
if (!file) throw new Error("Missing --file. Example: openmates workflows validate --file workflow.yml");
|
|
80017
|
-
const validation = await client.validateWorkflowYaml(
|
|
80152
|
+
const validation = await client.validateWorkflowYaml(readFileSync13(file, "utf8"));
|
|
80018
80153
|
if (flags.json === true) {
|
|
80019
80154
|
printJson2(validation);
|
|
80020
80155
|
} else {
|
|
@@ -80030,7 +80165,7 @@ async function handleWorkflows(client, subcommand, rest, flags, redactor) {
|
|
|
80030
80165
|
if (subcommand === "create") {
|
|
80031
80166
|
const yamlFile = typeof flags.file === "string" ? flags.file : "";
|
|
80032
80167
|
if (yamlFile) {
|
|
80033
|
-
const result = await client.createWorkflowYaml(
|
|
80168
|
+
const result = await client.createWorkflowYaml(readFileSync13(yamlFile, "utf8"));
|
|
80034
80169
|
if (flags.json === true) {
|
|
80035
80170
|
printJson2(result);
|
|
80036
80171
|
} else {
|
|
@@ -80064,7 +80199,7 @@ async function handleWorkflows(client, subcommand, rest, flags, redactor) {
|
|
|
80064
80199
|
const workflowId = rest[0] ? await requiredResolvedWorkflowId(client, rest[0], flags, "update") : void 0;
|
|
80065
80200
|
const yamlFile = typeof flags.file === "string" ? flags.file : "";
|
|
80066
80201
|
if (!workflowId || !yamlFile) throw new Error("Missing workflow ID or --file. Example: openmates workflows update <id> --file workflow.yml");
|
|
80067
|
-
const result = await client.updateWorkflowYaml(workflowId,
|
|
80202
|
+
const result = await client.updateWorkflowYaml(workflowId, readFileSync13(yamlFile, "utf8"));
|
|
80068
80203
|
if (typeof flags.slug === "string") {
|
|
80069
80204
|
result.workflow = await client.updateWorkflow(result.workflow.id, { slug: flags.slug });
|
|
80070
80205
|
}
|
|
@@ -80666,7 +80801,7 @@ function buildCodeImageToHtmlRequest(positionals, flags) {
|
|
|
80666
80801
|
);
|
|
80667
80802
|
}
|
|
80668
80803
|
const request = {
|
|
80669
|
-
image_base64:
|
|
80804
|
+
image_base64: readFileSync13(file).toString("base64"),
|
|
80670
80805
|
mime_type: inferImageToHtmlMimeType(file),
|
|
80671
80806
|
filename: file.split(/[\\/]/).pop() ?? file
|
|
80672
80807
|
};
|
|
@@ -81545,7 +81680,7 @@ async function handleEmbeds(client, subcommand, rest, flags) {
|
|
|
81545
81680
|
throw new Error("Embed version content was not available after local reconstruction.");
|
|
81546
81681
|
}
|
|
81547
81682
|
if (typeof flags.output === "string") {
|
|
81548
|
-
|
|
81683
|
+
writeFileSync11(flags.output, result.content, "utf-8");
|
|
81549
81684
|
if (flags.json === true) {
|
|
81550
81685
|
printJson2({ ...result, output: flags.output });
|
|
81551
81686
|
} else {
|
|
@@ -82204,10 +82339,10 @@ function parseYamlScalar(value) {
|
|
|
82204
82339
|
}
|
|
82205
82340
|
async function saveDownloadedDocument(document, output) {
|
|
82206
82341
|
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
82207
|
-
const { join:
|
|
82342
|
+
const { join: join13, basename: basename5, dirname: dirname11 } = await import("path");
|
|
82208
82343
|
const target = typeof output === "string" ? output : ".";
|
|
82209
82344
|
const filename = basename5(document.filename || "document.pdf");
|
|
82210
|
-
const filePath = target.endsWith(".pdf") ? target :
|
|
82345
|
+
const filePath = target.endsWith(".pdf") ? target : join13(target, filename);
|
|
82211
82346
|
await mkdir2(dirname11(filePath), { recursive: true });
|
|
82212
82347
|
await writeFile2(filePath, document.data);
|
|
82213
82348
|
return filePath;
|
|
@@ -82380,7 +82515,7 @@ async function handleFinance(client, subcommand, _rest, flags) {
|
|
|
82380
82515
|
}
|
|
82381
82516
|
function readFinanceCsvStatements(flags) {
|
|
82382
82517
|
const paths = parseCsvFlag(flags.csv) ?? [];
|
|
82383
|
-
return paths.map((filePath) => ({ filename: basename4(filePath), content:
|
|
82518
|
+
return paths.map((filePath) => ({ filename: basename4(filePath), content: readFileSync13(filePath, "utf8") }));
|
|
82384
82519
|
}
|
|
82385
82520
|
function buildFinanceCheckAccountsInput(flags, csvStatements, connectedAccount) {
|
|
82386
82521
|
const input = {
|
|
@@ -82609,7 +82744,7 @@ async function handleRevolutBusinessConnect(client, rest, flags) {
|
|
|
82609
82744
|
if (!clientId.trim()) throw new Error("ClientID is required to connect Revolut Business.");
|
|
82610
82745
|
const code = providedCode || await promptPlainText("Code from OpenMates callback page: ");
|
|
82611
82746
|
if (!code.trim()) throw new Error("Authorization code is required to connect Revolut Business.");
|
|
82612
|
-
const privateKeyPem =
|
|
82747
|
+
const privateKeyPem = readFileSync13(result.privateKeyPath, "utf8");
|
|
82613
82748
|
const setup = await client.exchangeRevolutBusinessSetupCode({
|
|
82614
82749
|
clientId: clientId.trim(),
|
|
82615
82750
|
code: code.trim(),
|
|
@@ -86197,8 +86332,10 @@ function printTasksHelp() {
|
|
|
86197
86332
|
openmates tasks reorder <task-id|short-id> [--before <task-id>] [--after <task-id>] [--position <n>] [--status <status>] [--json]
|
|
86198
86333
|
openmates tasks dependencies list <task-id|short-id> [--json]
|
|
86199
86334
|
openmates tasks dependencies add|remove <task-id|short-id> --target plan:<id>|task:<id> [--recovery-root <external-root>] [--json]
|
|
86200
|
-
openmates tasks activity list <task-id|short-id> [--
|
|
86201
|
-
openmates tasks activity
|
|
86335
|
+
openmates tasks activity list <task-id|short-id> [--max-entries <1-200>] [--cursor <cursor>] [--newest-first] [--json]
|
|
86336
|
+
openmates tasks activity search <task-id|short-id> --query <text> [--max-entries <1-200>] [--json]
|
|
86337
|
+
openmates tasks activity add <task-id|short-id> --message <text> [--as-assignee] [--delivery-id <sha256>] [--json]
|
|
86338
|
+
openmates tasks activity flush <task-id|short-id> [--as-assignee] [--json]
|
|
86202
86339
|
openmates tasks activity delete <task-id|short-id> <entry-id> [--json]
|
|
86203
86340
|
|
|
86204
86341
|
Chat-scoped aliases:
|
|
@@ -86668,7 +86805,7 @@ async function handleDocs(client, subcommand, rest, flags) {
|
|
|
86668
86805
|
}
|
|
86669
86806
|
if (subcommand === "download") {
|
|
86670
86807
|
const { writeFile: writeFile2, mkdir: mkdir2 } = await import("fs/promises");
|
|
86671
|
-
const { join:
|
|
86808
|
+
const { join: join13, dirname: dirname11 } = await import("path");
|
|
86672
86809
|
if (flags.all === true) {
|
|
86673
86810
|
const outputDir = typeof flags.output === "string" ? flags.output : "./openmates-docs";
|
|
86674
86811
|
const tree = await client.listDocs();
|
|
@@ -86677,7 +86814,7 @@ async function handleDocs(client, subcommand, rest, flags) {
|
|
|
86677
86814
|
let count = 0;
|
|
86678
86815
|
for (const slug2 of slugs) {
|
|
86679
86816
|
const content2 = await client.getDoc(slug2);
|
|
86680
|
-
const filePath =
|
|
86817
|
+
const filePath = join13(outputDir, `${slug2}.md`);
|
|
86681
86818
|
await mkdir2(dirname11(filePath), { recursive: true });
|
|
86682
86819
|
await writeFile2(filePath, content2, "utf-8");
|
|
86683
86820
|
count++;
|