openmates 0.11.0-alpha.16 → 0.11.0-alpha.18

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.
@@ -23,6 +23,49 @@ function getUploadOrigin(apiUrl) {
23
23
  }
24
24
  return "https://app.openmates.org";
25
25
  }
26
+ function getUploadMimeType(filename) {
27
+ const ext = extname(filename).toLowerCase();
28
+ switch (ext) {
29
+ case ".jpg":
30
+ case ".jpeg":
31
+ return "image/jpeg";
32
+ case ".png":
33
+ return "image/png";
34
+ case ".webp":
35
+ return "image/webp";
36
+ case ".gif":
37
+ return "image/gif";
38
+ case ".heic":
39
+ return "image/heic";
40
+ case ".heif":
41
+ return "image/heif";
42
+ case ".bmp":
43
+ return "image/bmp";
44
+ case ".tif":
45
+ case ".tiff":
46
+ return "image/tiff";
47
+ case ".svg":
48
+ return "image/svg+xml";
49
+ case ".pdf":
50
+ return "application/pdf";
51
+ case ".mp3":
52
+ return "audio/mpeg";
53
+ case ".m4a":
54
+ case ".mp4":
55
+ return "audio/mp4";
56
+ case ".wav":
57
+ return "audio/wav";
58
+ case ".webm":
59
+ return "audio/webm";
60
+ case ".ogg":
61
+ case ".oga":
62
+ return "audio/ogg";
63
+ case ".aac":
64
+ return "audio/aac";
65
+ default:
66
+ return "application/octet-stream";
67
+ }
68
+ }
26
69
  async function uploadFile(filePath, session) {
27
70
  const filename = basename(filePath);
28
71
  const fileBytes = readFileSync(filePath);
@@ -36,7 +79,7 @@ async function uploadFile(filePath, session) {
36
79
  let lastError;
37
80
  for (let attempt = 1; attempt <= UPLOAD_MAX_ATTEMPTS; attempt++) {
38
81
  try {
39
- const blob = new Blob([fileBytes]);
82
+ const blob = new Blob([fileBytes], { type: getUploadMimeType(filename) });
40
83
  const formData = new FormData();
41
84
  formData.append("file", blob, filename);
42
85
  response = await fetch(uploadUrl, {
@@ -89,6 +132,73 @@ async function uploadFile(filePath, session) {
89
132
  const data = await response.json();
90
133
  return data;
91
134
  }
135
+ async function transcribeUploadedAudio(uploadResult, filename, session, options = {}) {
136
+ const s3Key = uploadResult.files?.original?.s3_key ?? Object.values(uploadResult.files ?? {})[0]?.s3_key;
137
+ if (!s3Key) {
138
+ throw new Error("Upload succeeded but no audio file key was returned.");
139
+ }
140
+ const cookies = [];
141
+ if (session.cookies?.auth_refresh_token) {
142
+ cookies.push(`auth_refresh_token=${session.cookies.auth_refresh_token}`);
143
+ }
144
+ const requestItem = {
145
+ id: options.requestId ?? uploadResult.embed_id,
146
+ embed_id: uploadResult.embed_id,
147
+ s3_key: s3Key,
148
+ s3_base_url: uploadResult.s3_base_url,
149
+ aes_key: uploadResult.aes_key,
150
+ aes_nonce: uploadResult.aes_nonce,
151
+ vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key,
152
+ filename,
153
+ mime_type: uploadResult.content_type
154
+ };
155
+ if (options.chatId) {
156
+ requestItem.chat_id = options.chatId;
157
+ }
158
+ const response = await fetch(
159
+ `${session.apiUrl.replace(/\/$/, "")}/v1/apps/audio/skills/transcribe`,
160
+ {
161
+ method: "POST",
162
+ headers: {
163
+ Accept: "application/json",
164
+ "Content-Type": "application/json",
165
+ ...cookies.length > 0 ? { Cookie: cookies.join("; ") } : {}
166
+ },
167
+ body: JSON.stringify({ requests: [requestItem] }),
168
+ signal: AbortSignal.timeout(10 * 60 * 1e3)
169
+ }
170
+ );
171
+ if (!response.ok) {
172
+ let detail = `Transcription failed (HTTP ${response.status}).`;
173
+ try {
174
+ const data2 = await response.json();
175
+ detail = data2.detail ?? data2.error ?? detail;
176
+ } catch {
177
+ }
178
+ throw new Error(detail);
179
+ }
180
+ const data = await response.json();
181
+ if (data.success === false) {
182
+ throw new Error(data.error ?? "Transcription failed.");
183
+ }
184
+ const requestId = options.requestId ?? uploadResult.embed_id;
185
+ const group = data.data?.results?.find((item) => item.id === requestId) ?? data.data?.results?.[0];
186
+ const result = group?.results?.[0];
187
+ if (!result) {
188
+ throw new Error(group?.error ?? "Transcription response did not include a result.");
189
+ }
190
+ if (result.error) {
191
+ throw new Error(result.error);
192
+ }
193
+ return {
194
+ transcript: result.transcript ?? null,
195
+ transcript_original: result.transcript_original ?? null,
196
+ transcript_corrected: result.transcript_corrected ?? null,
197
+ use_corrected: result.use_corrected ?? null,
198
+ correction_model: result.correction_model ?? null,
199
+ model: result.model ?? null
200
+ };
201
+ }
92
202
  function getProfileImageMime(filename) {
93
203
  const ext = extname(filename).toLowerCase();
94
204
  if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
@@ -127,5 +237,6 @@ async function uploadProfileImage(filePath, session) {
127
237
 
128
238
  export {
129
239
  uploadFile,
240
+ transcribeUploadedAudio,
130
241
  uploadProfileImage
131
242
  };
@@ -1,6 +1,7 @@
1
1
  import {
2
+ transcribeUploadedAudio,
2
3
  uploadFile
3
- } from "./chunk-SFTCIVE2.js";
4
+ } from "./chunk-AXNRPVLE.js";
4
5
 
5
6
  // src/client.ts
6
7
  import { randomUUID as randomUUID2 } from "crypto";
@@ -2687,6 +2688,15 @@ var OpenMatesClient = class _OpenMatesClient {
2687
2688
  }
2688
2689
  }
2689
2690
  }
2691
+ if (params.preparedEmbeds && params.preparedEmbeds.length > 0) {
2692
+ messagePayload.embeds = params.preparedEmbeds.map((embed) => ({
2693
+ embed_id: embed.embedId,
2694
+ type: embed.type,
2695
+ content: embed.content,
2696
+ status: embed.status,
2697
+ text_preview: embed.textPreview
2698
+ }));
2699
+ }
2690
2700
  const encryptedEmbeds = [...params.encryptedEmbeds ?? []];
2691
2701
  if (!params.incognito && params.preparedEmbeds && params.preparedEmbeds.length > 0) {
2692
2702
  const masterKey = this.getMasterKeyBytes();
@@ -3328,7 +3338,7 @@ var OpenMatesClient = class _OpenMatesClient {
3328
3338
  return this.settingsPost("user/username", { username });
3329
3339
  }
3330
3340
  async updateProfileImage(filePath) {
3331
- const { uploadProfileImage } = await import("./uploadService-3CAJXU4L.js");
3341
+ const { uploadProfileImage } = await import("./uploadService-S464XJRA.js");
3332
3342
  const result = await uploadProfileImage(filePath, this.requireSession());
3333
3343
  if (result.status === "rejected") {
3334
3344
  throw new Error(result.detail ?? "Profile image rejected by content safety checks.");
@@ -5100,6 +5110,16 @@ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
5100
5110
  "tif",
5101
5111
  "svg"
5102
5112
  ]);
5113
+ var AUDIO_EXTENSIONS = /* @__PURE__ */ new Set([
5114
+ "mp3",
5115
+ "m4a",
5116
+ "mp4",
5117
+ "wav",
5118
+ "webm",
5119
+ "ogg",
5120
+ "oga",
5121
+ "aac"
5122
+ ]);
5103
5123
  function isEnvFile(filename) {
5104
5124
  const lower = filename.toLowerCase();
5105
5125
  return lower === ".env" || lower.startsWith(".env.") || lower === ".envrc";
@@ -5120,6 +5140,9 @@ function isImageFile(filename) {
5120
5140
  function isPDFFile(filename) {
5121
5141
  return getExt(filename) === "pdf";
5122
5142
  }
5143
+ function isAudioFile(filename) {
5144
+ return AUDIO_EXTENSIONS.has(getExt(filename));
5145
+ }
5123
5146
  var LANGUAGE_MAP = {
5124
5147
  ts: "typescript",
5125
5148
  tsx: "typescript",
@@ -5264,10 +5287,14 @@ function processFiles(filePaths, redactor) {
5264
5287
  const result = processPDFFile(resolvedPath, filename);
5265
5288
  if (result) embeds.push(result);
5266
5289
  else errors.push({ path: rawPath, error: "Failed to process PDF" });
5290
+ } else if (isAudioFile(filename)) {
5291
+ const result = processAudioFile(resolvedPath, filename);
5292
+ if (result) embeds.push(result);
5293
+ else errors.push({ path: rawPath, error: "Failed to process audio" });
5267
5294
  } else {
5268
5295
  errors.push({
5269
5296
  path: rawPath,
5270
- error: `Unsupported file type: .${ext}. Supported: code/text, images, PDFs.`
5297
+ error: `Unsupported file type: .${ext}. Supported: code/text, images, PDFs, audio.`
5271
5298
  });
5272
5299
  }
5273
5300
  }
@@ -5396,6 +5423,40 @@ function processPDFFile(filePath, filename) {
5396
5423
  return null;
5397
5424
  }
5398
5425
  }
5426
+ function processAudioFile(filePath, filename) {
5427
+ try {
5428
+ const embedId = generateEmbedId();
5429
+ const embedContent = toonEncodeContent({
5430
+ app_id: "audio",
5431
+ skill_id: "transcribe",
5432
+ type: "audio-recording",
5433
+ status: "uploading",
5434
+ filename
5435
+ });
5436
+ const embed = {
5437
+ embedId,
5438
+ type: "audio-recording",
5439
+ content: embedContent,
5440
+ textPreview: filename,
5441
+ status: "processing"
5442
+ };
5443
+ return {
5444
+ embed,
5445
+ referenceBlock: createEmbedReferenceBlock("audio-recording", embedId),
5446
+ displayName: filename,
5447
+ secretsRedacted: false,
5448
+ zeroKnowledge: false,
5449
+ requiresUpload: true,
5450
+ localPath: filePath
5451
+ };
5452
+ } catch (e) {
5453
+ process.stderr.write(
5454
+ `\x1B[31mError:\x1B[0m Failed to process ${filename}: ${e instanceof Error ? e.message : String(e)}
5455
+ `
5456
+ );
5457
+ return null;
5458
+ }
5459
+ }
5399
5460
  function formatEmbedsForMessage(embeds) {
5400
5461
  if (embeds.length === 0) return "";
5401
5462
  return "\n" + embeds.map((e) => e.referenceBlock).join("\n");
@@ -6514,7 +6575,8 @@ function formatTs(ts) {
6514
6575
 
6515
6576
  // src/server.ts
6516
6577
  import { execSync, spawn as nodeSpawn } from "child_process";
6517
- import { copyFileSync, existsSync as existsSync5, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
6578
+ import { randomBytes as randomBytes2 } from "crypto";
6579
+ import { copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
6518
6580
  import { createInterface as createInterface2 } from "readline";
6519
6581
  import { homedir as homedir5 } from "os";
6520
6582
  import { join as join3, resolve as resolve3 } from "path";
@@ -6552,16 +6614,17 @@ function removeServerConfig() {
6552
6614
  rmSync2(filePath);
6553
6615
  }
6554
6616
  }
6555
- var COMPOSE_MARKER = join2("backend", "core", "docker-compose.yml");
6617
+ var SOURCE_COMPOSE_MARKER = join2("backend", "core", "docker-compose.yml");
6618
+ var IMAGE_COMPOSE_MARKER = join2("backend", "core", "docker-compose.selfhost.yml");
6556
6619
  function isOpenMatesDir(dir) {
6557
- return existsSync4(join2(dir, COMPOSE_MARKER));
6620
+ return existsSync4(join2(dir, SOURCE_COMPOSE_MARKER)) || existsSync4(join2(dir, IMAGE_COMPOSE_MARKER));
6558
6621
  }
6559
6622
  function resolveServerPath(flags) {
6560
6623
  if (typeof flags.path === "string" && flags.path) {
6561
6624
  const explicit = resolve2(flags.path);
6562
6625
  if (!isOpenMatesDir(explicit)) {
6563
6626
  throw new Error(
6564
- `${explicit} does not appear to be an OpenMates installation (missing ${COMPOSE_MARKER}).`
6627
+ `${explicit} does not appear to be an OpenMates installation (missing ${SOURCE_COMPOSE_MARKER} or ${IMAGE_COMPOSE_MARKER}).`
6565
6628
  );
6566
6629
  }
6567
6630
  return explicit;
@@ -6580,11 +6643,68 @@ function resolveServerPath(flags) {
6580
6643
  }
6581
6644
 
6582
6645
  // src/server.ts
6583
- var COMPOSE_FILE = join3("backend", "core", "docker-compose.yml");
6646
+ var SOURCE_COMPOSE_FILE = join3("backend", "core", "docker-compose.yml");
6647
+ var IMAGE_COMPOSE_FILE = join3("backend", "core", "docker-compose.selfhost.yml");
6584
6648
  var COMPOSE_OVERRIDE = join3("backend", "core", "docker-compose.override.yml");
6585
6649
  var DEFAULT_INSTALL_PATH = join3(homedir5(), "openmates");
6586
6650
  var REPO_URL = "https://github.com/glowingkitty/OpenMates.git";
6587
6651
  var DEV_BRANCH = "dev";
6652
+ var DEFAULT_IMAGE_REGISTRY = "ghcr.io/glowingkitty";
6653
+ var MINIMAL_ENV_TEMPLATE = `# OpenMates self-host image-mode environment
6654
+ SECRET__MISTRAL_AI__API_KEY=
6655
+ SECRET__CEREBRAS__API_KEY=
6656
+ SECRET__GROQ__API_KEY=
6657
+ SECRET__OPENAI__API_KEY=
6658
+ SECRET__ANTHROPIC__API_KEY=
6659
+ SECRET__GOOGLE_AI_STUDIO__API_KEY=
6660
+ SECRET__OPENROUTER__API_KEY=
6661
+ SECRET__TOGETHER__API_KEY=
6662
+ SECRET__BRAVE__API_KEY=
6663
+ SECRET__FIRECRAWL__API_KEY=
6664
+ SECRET__CONTEXT7__API_KEY=
6665
+ DATABASE_ADMIN_EMAIL=admin@example.com
6666
+ DATABASE_ADMIN_PASSWORD=
6667
+ DATABASE_NAME=directus
6668
+ DATABASE_USERNAME=directus
6669
+ DATABASE_PASSWORD=
6670
+ DIRECTUS_TOKEN=
6671
+ DIRECTUS_SECRET=
6672
+ DRAGONFLY_PASSWORD=
6673
+ OPENOBSERVE_ROOT_EMAIL=admin@openmates.internal
6674
+ OPENOBSERVE_ROOT_PASSWORD=
6675
+ INTERNAL_API_SHARED_TOKEN=
6676
+ TUNNEL_TRIGGER_SECRET=<PLACEHOLDER>
6677
+ SERVER_ENVIRONMENT=production
6678
+ FRONTEND_URLS="http://localhost:5173"
6679
+ PRODUCTION_URL="http://localhost:5173"
6680
+ TRUSTED_PROXY_IPS="172.16.0.0/12"
6681
+ CORE_SIDECAR_URL=http://admin-sidecar:8001
6682
+ CLEAR_CACHE_ON_UPDATE=true
6683
+ SIGNUP_LIMIT=20
6684
+ SELF_HOST_SIGNUP_MODE=invite_only
6685
+ SELF_HOST_SIGNUP_ALLOWED_DOMAINS=
6686
+ SELF_HOST_FIRST_INVITE_CODE=
6687
+ APPLICATION_PREVIEW_ORIGIN=
6688
+ OPENMATES_IMAGE_REGISTRY=${DEFAULT_IMAGE_REGISTRY}
6689
+ OPENMATES_IMAGE_TAG=
6690
+ GIT_WORK_DIR=
6691
+ DOCKER_GID=999
6692
+ `;
6693
+ var VAULT_CONFIG_TEMPLATE = `# Minimal Vault configuration
6694
+ listener "tcp" {
6695
+ address = "0.0.0.0:8200"
6696
+ tls_disable = true
6697
+ }
6698
+
6699
+ storage "file" {
6700
+ path = "/vault/file"
6701
+ }
6702
+
6703
+ api_addr = "http://0.0.0.0:8200"
6704
+ ui = false
6705
+ disable_mlock = true
6706
+ log_level = "info"
6707
+ `;
6588
6708
  var LLM_PROVIDER_ENV_KEYS = /* @__PURE__ */ new Set([
6589
6709
  "SECRET__MISTRAL_AI__API_KEY",
6590
6710
  "SECRET__CEREBRAS__API_KEY",
@@ -6605,8 +6725,21 @@ function runInteractive(cmd, args, cwd) {
6605
6725
  child.on("error", reject);
6606
6726
  });
6607
6727
  }
6608
- function composeArgs(installPath, withOverrides) {
6609
- const args = ["compose", "--env-file", ".env", "-f", COMPOSE_FILE];
6728
+ function loadConfigForInstallPath(installPath) {
6729
+ const config = loadServerConfig();
6730
+ return config?.installPath === installPath ? config : null;
6731
+ }
6732
+ function getInstallMode(installPath, config = loadConfigForInstallPath(installPath)) {
6733
+ if (config?.installMode) return config.installMode;
6734
+ if (existsSync5(join3(installPath, IMAGE_COMPOSE_FILE))) return "image";
6735
+ return "source";
6736
+ }
6737
+ function shouldPullImages() {
6738
+ return process.env.OPENMATES_SKIP_IMAGE_PULL !== "1";
6739
+ }
6740
+ function composeArgs(installPath, withOverrides, installMode = getInstallMode(installPath)) {
6741
+ const composeFile = installMode === "image" ? IMAGE_COMPOSE_FILE : SOURCE_COMPOSE_FILE;
6742
+ const args = ["compose", "--env-file", ".env", "-f", composeFile];
6610
6743
  if (withOverrides && existsSync5(join3(installPath, COMPOSE_OVERRIDE))) {
6611
6744
  args.push("-f", COMPOSE_OVERRIDE);
6612
6745
  }
@@ -6651,6 +6784,80 @@ function getPackageVersion() {
6651
6784
  return "";
6652
6785
  }
6653
6786
  }
6787
+ function getDefaultImageTag() {
6788
+ const version = getPackageVersion();
6789
+ return version ? `v${version}` : "dev";
6790
+ }
6791
+ function defaultTemplateRefForVersion(version) {
6792
+ return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : `v${version}`;
6793
+ }
6794
+ function randomHex(bytes) {
6795
+ return randomBytes2(bytes).toString("hex");
6796
+ }
6797
+ function generateInviteCode() {
6798
+ const digits = Array.from(randomBytes2(12), (byte) => String(byte % 10)).join("");
6799
+ return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
6800
+ }
6801
+ function getEnvVar(content, name) {
6802
+ const match = content.match(new RegExp(`^${name}=(.*)$`, "m"));
6803
+ return match?.[1]?.replace(/^"|"$/g, "") ?? "";
6804
+ }
6805
+ function setEnvVar(content, name, value) {
6806
+ const line = `${name}=${value}`;
6807
+ const pattern = new RegExp(`^${name}=.*$`, "m");
6808
+ if (pattern.test(content)) {
6809
+ return content.replace(pattern, line);
6810
+ }
6811
+ const separator = content.endsWith("\n") ? "" : "\n";
6812
+ return `${content}${separator}${line}
6813
+ `;
6814
+ }
6815
+ function setEnvIfEmpty(content, name, value) {
6816
+ return getEnvVar(content, name) ? content : setEnvVar(content, name, value);
6817
+ }
6818
+ async function fetchText(url) {
6819
+ const response = await fetch(url);
6820
+ if (!response.ok) {
6821
+ throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
6822
+ }
6823
+ return response.text();
6824
+ }
6825
+ async function loadSelfHostComposeTemplate(version) {
6826
+ const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
6827
+ if (templateDir) {
6828
+ return readFileSync5(join3(resolve3(templateDir), IMAGE_COMPOSE_FILE), "utf-8");
6829
+ }
6830
+ const overrideUrl = process.env.OPENMATES_SELFHOST_COMPOSE_URL;
6831
+ if (overrideUrl) {
6832
+ return fetchText(overrideUrl);
6833
+ }
6834
+ const ref = defaultTemplateRefForVersion(version);
6835
+ return fetchText(
6836
+ `https://raw.githubusercontent.com/glowingkitty/OpenMates/${ref}/backend/core/docker-compose.selfhost.yml`
6837
+ );
6838
+ }
6839
+ async function writeImageModeRuntimeFiles(installPath, imageTag) {
6840
+ const coreDir = join3(installPath, "backend", "core");
6841
+ const vaultConfigDir = join3(coreDir, "vault", "config");
6842
+ mkdirSync3(vaultConfigDir, { recursive: true });
6843
+ writeFileSync3(join3(coreDir, "docker-compose.selfhost.yml"), await loadSelfHostComposeTemplate(getPackageVersion()));
6844
+ writeFileSync3(join3(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
6845
+ const envPath = join3(installPath, ".env");
6846
+ let envContent = existsSync5(envPath) ? readFileSync5(envPath, "utf-8") : MINIMAL_ENV_TEMPLATE;
6847
+ envContent = setEnvIfEmpty(envContent, "DATABASE_ADMIN_PASSWORD", randomHex(12));
6848
+ envContent = setEnvIfEmpty(envContent, "DATABASE_PASSWORD", randomHex(12));
6849
+ envContent = setEnvIfEmpty(envContent, "DIRECTUS_TOKEN", randomHex(32));
6850
+ envContent = setEnvIfEmpty(envContent, "DIRECTUS_SECRET", randomHex(32));
6851
+ envContent = setEnvIfEmpty(envContent, "DRAGONFLY_PASSWORD", randomHex(12));
6852
+ envContent = setEnvIfEmpty(envContent, "OPENOBSERVE_ROOT_PASSWORD", randomHex(32));
6853
+ envContent = setEnvIfEmpty(envContent, "INTERNAL_API_SHARED_TOKEN", randomHex(32));
6854
+ envContent = setEnvIfEmpty(envContent, "SELF_HOST_FIRST_INVITE_CODE", generateInviteCode());
6855
+ envContent = setEnvVar(envContent, "OPENMATES_IMAGE_TAG", imageTag);
6856
+ envContent = setEnvVar(envContent, "OPENMATES_IMAGE_REGISTRY", DEFAULT_IMAGE_REGISTRY);
6857
+ envContent = setEnvVar(envContent, "GIT_WORK_DIR", installPath);
6858
+ writeFileSync3(envPath, envContent.endsWith("\n") ? envContent : `${envContent}
6859
+ `);
6860
+ }
6654
6861
  function defaultCloneBranchForVersion(version) {
6655
6862
  return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : null;
6656
6863
  }
@@ -6700,9 +6907,9 @@ async function serverStatus(flags) {
6700
6907
  requireDocker();
6701
6908
  const installPath = resolveServerPath(flags);
6702
6909
  ensureGitWorkDirEnv(installPath);
6703
- const config = loadServerConfig();
6910
+ const config = loadConfigForInstallPath(installPath);
6704
6911
  const withOverrides = config?.composeProfile === "full";
6705
- const args = [...composeArgs(installPath, withOverrides), "ps"];
6912
+ const args = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "ps"];
6706
6913
  if (flags.json === true) {
6707
6914
  args.push("--format", "json");
6708
6915
  }
@@ -6715,13 +6922,20 @@ async function serverStart(flags) {
6715
6922
  ensureGitWorkDirEnv(installPath);
6716
6923
  warnIfMissingLlmCredentials(installPath);
6717
6924
  const withOverrides = flags["with-overrides"] === true;
6718
- const args = [...composeArgs(installPath, withOverrides), "up", "-d"];
6719
- const config = loadServerConfig();
6925
+ const config = loadConfigForInstallPath(installPath);
6926
+ const installMode = getInstallMode(installPath, config);
6927
+ const pullArgs = [...composeArgs(installPath, withOverrides, installMode), "pull"];
6928
+ const args = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6720
6929
  if (config && withOverrides && config.composeProfile !== "full") {
6721
6930
  saveServerConfig({ ...config, composeProfile: "full" });
6722
6931
  }
6723
6932
  console.error("Starting OpenMates server...");
6724
- const code = await runInteractive("docker", args, installPath);
6933
+ let code = 0;
6934
+ if (installMode === "image" && shouldPullImages()) {
6935
+ code = await runInteractive("docker", pullArgs, installPath);
6936
+ if (code !== 0) process.exit(code);
6937
+ }
6938
+ code = await runInteractive("docker", args, installPath);
6725
6939
  if (code !== 0) process.exit(code);
6726
6940
  if (flags.json === true) {
6727
6941
  printJson({ command: "start", status: "success", path: installPath });
@@ -6739,9 +6953,9 @@ async function serverStop(flags) {
6739
6953
  requireDocker();
6740
6954
  const installPath = resolveServerPath(flags);
6741
6955
  ensureGitWorkDirEnv(installPath);
6742
- const config = loadServerConfig();
6956
+ const config = loadConfigForInstallPath(installPath);
6743
6957
  const withOverrides = config?.composeProfile === "full";
6744
- const args = [...composeArgs(installPath, withOverrides), "down"];
6958
+ const args = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "down"];
6745
6959
  console.error("Stopping OpenMates server...");
6746
6960
  const code = await runInteractive("docker", args, installPath);
6747
6961
  if (code !== 0) process.exit(code);
@@ -6755,26 +6969,32 @@ async function serverRestart(flags) {
6755
6969
  requireDocker();
6756
6970
  const installPath = resolveServerPath(flags);
6757
6971
  ensureGitWorkDirEnv(installPath);
6758
- const config = loadServerConfig();
6972
+ const config = loadConfigForInstallPath(installPath);
6759
6973
  const withOverrides = config?.composeProfile === "full";
6974
+ const installMode = getInstallMode(installPath, config);
6760
6975
  if (flags.rebuild === true) {
6976
+ if (installMode === "image") {
6977
+ throw new Error(
6978
+ "Image-mode installs use prebuilt images and cannot rebuild locally. Run 'openmates server update' to pull newer images, or reinstall with --from-source to build from source."
6979
+ );
6980
+ }
6761
6981
  console.error("Rebuilding OpenMates server (this may take a few minutes)...");
6762
- const downArgs = [...composeArgs(installPath, withOverrides), "down"];
6982
+ const downArgs = [...composeArgs(installPath, withOverrides, installMode), "down"];
6763
6983
  let code = await runInteractive("docker", downArgs, installPath);
6764
6984
  if (code !== 0) process.exit(code);
6765
6985
  try {
6766
6986
  exec("docker volume rm openmates-cache-data", installPath);
6767
6987
  } catch {
6768
6988
  }
6769
- const buildArgs = [...composeArgs(installPath, withOverrides), "build"];
6989
+ const buildArgs = [...composeArgs(installPath, withOverrides, installMode), "build"];
6770
6990
  code = await runInteractive("docker", buildArgs, installPath);
6771
6991
  if (code !== 0) process.exit(code);
6772
- const upArgs = [...composeArgs(installPath, withOverrides), "up", "-d"];
6992
+ const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6773
6993
  code = await runInteractive("docker", upArgs, installPath);
6774
6994
  if (code !== 0) process.exit(code);
6775
6995
  } else {
6776
6996
  console.error("Restarting OpenMates server...");
6777
- const args = [...composeArgs(installPath, withOverrides), "restart"];
6997
+ const args = [...composeArgs(installPath, withOverrides, installMode), "restart"];
6778
6998
  const code = await runInteractive("docker", args, installPath);
6779
6999
  if (code !== 0) process.exit(code);
6780
7000
  }
@@ -6788,9 +7008,9 @@ async function serverLogs(flags) {
6788
7008
  requireDocker();
6789
7009
  const installPath = resolveServerPath(flags);
6790
7010
  ensureGitWorkDirEnv(installPath);
6791
- const config = loadServerConfig();
7011
+ const config = loadConfigForInstallPath(installPath);
6792
7012
  const withOverrides = config?.composeProfile === "full";
6793
- const args = [...composeArgs(installPath, withOverrides), "logs"];
7013
+ const args = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "logs"];
6794
7014
  if (flags.follow === true || flags.f === true) {
6795
7015
  args.push("--follow");
6796
7016
  }
@@ -6808,14 +7028,56 @@ async function serverLogs(flags) {
6808
7028
  if (code !== 0) process.exit(code);
6809
7029
  }
6810
7030
  async function serverInstall(flags) {
6811
- requireGit();
6812
7031
  const installPath = typeof flags.path === "string" ? resolve3(flags.path) : DEFAULT_INSTALL_PATH;
6813
7032
  const sourcePath = typeof flags["source-path"] === "string" ? resolve3(flags["source-path"]) : null;
6814
- if (existsSync5(join3(installPath, "setup.sh"))) {
7033
+ const fromSource = flags["from-source"] === true || sourcePath !== null;
7034
+ if (existsSync5(join3(installPath, SOURCE_COMPOSE_FILE)) || existsSync5(join3(installPath, IMAGE_COMPOSE_FILE))) {
6815
7035
  console.error(`OpenMates already exists at ${installPath}.`);
6816
7036
  console.error("Use 'openmates server update' to update, or choose a different --path.");
6817
7037
  process.exit(1);
6818
7038
  }
7039
+ if (!fromSource) {
7040
+ requireDocker();
7041
+ mkdirSync3(installPath, { recursive: true });
7042
+ if (typeof flags["env-path"] === "string") {
7043
+ const envSource = resolve3(flags["env-path"]);
7044
+ if (!existsSync5(envSource)) {
7045
+ throw new Error(`Env file not found: ${envSource}`);
7046
+ }
7047
+ copyFileSync(envSource, join3(installPath, ".env"));
7048
+ console.error(`Copied ${envSource} to ${installPath}/.env`);
7049
+ }
7050
+ const imageTag = typeof flags["image-tag"] === "string" ? flags["image-tag"] : getDefaultImageTag();
7051
+ console.error(`Preparing OpenMates image-mode install at ${installPath}...`);
7052
+ await writeImageModeRuntimeFiles(installPath, imageTag);
7053
+ try {
7054
+ exec("docker network create openmates", installPath);
7055
+ } catch {
7056
+ }
7057
+ saveServerConfig({
7058
+ installPath,
7059
+ installedAt: Date.now(),
7060
+ composeProfile: "core",
7061
+ installMode: "image",
7062
+ imageTag
7063
+ });
7064
+ if (flags.json === true) {
7065
+ printJson({ command: "install", status: "success", path: installPath, mode: "image", imageTag });
7066
+ } else {
7067
+ const firstInvite = getEnvVar(readFileSync5(join3(installPath, ".env"), "utf-8"), "SELF_HOST_FIRST_INVITE_CODE");
7068
+ console.log(`
7069
+ OpenMates installed at ${installPath}`);
7070
+ console.log(`Mode: image (${DEFAULT_IMAGE_REGISTRY}, tag ${imageTag})`);
7071
+ console.log("\nNext steps:");
7072
+ console.log(" 1. Run: openmates server start");
7073
+ console.log(" 2. Open http://localhost:5173");
7074
+ if (firstInvite) console.log(` 3. Sign up with invite code: ${firstInvite}`);
7075
+ console.log(" 4. After signup, make yourself admin: openmates server make-admin your@email.com");
7076
+ console.log("\nOptional: edit .env first to add LLM provider API keys. Source builds are available with --from-source.");
7077
+ }
7078
+ return;
7079
+ }
7080
+ requireGit();
6819
7081
  const cloneSource = sourcePath ?? REPO_URL;
6820
7082
  const cloneBranch = sourcePath ? null : defaultCloneBranchForVersion(getPackageVersion());
6821
7083
  const cloneArgs = ["clone"];
@@ -6856,10 +7118,11 @@ async function serverInstall(flags) {
6856
7118
  saveServerConfig({
6857
7119
  installPath,
6858
7120
  installedAt: Date.now(),
6859
- composeProfile: "core"
7121
+ composeProfile: "core",
7122
+ installMode: "source"
6860
7123
  });
6861
7124
  if (flags.json === true) {
6862
- printJson({ command: "install", status: "success", path: installPath });
7125
+ printJson({ command: "install", status: "success", path: installPath, mode: "source" });
6863
7126
  } else {
6864
7127
  console.log(`
6865
7128
  OpenMates installed at ${installPath}`);
@@ -6871,11 +7134,29 @@ OpenMates installed at ${installPath}`);
6871
7134
  }
6872
7135
  }
6873
7136
  async function serverUpdate(flags) {
6874
- requireGit();
6875
7137
  requireDocker();
6876
7138
  const installPath = resolveServerPath(flags);
6877
7139
  ensureGitWorkDirEnv(installPath);
6878
7140
  console.error("Updating OpenMates...");
7141
+ const config = loadConfigForInstallPath(installPath);
7142
+ const withOverrides = config?.composeProfile === "full";
7143
+ const installMode = getInstallMode(installPath, config);
7144
+ if (installMode === "image") {
7145
+ const pullArgs = [...composeArgs(installPath, withOverrides, installMode), "pull"];
7146
+ console.error("Pulling prebuilt images...");
7147
+ let code2 = await runInteractive("docker", pullArgs, installPath);
7148
+ if (code2 !== 0) process.exit(code2);
7149
+ const upArgs2 = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
7150
+ code2 = await runInteractive("docker", upArgs2, installPath);
7151
+ if (code2 !== 0) process.exit(code2);
7152
+ if (flags.json === true) {
7153
+ printJson({ command: "update", status: "success", path: installPath, mode: "image" });
7154
+ } else {
7155
+ console.log("Server images pulled and containers restarted.");
7156
+ }
7157
+ return;
7158
+ }
7159
+ requireGit();
6879
7160
  if (flags.force === true) {
6880
7161
  console.error("Stashing local changes...");
6881
7162
  try {
@@ -6903,13 +7184,11 @@ async function serverUpdate(flags) {
6903
7184
  } catch {
6904
7185
  }
6905
7186
  }
6906
- const config = loadServerConfig();
6907
- const withOverrides = config?.composeProfile === "full";
6908
- const buildArgs = [...composeArgs(installPath, withOverrides), "build"];
7187
+ const buildArgs = [...composeArgs(installPath, withOverrides, installMode), "build"];
6909
7188
  console.error("Rebuilding containers...");
6910
7189
  let code = await runInteractive("docker", buildArgs, installPath);
6911
7190
  if (code !== 0) process.exit(code);
6912
- const upArgs = [...composeArgs(installPath, withOverrides), "up", "-d"];
7191
+ const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6913
7192
  code = await runInteractive("docker", upArgs, installPath);
6914
7193
  if (code !== 0) process.exit(code);
6915
7194
  if (flags.json === true) {
@@ -6922,8 +7201,9 @@ async function serverReset(flags) {
6922
7201
  requireDocker();
6923
7202
  const installPath = resolveServerPath(flags);
6924
7203
  ensureGitWorkDirEnv(installPath);
6925
- const config = loadServerConfig();
7204
+ const config = loadConfigForInstallPath(installPath);
6926
7205
  const withOverrides = config?.composeProfile === "full";
7206
+ const installMode = getInstallMode(installPath, config);
6927
7207
  const userDataOnly = flags["delete-user-data-only"] === true;
6928
7208
  if (userDataOnly) {
6929
7209
  console.error("\nWARNING: This will delete all user data (database and cache).");
@@ -6943,24 +7223,26 @@ async function serverReset(flags) {
6943
7223
  }
6944
7224
  console.error("Resetting server...");
6945
7225
  if (userDataOnly) {
6946
- const downArgs = [...composeArgs(installPath, withOverrides), "down"];
7226
+ const downArgs = [...composeArgs(installPath, withOverrides, installMode), "down"];
6947
7227
  let code = await runInteractive("docker", downArgs, installPath);
6948
7228
  if (code !== 0) process.exit(code);
6949
- for (const vol of ["openmates-cache-data", "openmates-cms-database-data"]) {
7229
+ for (const vol of ["openmates-cache-data", "openmates-postgres-data", "openmates-cms-database-data"]) {
6950
7230
  try {
6951
7231
  exec(`docker volume rm ${vol}`, installPath);
6952
7232
  console.error(` Removed volume: ${vol}`);
6953
7233
  } catch {
6954
7234
  }
6955
7235
  }
6956
- const buildArgs = [...composeArgs(installPath, withOverrides), "build"];
6957
- code = await runInteractive("docker", buildArgs, installPath);
6958
- if (code !== 0) process.exit(code);
6959
- const upArgs = [...composeArgs(installPath, withOverrides), "up", "-d"];
7236
+ if (installMode === "source") {
7237
+ const buildArgs = [...composeArgs(installPath, withOverrides, installMode), "build"];
7238
+ code = await runInteractive("docker", buildArgs, installPath);
7239
+ if (code !== 0) process.exit(code);
7240
+ }
7241
+ const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6960
7242
  code = await runInteractive("docker", upArgs, installPath);
6961
7243
  if (code !== 0) process.exit(code);
6962
7244
  } else {
6963
- const args = [...composeArgs(installPath, withOverrides), "down", "-v"];
7245
+ const args = [...composeArgs(installPath, withOverrides, installMode), "down", "-v"];
6964
7246
  const code = await runInteractive("docker", args, installPath);
6965
7247
  if (code !== 0) process.exit(code);
6966
7248
  }
@@ -7003,7 +7285,7 @@ async function serverUninstall(flags) {
7003
7285
  requireDocker();
7004
7286
  const installPath = resolveServerPath(flags);
7005
7287
  ensureGitWorkDirEnv(installPath);
7006
- const config = loadServerConfig();
7288
+ const config = loadConfigForInstallPath(installPath);
7007
7289
  const withOverrides = config?.composeProfile === "full";
7008
7290
  const keepData = flags["keep-data"] === true;
7009
7291
  console.error("\nWARNING: This will completely uninstall OpenMates:");
@@ -7025,7 +7307,7 @@ async function serverUninstall(flags) {
7025
7307
  }
7026
7308
  }
7027
7309
  console.error("Uninstalling OpenMates...");
7028
- const downArgs = [...composeArgs(installPath, withOverrides), "down", "--rmi", "local"];
7310
+ const downArgs = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "down", "--rmi", "local"];
7029
7311
  if (!keepData) {
7030
7312
  downArgs.push("-v");
7031
7313
  }
@@ -7058,13 +7340,13 @@ OpenMates Server Management
7058
7340
  Usage: openmates server <command> [options]
7059
7341
 
7060
7342
  Commands:
7061
- install Install OpenMates server (clone repo + run setup)
7343
+ install Install OpenMates server (prebuilt GHCR images by default)
7062
7344
  start Start the server
7063
7345
  stop Stop the server
7064
7346
  restart Restart the server
7065
7347
  status Show server status (container health)
7066
7348
  logs Display server logs
7067
- update Update to latest version (git pull + rebuild)
7349
+ update Update to latest version (pull images, or git pull + rebuild for source installs)
7068
7350
  make-admin Grant admin privileges to a user
7069
7351
  reset Reset server data (requires confirmation)
7070
7352
  uninstall Completely remove OpenMates (requires confirmation)
@@ -7078,7 +7360,9 @@ Command Options:
7078
7360
  install:
7079
7361
  --path <dir> Install directory (default: ~/openmates)
7080
7362
  --env-path <file> Copy a pre-existing .env file during install
7081
- --source-path <dir> Clone from a local checkout instead of GitHub (CI/testing)
7363
+ --image-tag <tag> Prebuilt image tag (default: CLI version tag)
7364
+ --from-source Clone/build from source instead of using prebuilt GHCR images
7365
+ --source-path <dir> Clone from a local checkout instead of GitHub (implies --from-source)
7082
7366
 
7083
7367
  start:
7084
7368
  --with-overrides Include admin UIs (Directus CMS, Grafana)
@@ -9338,8 +9622,44 @@ async function sendMessageStreaming(client, params, redactor) {
9338
9622
  try {
9339
9623
  const session = client.getSession();
9340
9624
  const uploadResult = await uploadFile(fe.localPath, session);
9341
- fe.embed.content = toonEncodeContent({
9342
- type: fe.embed.type === "pdf" ? "pdf" : "image",
9625
+ const embedType = fe.embed.type;
9626
+ const audioTranscription = embedType === "audio-recording" ? await transcribeUploadedAudio(
9627
+ uploadResult,
9628
+ fe.displayName,
9629
+ session,
9630
+ { chatId: params.chatId, requestId: uploadResult.embed_id }
9631
+ ) : null;
9632
+ const uploadedContent = embedType === "audio-recording" ? {
9633
+ app_id: "audio",
9634
+ skill_id: "transcribe",
9635
+ type: "audio-recording",
9636
+ status: "finished",
9637
+ filename: fe.displayName,
9638
+ mime_type: uploadResult.content_type,
9639
+ transcript: audioTranscription?.transcript ?? null,
9640
+ transcript_original: audioTranscription?.transcript_original ?? null,
9641
+ transcript_corrected: audioTranscription?.transcript_corrected ?? null,
9642
+ use_corrected: audioTranscription?.use_corrected ?? null,
9643
+ correction_model: audioTranscription?.correction_model ?? null,
9644
+ model: audioTranscription?.model ?? null,
9645
+ s3_base_url: uploadResult.s3_base_url,
9646
+ files: uploadResult.files,
9647
+ aes_key: uploadResult.aes_key,
9648
+ aes_nonce: uploadResult.aes_nonce,
9649
+ vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key
9650
+ } : embedType === "pdf" ? {
9651
+ type: "pdf",
9652
+ status: "processing",
9653
+ filename: fe.displayName,
9654
+ page_count: uploadResult.page_count ?? null,
9655
+ content_hash: uploadResult.content_hash,
9656
+ s3_base_url: uploadResult.s3_base_url,
9657
+ files: uploadResult.files,
9658
+ aes_key: uploadResult.aes_key,
9659
+ aes_nonce: uploadResult.aes_nonce,
9660
+ vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key
9661
+ } : {
9662
+ type: "image",
9343
9663
  app_id: "images",
9344
9664
  skill_id: "upload",
9345
9665
  status: "finished",
@@ -9351,10 +9671,15 @@ async function sendMessageStreaming(client, params, redactor) {
9351
9671
  aes_nonce: uploadResult.aes_nonce,
9352
9672
  vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key,
9353
9673
  ai_detection: uploadResult.ai_detection
9354
- });
9355
- fe.embed.status = fe.embed.type === "pdf" ? "processing" : "finished";
9674
+ };
9675
+ fe.embed.content = toonEncodeContent(uploadedContent);
9676
+ fe.embed.status = embedType === "pdf" ? "processing" : "finished";
9356
9677
  fe.embed.contentHash = uploadResult.content_hash;
9357
9678
  fe.embed.embedId = uploadResult.embed_id;
9679
+ fe.referenceBlock = createEmbedReferenceBlock(
9680
+ embedType,
9681
+ uploadResult.embed_id
9682
+ );
9358
9683
  if (!params.json) {
9359
9684
  process.stderr.write(
9360
9685
  `\x1B[32m \u2713\x1B[0m \x1B[2m${fe.displayName} uploaded\x1B[0m
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  getExtForLang,
4
4
  serializeToYaml
5
- } from "./chunk-TUPR2SL3.js";
6
- import "./chunk-SFTCIVE2.js";
5
+ } from "./chunk-IJXZKFPX.js";
6
+ import "./chunk-AXNRPVLE.js";
7
7
  export {
8
8
  getExtForLang,
9
9
  serializeToYaml
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@ import {
7
7
  getExtForLang,
8
8
  parseNewChatSuggestionText,
9
9
  serializeToYaml
10
- } from "./chunk-TUPR2SL3.js";
11
- import "./chunk-SFTCIVE2.js";
10
+ } from "./chunk-IJXZKFPX.js";
11
+ import "./chunk-AXNRPVLE.js";
12
12
  export {
13
13
  MATE_NAMES,
14
14
  MEMORY_TYPE_REGISTRY,
@@ -1,8 +1,10 @@
1
1
  import {
2
+ transcribeUploadedAudio,
2
3
  uploadFile,
3
4
  uploadProfileImage
4
- } from "./chunk-SFTCIVE2.js";
5
+ } from "./chunk-AXNRPVLE.js";
5
6
  export {
7
+ transcribeUploadedAudio,
6
8
  uploadFile,
7
9
  uploadProfileImage
8
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmates",
3
- "version": "0.11.0-alpha.16",
3
+ "version": "0.11.0-alpha.18",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",