openmates 0.18.0-alpha.0 → 0.18.0-alpha.2

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.
@@ -24301,7 +24301,7 @@ import { stdin as stdin3, stdout as stdout2 } from "process";
24301
24301
  import { existsSync as existsSync13, readFileSync as readFileSync12, realpathSync as realpathSync2, writeFileSync as writeFileSync9 } from "fs";
24302
24302
  import { fileURLToPath as fileURLToPath2 } from "url";
24303
24303
  import { basename as basename3, dirname as dirname9 } from "path";
24304
- import { createHash as createHash14, randomBytes as randomBytes9, randomUUID as randomUUID14 } from "crypto";
24304
+ import { createHash as createHash14, randomBytes as randomBytes9, randomUUID as randomUUID15 } from "crypto";
24305
24305
  import { arch as arch2, platform as platform3 } from "os";
24306
24306
  import { parse as parseYaml } from "yaml";
24307
24307
  import WebSocket2 from "ws";
@@ -26479,9 +26479,9 @@ function formatTs(ts) {
26479
26479
  }
26480
26480
 
26481
26481
  // src/server.ts
26482
- import { execFileSync as execFileSync3, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
26483
- import { createHash as createHash12, randomBytes as randomBytes8, randomUUID as randomUUID10 } from "crypto";
26484
- import { appendFileSync, chmodSync as chmodSync4, closeSync as closeSync2, copyFileSync as copyFileSync2, cpSync, existsSync as existsSync9, mkdirSync as mkdirSync6, mkdtempSync, openSync as openSync2, readFileSync as readFileSync7, readSync, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
26482
+ import { execFileSync as execFileSync4, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
26483
+ import { createHash as createHash12, randomBytes as randomBytes8, randomUUID as randomUUID11 } from "crypto";
26484
+ import { appendFileSync, chmodSync as chmodSync5, closeSync as closeSync2, copyFileSync as copyFileSync2, cpSync, existsSync as existsSync9, mkdirSync as mkdirSync6, mkdtempSync, openSync as openSync2, readFileSync as readFileSync7, readSync, readdirSync as readdirSync4, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "fs";
26485
26485
  import { createInterface as createInterface3 } from "readline";
26486
26486
  import { createInterface as createPromptInterface } from "readline/promises";
26487
26487
  import { homedir as homedir6 } from "os";
@@ -26955,12 +26955,11 @@ function planUpdate(input) {
26955
26955
  function planBackup(input) {
26956
26956
  const role = parseServerRole(input.role);
26957
26957
  const contentsByRole = {
26958
- core: ["postgres-dump", "directus-uploads", "directus-extensions", "vault-data", "vault-setup-data", "runtime-env", "runtime-config", "manifest", "checksums"],
26959
- upload: ["vault-data", "vault-setup-data", "runtime-env", "runtime-config", "manifest", "checksums"],
26960
- preview: ["runtime-env", "runtime-config", "preview-cache", "manifest", "checksums"]
26958
+ core: ["postgres-dump", "runtime-env", "runtime-config", "manifest", "checksums"],
26959
+ upload: ["runtime-env", "runtime-config", "manifest", "checksums"],
26960
+ preview: ["runtime-env", "runtime-config", "manifest", "checksums"]
26961
26961
  };
26962
26962
  const contents = [...contentsByRole[role]];
26963
- if (input.includeObservability) contents.push("openobserve-data", "prometheus-data");
26964
26963
  return { role, contents, fileMode: 384 };
26965
26964
  }
26966
26965
  function planRestore(input) {
@@ -27193,9 +27192,38 @@ function shouldAutoInstallRuntimeMonitoringServices(env) {
27193
27192
  return env.OPENMATES_SKIP_RUNTIME_MONITORING !== "1";
27194
27193
  }
27195
27194
 
27195
+ // src/serverBackupArchive.ts
27196
+ import { execFileSync as execFileSync3 } from "child_process";
27197
+ import { chmodSync as chmodSync3, lstatSync, readdirSync as readdirSync3, renameSync, rmSync as rmSync4 } from "fs";
27198
+ import { randomUUID as randomUUID8 } from "crypto";
27199
+ function assertRegularBackupTree(path2) {
27200
+ const stat2 = lstatSync(path2);
27201
+ if (stat2.isDirectory()) {
27202
+ for (const entry of readdirSync3(path2)) assertRegularBackupTree(`${path2}/${entry}`);
27203
+ return;
27204
+ }
27205
+ if (!stat2.isFile() || stat2.nlink !== 1) {
27206
+ throw new Error(`Backup archive refused unsafe entry: ${path2}`);
27207
+ }
27208
+ }
27209
+ function publishServerBackupArchive(sourceDir, archivePath, options = {}) {
27210
+ assertRegularBackupTree(sourceDir);
27211
+ const temporaryArchivePath = `${archivePath}.tmp-${randomUUID8()}`;
27212
+ const previousUmask = process.umask(63);
27213
+ try {
27214
+ execFileSync3(options.tarCommand ?? "tar", ["-czf", temporaryArchivePath, "-C", sourceDir, "."], { stdio: "pipe" });
27215
+ chmodSync3(temporaryArchivePath, 384);
27216
+ renameSync(temporaryArchivePath, archivePath);
27217
+ chmodSync3(archivePath, 384);
27218
+ } finally {
27219
+ process.umask(previousUmask);
27220
+ rmSync4(temporaryArchivePath, { force: true });
27221
+ }
27222
+ }
27223
+
27196
27224
  // src/serverHealth.ts
27197
27225
  import dnsModule, { promises as dns } from "dns";
27198
- import { createHmac as createHmac4, randomUUID as randomUUID8 } from "crypto";
27226
+ import { createHmac as createHmac4, randomUUID as randomUUID9 } from "crypto";
27199
27227
  import { promises as fs } from "fs";
27200
27228
  import { isIP } from "net";
27201
27229
  import { request as httpsRequest } from "https";
@@ -27578,7 +27606,7 @@ async function resolveGenericWebhookTarget(rawUrl, allowLocalDevelopmentFixture
27578
27606
  async function sendGenericWebhook(target, secret, payload, allowLocalDevelopmentFixture = false) {
27579
27607
  const { url, addresses } = await resolveGenericWebhookTarget(target, allowLocalDevelopmentFixture);
27580
27608
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
27581
- const eventId = randomUUID8();
27609
+ const eventId = randomUUID9();
27582
27610
  const signed = signRuntimeWebhookPayload(payload, secret, timestamp, eventId);
27583
27611
  const selected = addresses[0];
27584
27612
  await new Promise((resolve11, reject) => {
@@ -27787,7 +27815,7 @@ function signRuntimeWebhookPayload(payload, secret, timestamp, eventId) {
27787
27815
 
27788
27816
  // src/serverUpdateState.ts
27789
27817
  import { randomBytes as randomBytes7 } from "crypto";
27790
- import { chmodSync as chmodSync3, closeSync, existsSync as existsSync8, mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync6, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
27818
+ import { chmodSync as chmodSync4, closeSync, existsSync as existsSync8, mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync6, renameSync as renameSync2, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "fs";
27791
27819
  import { dirname as dirname5, join as join6 } from "path";
27792
27820
  function serverUpdateStatusFile(installPath, role) {
27793
27821
  return join6(installPath, ".openmates", `${role}-update-status.json`);
@@ -27811,8 +27839,8 @@ function writeServerUpdateStatus(installPath, role, status) {
27811
27839
  const temporaryPath = `${filePath}.${process.pid}.${randomBytes7(4).toString("hex")}.tmp`;
27812
27840
  writeFileSync4(temporaryPath, `${JSON.stringify({ role, updated_at: (/* @__PURE__ */ new Date()).toISOString(), ...status }, null, 2)}
27813
27841
  `, { mode: 384 });
27814
- renameSync(temporaryPath, filePath);
27815
- chmodSync3(filePath, 384);
27842
+ renameSync2(temporaryPath, filePath);
27843
+ chmodSync4(filePath, 384);
27816
27844
  }
27817
27845
  function acquireServerUpdateLock(installPath) {
27818
27846
  const stateDir2 = join6(installPath, ".openmates");
@@ -27839,13 +27867,13 @@ function acquireServerUpdateLock(installPath) {
27839
27867
  return () => {
27840
27868
  closeSync(descriptor);
27841
27869
  if (existsSync8(lockPath) && readFileSync6(lockPath, "utf8").trim() === ownershipToken) {
27842
- rmSync4(lockPath);
27870
+ rmSync5(lockPath);
27843
27871
  }
27844
27872
  };
27845
27873
  }
27846
27874
 
27847
27875
  // src/serverQuickTest.ts
27848
- import { randomUUID as randomUUID9 } from "crypto";
27876
+ import { randomUUID as randomUUID10 } from "crypto";
27849
27877
  var QUICK_TEST_RESPONSE_TIMEOUT_MS = 12e4;
27850
27878
  var QUICK_TEST_WEB_QUERY = "site:openmates.org OpenMates";
27851
27879
  var QUICK_TEST_EXPECTED_RESPONSE = "server quick test passed";
@@ -27911,7 +27939,7 @@ function decideQuickServerTestAction(input) {
27911
27939
  async function runQuickServerTest(client, options = {}) {
27912
27940
  const now = options.now ?? Date.now;
27913
27941
  const checks = [];
27914
- const chatId = randomUUID9();
27942
+ const chatId = randomUUID10();
27915
27943
  let chatMayExist = false;
27916
27944
  checks.push({ id: "account.session", status: "passed", duration_ms: 0 });
27917
27945
  const createStarted = now();
@@ -28042,6 +28070,7 @@ var HEALTH_REQUEST_TIMEOUT_MS = 5e3;
28042
28070
  var CHECKSUM_BUFFER_BYTES = 1024 * 1024;
28043
28071
  var ENV_BACKUP_PREFIX = ".env.openmates-backup-";
28044
28072
  var ENV_BACKUP_RETENTION_COUNT = 5;
28073
+ var BACKUP_FORMAT_VERSION = 2;
28045
28074
  var IMAGE_CHANNEL_TAGS = {
28046
28075
  stable: MAIN_BRANCH,
28047
28076
  main: MAIN_BRANCH,
@@ -28268,7 +28297,7 @@ function beginEngineeringRuntimeOperation(installPath, operationType, services)
28268
28297
  "dev-stack"
28269
28298
  ];
28270
28299
  for (const service of services) args.push("--service", service);
28271
- const output = execFileSync3("python3", args, {
28300
+ const output = execFileSync4("python3", args, {
28272
28301
  cwd: installPath,
28273
28302
  encoding: "utf-8",
28274
28303
  stdio: ["ignore", "pipe", "pipe"]
@@ -28279,7 +28308,7 @@ function beginEngineeringRuntimeOperation(installPath, operationType, services)
28279
28308
  }
28280
28309
  function finishEngineeringRuntimeOperation(installPath, operationKey, status) {
28281
28310
  const manager = join7(installPath, "scripts", "engineering_control_plane.py");
28282
- execFileSync3(
28311
+ execFileSync4(
28283
28312
  "python3",
28284
28313
  [manager, "operation", "update", "--operation-key", operationKey, "--status", status],
28285
28314
  { cwd: installPath, stdio: ["ignore", "ignore", "pipe"] }
@@ -28939,13 +28968,13 @@ function targetSourceLinks(imageTag, templateRef) {
28939
28968
  };
28940
28969
  }
28941
28970
  function installedImageMetadata(input) {
28942
- const containerId = execFileSync3(
28971
+ const containerId = execFileSync4(
28943
28972
  "docker",
28944
28973
  [...composeArgs(input.installPath, input.withOverrides, "image", input.role), "ps", "-q", ROLE_PROVENANCE_SERVICE[input.role]],
28945
28974
  { cwd: input.installPath, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
28946
28975
  ).trim();
28947
28976
  if (!containerId) throw new Error("updated_container_unavailable");
28948
- const labels = JSON.parse(execFileSync3(
28977
+ const labels = JSON.parse(execFileSync4(
28949
28978
  "docker",
28950
28979
  ["inspect", "--format", "{{json .Config.Labels}}", containerId],
28951
28980
  { cwd: input.installPath, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
@@ -29012,9 +29041,9 @@ function readEnvContent(installPath) {
29012
29041
  return existsSync9(envPath) ? readFileSync7(envPath, "utf-8") : "";
29013
29042
  }
29014
29043
  function pruneEnvBackups(installPath) {
29015
- const backups = readdirSync3(installPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith(ENV_BACKUP_PREFIX)).map((entry) => entry.name).sort();
29044
+ const backups = readdirSync4(installPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith(ENV_BACKUP_PREFIX)).map((entry) => entry.name).sort();
29016
29045
  for (const backup of backups.slice(0, Math.max(0, backups.length - ENV_BACKUP_RETENTION_COUNT))) {
29017
- rmSync5(join7(installPath, backup), { force: true });
29046
+ rmSync6(join7(installPath, backup), { force: true });
29018
29047
  }
29019
29048
  }
29020
29049
  function backupEnvFile(installPath) {
@@ -29022,7 +29051,7 @@ function backupEnvFile(installPath) {
29022
29051
  if (!existsSync9(envPath)) return null;
29023
29052
  const backupPath = join7(installPath, `${ENV_BACKUP_PREFIX}${nowStamp()}`);
29024
29053
  copyFileSync2(envPath, backupPath);
29025
- chmodSync4(backupPath, 384);
29054
+ chmodSync5(backupPath, 384);
29026
29055
  pruneEnvBackups(installPath);
29027
29056
  return backupPath;
29028
29057
  }
@@ -29030,7 +29059,7 @@ function writeEnvContent(installPath, content) {
29030
29059
  const envPath = envPathForInstall(installPath);
29031
29060
  mkdirSync6(dirname6(envPath), { recursive: true });
29032
29061
  writeFileSync5(envPath, content, { mode: 384 });
29033
- chmodSync4(envPath, 384);
29062
+ chmodSync5(envPath, 384);
29034
29063
  }
29035
29064
  function writeDeploymentModeEnv(installPath, deploymentMode, overlayPath) {
29036
29065
  let content = readEnvContent(installPath);
@@ -29203,7 +29232,7 @@ for item in checks:
29203
29232
  print(json.dumps({"ok": True, "presence": presence}))
29204
29233
  `;
29205
29234
  try {
29206
- const output = execFileSync3(
29235
+ const output = execFileSync4(
29207
29236
  "docker",
29208
29237
  ["exec", "-e", `OPENMATES_SECRET_CHECKS=${JSON.stringify(checks)}`, container, "python", "-c", script],
29209
29238
  { encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
@@ -29258,7 +29287,7 @@ function hashFile(path2) {
29258
29287
  function writeChecksums(rootDir) {
29259
29288
  const lines = [];
29260
29289
  const walk = (dir) => {
29261
- for (const entry of readdirSync3(dir, { withFileTypes: true })) {
29290
+ for (const entry of readdirSync4(dir, { withFileTypes: true })) {
29262
29291
  const path2 = join7(dir, entry.name);
29263
29292
  if (entry.isDirectory()) {
29264
29293
  walk(path2);
@@ -29296,19 +29325,20 @@ function createServerBackup(installPath, role, options = {}) {
29296
29325
  const backupDir = roleBackupDir(installPath, role);
29297
29326
  mkdirSync6(backupDir, { recursive: true, mode: 448 });
29298
29327
  const archivePath = options.output ? resolve6(options.output) : join7(backupDir, options.preUpdate ? `latest-pre-update-${role}.tar.gz` : `openmates-${role}-${nowStamp()}.tar.gz`);
29328
+ mkdirSync6(dirname6(archivePath), { recursive: true, mode: 448 });
29299
29329
  const tempDir = mkdtempSync(join7(backupDir, ".tmp-"));
29300
29330
  const env = readEnvMap(installPath);
29301
29331
  const manifest = {
29302
29332
  role,
29333
+ backup_format_version: BACKUP_FORMAT_VERSION,
29303
29334
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
29304
29335
  cli_version: getPackageVersion(),
29305
29336
  image_tag: getEnvVar(existsSync9(join7(installPath, ".env")) ? readFileSync7(join7(installPath, ".env"), "utf-8") : "", "OPENMATES_IMAGE_TAG"),
29306
- include_observability: options.includeObservability === true,
29307
- contents: plan.contents
29337
+ recovery_scope: role === "core" ? "database-and-runtime-only" : "runtime-only",
29338
+ include_observability: false,
29339
+ contents: plan.contents.filter((content) => content !== "runtime-env" || existsSync9(join7(installPath, ".env"))).filter((content) => content !== "runtime-config" || existsSync9(join7(installPath, "config")))
29308
29340
  };
29309
29341
  try {
29310
- writeFileSync5(join7(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
29311
- `);
29312
29342
  copyIfExists(join7(installPath, ".env"), join7(tempDir, "runtime", ".env"));
29313
29343
  copyIfExists(join7(installPath, "config"), join7(tempDir, "runtime", "config"));
29314
29344
  if (role === "core") {
@@ -29329,19 +29359,13 @@ function createServerBackup(installPath, role, options = {}) {
29329
29359
  closeSync2(dumpFile);
29330
29360
  }
29331
29361
  }
29332
- for (const item of [
29333
- [join7(installPath, "backend", "core", "uploads"), join7(tempDir, "directus-uploads")],
29334
- [join7(installPath, "backend", "core", "extensions"), join7(tempDir, "directus-extensions")],
29335
- [join7(installPath, "backend", role, "vault"), join7(tempDir, `${role}-vault-config`)]
29336
- ]) {
29337
- copyIfExists(item[0], item[1]);
29338
- }
29362
+ writeFileSync5(join7(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
29363
+ `, { mode: 384 });
29339
29364
  writeChecksums(tempDir);
29340
- execSync(`tar -czf ${shellQuote(archivePath)} -C ${shellQuote(tempDir)} .`, { stdio: "pipe" });
29341
- chmodSync4(archivePath, plan.fileMode);
29365
+ publishServerBackupArchive(tempDir, archivePath);
29342
29366
  return archivePath;
29343
29367
  } finally {
29344
- rmSync5(tempDir, { recursive: true, force: true });
29368
+ rmSync6(tempDir, { recursive: true, force: true });
29345
29369
  }
29346
29370
  }
29347
29371
  function restoreServerBackup(installPath, role, file) {
@@ -29357,19 +29381,30 @@ function restoreServerBackup(installPath, role, file) {
29357
29381
  if (!existsSync9(manifestPath)) throw new Error("Backup archive is missing manifest.json.");
29358
29382
  const manifest = JSON.parse(readFileSync7(manifestPath, "utf-8"));
29359
29383
  if (manifest.role !== role) throw new Error(`Backup role '${manifest.role}' does not match requested role '${role}'.`);
29384
+ if (role === "core" && manifest.backup_format_version === BACKUP_FORMAT_VERSION && manifest.recovery_scope !== "full-core") {
29385
+ throw new Error("This backup records database and runtime state only; refusing unsafe full core restore.");
29386
+ }
29360
29387
  copyIfExists(join7(tempDir, "runtime", ".env"), join7(installPath, ".env"));
29361
29388
  copyIfExists(join7(tempDir, "runtime", "config"), join7(installPath, "config"));
29362
29389
  const postgresDump = join7(tempDir, "postgres.sql");
29363
29390
  if (role === "core" && existsSync9(postgresDump)) {
29364
29391
  const databaseUser = env.DATABASE_USERNAME || "directus";
29365
29392
  const databaseName = env.DATABASE_NAME || "directus";
29366
- execSync(`docker exec -i cms-database psql -v ON_ERROR_STOP=1 -U ${shellQuote(databaseUser)} ${shellQuote(databaseName)}`, {
29367
- input: readFileSync7(postgresDump),
29368
- stdio: ["pipe", "pipe", "pipe"]
29369
- });
29393
+ const dumpFile = openSync2(postgresDump, "r");
29394
+ try {
29395
+ const result = spawnSync(
29396
+ "docker",
29397
+ ["exec", "-i", "cms-database", "psql", "-v", "ON_ERROR_STOP=1", "-U", databaseUser, databaseName],
29398
+ { encoding: "utf-8", stdio: [dumpFile, "pipe", "pipe"] }
29399
+ );
29400
+ if (result.error) throw result.error;
29401
+ if (result.status !== 0) throw new Error(result.stderr || `psql exited with status ${result.status}`);
29402
+ } finally {
29403
+ closeSync2(dumpFile);
29404
+ }
29370
29405
  }
29371
29406
  } finally {
29372
- rmSync5(tempDir, { recursive: true, force: true });
29407
+ rmSync6(tempDir, { recursive: true, force: true });
29373
29408
  }
29374
29409
  }
29375
29410
  function restoreStopServices(installPath, withOverrides, installMode, role) {
@@ -29992,7 +30027,7 @@ async function runUpdateCompletionEmailGate(input) {
29992
30027
  deliveryId: deliveryPlan.deliveryId
29993
30028
  };
29994
30029
  }
29995
- const deliveryId = deliveryPlan.deliveryId ?? randomUUID10();
30030
+ const deliveryId = deliveryPlan.deliveryId ?? randomUUID11();
29996
30031
  const deliveryPendingAt = deliveryPlan.pendingAt ?? completedAt;
29997
30032
  const previousAttempts = deliveryPlan.previousAttempts ?? 0;
29998
30033
  const { role: _role, updated_at: _updatedAt, ...currentStatus } = readServerUpdateStatus(input.installPath, input.role);
@@ -30131,18 +30166,18 @@ async function installRuntimeMonitoringServices(installPath, role) {
30131
30166
  const installedOperationalUnits = operationalUnitNames.filter((name) => existsSync9(join7("/etc", "systemd", "system", name)));
30132
30167
  const installedOperationalTimers = installedOperationalUnits.filter((name) => name.endsWith(".timer"));
30133
30168
  if (installedOperationalTimers.length) {
30134
- execFileSync3("systemctl", ["disable", "--now", ...installedOperationalTimers], { stdio: "pipe" });
30169
+ execFileSync4("systemctl", ["disable", "--now", ...installedOperationalTimers], { stdio: "pipe" });
30135
30170
  }
30136
- for (const name of installedOperationalUnits) rmSync5(join7("/etc", "systemd", "system", name));
30171
+ for (const name of installedOperationalUnits) rmSync6(join7("/etc", "systemd", "system", name));
30137
30172
  for (const path2 of [reportStatePath, startedMetricPath, successMetricPath]) {
30138
- if (existsSync9(path2)) rmSync5(path2);
30173
+ if (existsSync9(path2)) rmSync6(path2);
30139
30174
  }
30140
30175
  }
30141
30176
  for (const [name, content] of files) writeFileSync5(join7("/etc", "systemd", "system", name), content, { mode: 420 });
30142
30177
  execSync("systemctl daemon-reload", { stdio: "pipe" });
30143
30178
  const timers = files.filter(([name]) => name.endsWith(".timer")).map(([name]) => name);
30144
30179
  execSync(`systemctl enable --now ${timers.map(shellQuote).join(" ")}`, { stdio: "pipe" });
30145
- for (const timer of timers) execFileSync3("systemctl", ["is-active", "--quiet", timer], { stdio: "pipe" });
30180
+ for (const timer of timers) execFileSync4("systemctl", ["is-active", "--quiet", timer], { stdio: "pipe" });
30146
30181
  }
30147
30182
  async function autoInstallRuntimeMonitoringServices(installPath, role) {
30148
30183
  if (!shouldAutoInstallRuntimeMonitoringServices(process.env)) {
@@ -31145,7 +31180,7 @@ async function serverBackup(rest, flags) {
31145
31180
  const role = getServerRole(flags, config);
31146
31181
  if (action === "list") {
31147
31182
  const dir = roleBackupDir(installPath, role);
31148
- const files = existsSync9(dir) ? readdirSync3(dir).filter((item) => item.endsWith(".tar.gz")).sort() : [];
31183
+ const files = existsSync9(dir) ? readdirSync4(dir).filter((item) => item.endsWith(".tar.gz")).sort() : [];
31149
31184
  if (flags.json === true) {
31150
31185
  printJson({ role, backupDir: dir, files });
31151
31186
  return;
@@ -31240,7 +31275,7 @@ async function serverUninstall(flags) {
31240
31275
  } catch {
31241
31276
  }
31242
31277
  try {
31243
- rmSync5(installPath, { recursive: true, force: true });
31278
+ rmSync6(installPath, { recursive: true, force: true });
31244
31279
  console.error(` Removed installation directory: ${installPath}`);
31245
31280
  } catch (e) {
31246
31281
  console.error(` Could not remove ${installPath}: ${e instanceof Error ? e.message : e}`);
@@ -70488,8 +70523,8 @@ function buildAssistantFeedbackDecision(rating) {
70488
70523
  }
70489
70524
 
70490
70525
  // src/benchmark.ts
70491
- import { createHash as createHash13, randomUUID as randomUUID11 } from "crypto";
70492
- import { existsSync as existsSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, readdirSync as readdirSync4, writeFileSync as writeFileSync6 } from "fs";
70526
+ import { createHash as createHash13, randomUUID as randomUUID12 } from "crypto";
70527
+ import { existsSync as existsSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, readdirSync as readdirSync5, writeFileSync as writeFileSync6 } from "fs";
70493
70528
  import { tmpdir } from "os";
70494
70529
  import { dirname as dirname7, join as join8, resolve as resolve8 } from "path";
70495
70530
  import { fileURLToPath } from "url";
@@ -70904,7 +70939,7 @@ async function handleBenchmark(client, subcommand, rest, flags) {
70904
70939
  const caseIds = parseCaseIds(flags.case);
70905
70940
  const dryRun = flags["dry-run"] === true;
70906
70941
  const output = typeof flags.output === "string" ? flags.output : void 0;
70907
- const runId = typeof flags["run-id"] === "string" ? flags["run-id"] : randomUUID11();
70942
+ const runId = typeof flags["run-id"] === "string" ? flags["run-id"] : randomUUID12();
70908
70943
  const imagePath = typeof flags.image === "string" ? resolve8(flags.image) : defaultImageFixturePath();
70909
70944
  if (!dryRun && flags["confirm-spend-credits"] !== true) {
70910
70945
  throw new Error(
@@ -71006,7 +71041,7 @@ async function handlePromptBudgetBenchmark(client, rest, flags) {
71006
71041
  if (resumeArtifact) {
71007
71042
  validatePromptBudgetResumeArtifact(resumeArtifact, { phase, targetModel, judgeModel });
71008
71043
  }
71009
- const runId = stringFlag(flags["run-id"]) ?? resumeArtifact?.runId ?? randomUUID11();
71044
+ const runId = stringFlag(flags["run-id"]) ?? resumeArtifact?.runId ?? randomUUID12();
71010
71045
  const result = makePromptBudgetBaseResult({
71011
71046
  phase,
71012
71047
  runId,
@@ -71567,7 +71602,7 @@ function buildLongContextHistory() {
71567
71602
  }
71568
71603
  function appendHistory(history, role, content) {
71569
71604
  history.push({
71570
- message_id: randomUUID11(),
71605
+ message_id: randomUUID12(),
71571
71606
  role,
71572
71607
  sender_name: role === "user" ? "User" : "Assistant",
71573
71608
  content,
@@ -71657,7 +71692,7 @@ function loadProviderPricing() {
71657
71692
  const providersDir = findProvidersDir();
71658
71693
  const pricing = /* @__PURE__ */ new Map();
71659
71694
  if (!providersDir) return pricing;
71660
- for (const fileName of readdirSync4(providersDir)) {
71695
+ for (const fileName of readdirSync5(providersDir)) {
71661
71696
  if (!fileName.endsWith(".yml")) continue;
71662
71697
  const filePath = join8(providersDir, fileName);
71663
71698
  const text = readFileSync8(filePath, "utf-8");
@@ -73434,7 +73469,7 @@ function clamp(value, min, max) {
73434
73469
  // src/remoteAccess.ts
73435
73470
  import { homedir as homedir7 } from "os";
73436
73471
  import {
73437
- chmodSync as chmodSync5,
73472
+ chmodSync as chmodSync6,
73438
73473
  closeSync as closeSync3,
73439
73474
  constants,
73440
73475
  existsSync as existsSync11,
@@ -73442,7 +73477,7 @@ import {
73442
73477
  mkdirSync as mkdirSync7,
73443
73478
  openSync as openSync3,
73444
73479
  readFileSync as readFileSync9,
73445
- readdirSync as readdirSync5,
73480
+ readdirSync as readdirSync6,
73446
73481
  readSync as readSync2,
73447
73482
  realpathSync,
73448
73483
  statSync as statSync2,
@@ -73586,7 +73621,7 @@ function discoverRemoteAccessRepositories(roots) {
73586
73621
  const visit = (directory, approvedRoot) => {
73587
73622
  let entries;
73588
73623
  try {
73589
- entries = readdirSync5(directory, { withFileTypes: true });
73624
+ entries = readdirSync6(directory, { withFileTypes: true });
73590
73625
  } catch (error) {
73591
73626
  const code = error.code;
73592
73627
  if (code === "EACCES" || code === "EPERM") {
@@ -73624,7 +73659,7 @@ function listRemoteAccessDirectory(options) {
73624
73659
  const entries = [];
73625
73660
  let omitted = 0;
73626
73661
  let excluded = 0;
73627
- for (const entry of readdirSync5(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
73662
+ for (const entry of readdirSync6(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
73628
73663
  const entryPath = relative2(root, join9(directory, entry.name)).replace(/\\/g, "/");
73629
73664
  if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, entryPath) || classifyProjectFileRisk(entryPath, options.userProtectedPatterns ?? []).isHighRisk || entry.isFile() && isBinaryFile(join9(directory, entry.name))) {
73630
73665
  excluded += 1;
@@ -73972,7 +74007,7 @@ function searchRemoteSourceWithoutRg(query, sourceRoot, maxResults, userProtecte
73972
74007
  const directory = directories.pop();
73973
74008
  let entries;
73974
74009
  try {
73975
- entries = readdirSync5(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
74010
+ entries = readdirSync6(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
73976
74011
  } catch {
73977
74012
  excluded += 1;
73978
74013
  continue;
@@ -74185,10 +74220,10 @@ function saveRemoteAccessSources(sources, homeDirectory) {
74185
74220
  const filePath = remoteAccessStorePath(homeDirectory);
74186
74221
  const stateDir2 = join9(homeDirectory, ".openmates");
74187
74222
  mkdirSync7(stateDir2, { recursive: true, mode: 448 });
74188
- chmodSync5(stateDir2, 448);
74223
+ chmodSync6(stateDir2, 448);
74189
74224
  writeFileSync7(filePath, `${JSON.stringify({ sources }, null, 2)}
74190
74225
  `, { mode: 384 });
74191
- chmodSync5(filePath, 384);
74226
+ chmodSync6(filePath, 384);
74192
74227
  }
74193
74228
  function assertRemoteAccessSourceRecord(value, index) {
74194
74229
  if (typeof value !== "object" || value === null) {
@@ -74206,7 +74241,7 @@ function assertRemoteAccessSourceRecord(value, index) {
74206
74241
  }
74207
74242
 
74208
74243
  // src/projectRequester.ts
74209
- import { randomUUID as randomUUID12 } from "crypto";
74244
+ import { randomUUID as randomUUID13 } from "crypto";
74210
74245
  var REMOTE_PROTOCOL_TIMEOUT_MS = 45e3;
74211
74246
  var REMOTE_POLL_INTERVAL_MS = 250;
74212
74247
  var MAX_REMOTE_RESULT_BYTES = 200 * 1024;
@@ -74232,8 +74267,8 @@ async function requestProjectRemoteOperation(options) {
74232
74267
  keyEpoch = discovery.keyEpoch;
74233
74268
  routingIdentity = discovery.routingIdentity;
74234
74269
  }
74235
- const requestingClientId = randomUUID12();
74236
- const requestId = randomUUID12();
74270
+ const requestingClientId = randomUUID13();
74271
+ const requestId = randomUUID13();
74237
74272
  const identity = await buildIdentity(
74238
74273
  options.client,
74239
74274
  options.projectId,
@@ -74294,9 +74329,9 @@ async function requestProjectRemoteOperation(options) {
74294
74329
  return response.result;
74295
74330
  }
74296
74331
  async function discoverRouting(options, timeoutMs) {
74297
- const requestId = randomUUID12();
74298
- const requestingClientId = randomUUID12();
74299
- const nonce = randomUUID12();
74332
+ const requestId = randomUUID13();
74333
+ const requestingClientId = randomUUID13();
74334
+ const nonce = randomUUID13();
74300
74335
  const encryptedEnvelope = await encryptWithAesGcmCombined(JSON.stringify({
74301
74336
  type: "routing_discovery",
74302
74337
  requesting_client_id: requestingClientId,
@@ -74507,7 +74542,7 @@ function commandName(name) {
74507
74542
  }
74508
74543
 
74509
74544
  // src/workRecovery.ts
74510
- import { mkdirSync as mkdirSync8, renameSync as renameSync2, writeFileSync as writeFileSync8 } from "fs";
74545
+ import { mkdirSync as mkdirSync8, renameSync as renameSync3, writeFileSync as writeFileSync8 } from "fs";
74511
74546
  import { dirname as dirname8 } from "path";
74512
74547
  import { stringify } from "yaml";
74513
74548
  async function projectPlanAssumption(plan, masterKey, assumption) {
@@ -74690,13 +74725,13 @@ function writeWorkRecoveryAtomically(path2, document) {
74690
74725
  mkdirSync8(dirname8(path2), { recursive: true, mode: 448 });
74691
74726
  const temporaryPath = `${path2}.tmp`;
74692
74727
  writeFileSync8(temporaryPath, stringify(document, { sortMapEntries: true, lineWidth: 0 }), { encoding: "utf8", mode: 384 });
74693
- renameSync2(temporaryPath, path2);
74728
+ renameSync3(temporaryPath, path2);
74694
74729
  }
74695
74730
 
74696
74731
  // src/revolutBusinessCertificate.ts
74697
- import { execFileSync as execFileSync4 } from "child_process";
74698
- import { createSign, randomUUID as randomUUID13 } from "crypto";
74699
- import { chmodSync as chmodSync6, existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync11 } from "fs";
74732
+ import { execFileSync as execFileSync5 } from "child_process";
74733
+ import { createSign, randomUUID as randomUUID14 } from "crypto";
74734
+ import { chmodSync as chmodSync7, existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync11 } from "fs";
74700
74735
  import { homedir as homedir8 } from "os";
74701
74736
  import { join as join10, resolve as resolve10 } from "path";
74702
74737
  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";
@@ -74769,7 +74804,7 @@ function generateRevolutBusinessClientAssertion(options) {
74769
74804
  aud: REVOLUT_BUSINESS_AUDIENCE,
74770
74805
  exp: now + 3600,
74771
74806
  iat: now,
74772
- jti: randomUUID13()
74807
+ jti: randomUUID14()
74773
74808
  });
74774
74809
  const unsigned = `${header2}.${payload}`;
74775
74810
  const signer = createSign("RSA-SHA256");
@@ -74785,16 +74820,16 @@ function generateRevolutBusinessCertificate(options = {}) {
74785
74820
  if (!title) throw new Error("Revolut certificate title must not be empty.");
74786
74821
  if (!redirectUri.startsWith("https://")) throw new Error("Revolut OAuth redirect URI must start with https://.");
74787
74822
  mkdirSync9(outputDir, { recursive: true, mode: 448 });
74788
- chmodSync6(outputDir, 448);
74823
+ chmodSync7(outputDir, 448);
74789
74824
  const privateKeyPath = join10(outputDir, "privatecert.pem");
74790
74825
  const publicCertificatePath = join10(outputDir, "publiccert.cer");
74791
74826
  if (!options.overwrite && (existsSync12(privateKeyPath) || existsSync12(publicCertificatePath))) {
74792
74827
  throw new Error(`Revolut certificate files already exist in ${outputDir}. Re-run with --overwrite to replace them.`);
74793
74828
  }
74794
74829
  try {
74795
- execFileSync4("openssl", ["genrsa", "-out", privateKeyPath, RSA_BITS], { stdio: "ignore" });
74796
- chmodSync6(privateKeyPath, 384);
74797
- execFileSync4(
74830
+ execFileSync5("openssl", ["genrsa", "-out", privateKeyPath, RSA_BITS], { stdio: "ignore" });
74831
+ chmodSync7(privateKeyPath, 384);
74832
+ execFileSync5(
74798
74833
  "openssl",
74799
74834
  [
74800
74835
  "req",
@@ -76081,7 +76116,7 @@ async function resolveAddToProjectStorageChoice(client, project, flags, objectTy
76081
76116
  async function createEncryptedProjectItem(client, project, input) {
76082
76117
  const timestamp = nowSeconds3();
76083
76118
  return client.createProjectItem(project.projectId, {
76084
- project_item_id: randomUUID14(),
76119
+ project_item_id: randomUUID15(),
76085
76120
  folder_id: input.folderId ?? null,
76086
76121
  item_type: input.itemType,
76087
76122
  target_id: input.targetId,
@@ -76275,7 +76310,7 @@ async function handlePlans(client, subcommand, rest, flags, redactor) {
76275
76310
  const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
76276
76311
  if (action === "create" || action === "add") {
76277
76312
  const assumption = await client.createPlanAssumption(plan.planId, {
76278
- assumption_id: typeof flags.id === "string" ? flags.id : randomUUID14(),
76313
+ assumption_id: typeof flags.id === "string" ? flags.id : randomUUID15(),
76279
76314
  encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
76280
76315
  status: typeof flags.status === "string" ? flags.status : void 0,
76281
76316
  linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
@@ -77200,7 +77235,7 @@ async function handleProjects(client, subcommand, rest, flags, redactor) {
77200
77235
  const name = requiredStringFlag(rest[0] ?? flags.name, "project name");
77201
77236
  const projectKey = randomBytes9(32);
77202
77237
  const wrapping = await client.projectWrappingKey(context);
77203
- const projectId = randomUUID14();
77238
+ const projectId = randomUUID15();
77204
77239
  const timestamp = nowSeconds3();
77205
77240
  const slugMetadata = await buildEncryptedObjectSlugMetadata({
77206
77241
  value: typeof flags.slug === "string" ? flags.slug : name,
@@ -77308,7 +77343,7 @@ async function handleProjects(client, subcommand, rest, flags, redactor) {
77308
77343
  const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client.planProjectAsk({ instruction }), "proposed_project");
77309
77344
  const projectKey = randomBytes9(32);
77310
77345
  const timestamp = Math.floor(Date.now() / 1e3);
77311
- const projectId = randomUUID14();
77346
+ const projectId = randomUUID15();
77312
77347
  const name = requiredString(proposal, "name", instruction);
77313
77348
  const slugMetadata = await buildEncryptedObjectSlugMetadata({
77314
77349
  value: typeof flags.slug === "string" ? flags.slug : name,
@@ -78207,7 +78242,7 @@ async function handleRemoteAccess(client, subcommand, rest, flags) {
78207
78242
  const projects = await loadProjects(client, masterKey, hostingFlags, hostingContext);
78208
78243
  const bindings = await resolveRemoteAccessBindings(client, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
78209
78244
  if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
78210
- const sourceSessionId = randomUUID14();
78245
+ const sourceSessionId = randomUUID15();
78211
78246
  const controller = new AbortController();
78212
78247
  const stop = () => controller.abort();
78213
78248
  process.once("SIGINT", stop);
@@ -78315,7 +78350,7 @@ async function resolveRemoteAccessBindings(client, masterKey, projects, candidat
78315
78350
  if (answer !== "y" && answer !== "yes") throw new Error("Remote access Project creation cancelled.");
78316
78351
  for (const rootPath of unresolved) {
78317
78352
  const project = await createEncryptedRemoteAccessProject(client, masterKey, basename3(rootPath), context);
78318
- resolved.push({ rootPath, project, sourceId: randomUUID14() });
78353
+ resolved.push({ rootPath, project, sourceId: randomUUID15() });
78319
78354
  }
78320
78355
  }
78321
78356
  const bindings = [];
@@ -78352,7 +78387,7 @@ async function resolveRemoteAccessBindings(client, masterKey, projects, candidat
78352
78387
  }
78353
78388
  async function createEncryptedRemoteAccessProject(client, masterKey, name, context) {
78354
78389
  const projectKey = randomBytes9(32);
78355
- const projectId = randomUUID14();
78390
+ const projectId = randomUUID15();
78356
78391
  const timestamp = Math.floor(Date.now() / 1e3);
78357
78392
  const wrapping = await client.projectWrappingKey(context);
78358
78393
  const payload = {
@@ -81650,7 +81685,7 @@ async function runAccountImport(client, parserFormat, file, flags) {
81650
81685
  if (flags.yes !== true) {
81651
81686
  await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
81652
81687
  }
81653
- const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID14();
81688
+ const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID15();
81654
81689
  const selectedChats = parsed.chats.slice(0, selectedCount);
81655
81690
  const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
81656
81691
  const confirmation = await client.confirmAccountImport(importId, selectedFingerprints);
@@ -82144,8 +82179,8 @@ async function handleFinance(client, subcommand, _rest, flags) {
82144
82179
  refresh_token_envelope: connectedAccount.refreshTokenBundle
82145
82180
  }
82146
82181
  ] : [],
82147
- chatId: `cli-finance-${randomUUID14()}`,
82148
- messageId: `cli-finance-message-${randomUUID14()}`,
82182
+ chatId: `cli-finance-${randomUUID15()}`,
82183
+ messageId: `cli-finance-message-${randomUUID15()}`,
82149
82184
  apiKey: resolveApiKey(flags) ?? void 0,
82150
82185
  promptInjectionProtection: false
82151
82186
  });
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  resolveProjectContext,
12
12
  serializeToYaml,
13
13
  shouldRequireTrustedAccountGuard
14
- } from "./chunk-RLOO6QKC.js";
14
+ } from "./chunk-XKIFO4JO.js";
15
15
  import "./chunk-IWKP55ZB.js";
16
16
  import "./chunk-BBKJZ23O.js";
17
17
  export {
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  selectAssistantMessagesForSpeech,
43
43
  serializeToYaml,
44
44
  summarizeAssistantSpeech
45
- } from "./chunk-RLOO6QKC.js";
45
+ } from "./chunk-XKIFO4JO.js";
46
46
  import "./chunk-IWKP55ZB.js";
47
47
  import "./chunk-BBKJZ23O.js";
48
48
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmates",
3
- "version": "0.18.0-alpha.0",
3
+ "version": "0.18.0-alpha.2",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",