openmates 0.11.0-alpha.16 → 0.11.0-alpha.17

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,67 @@ 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
+ TRUSTED_PROXY_IPS="172.16.0.0/12"
6680
+ CORE_SIDECAR_URL=http://admin-sidecar:8001
6681
+ CLEAR_CACHE_ON_UPDATE=true
6682
+ SIGNUP_LIMIT=20
6683
+ SELF_HOST_SIGNUP_MODE=invite_only
6684
+ SELF_HOST_SIGNUP_ALLOWED_DOMAINS=
6685
+ SELF_HOST_FIRST_INVITE_CODE=
6686
+ APPLICATION_PREVIEW_ORIGIN=
6687
+ OPENMATES_IMAGE_REGISTRY=${DEFAULT_IMAGE_REGISTRY}
6688
+ OPENMATES_IMAGE_TAG=
6689
+ GIT_WORK_DIR=
6690
+ DOCKER_GID=999
6691
+ `;
6692
+ var VAULT_CONFIG_TEMPLATE = `# Minimal Vault configuration
6693
+ listener "tcp" {
6694
+ address = "0.0.0.0:8200"
6695
+ tls_disable = true
6696
+ }
6697
+
6698
+ storage "file" {
6699
+ path = "/vault/file"
6700
+ }
6701
+
6702
+ api_addr = "http://0.0.0.0:8200"
6703
+ ui = false
6704
+ disable_mlock = true
6705
+ log_level = "info"
6706
+ `;
6588
6707
  var LLM_PROVIDER_ENV_KEYS = /* @__PURE__ */ new Set([
6589
6708
  "SECRET__MISTRAL_AI__API_KEY",
6590
6709
  "SECRET__CEREBRAS__API_KEY",
@@ -6605,8 +6724,21 @@ function runInteractive(cmd, args, cwd) {
6605
6724
  child.on("error", reject);
6606
6725
  });
6607
6726
  }
6608
- function composeArgs(installPath, withOverrides) {
6609
- const args = ["compose", "--env-file", ".env", "-f", COMPOSE_FILE];
6727
+ function loadConfigForInstallPath(installPath) {
6728
+ const config = loadServerConfig();
6729
+ return config?.installPath === installPath ? config : null;
6730
+ }
6731
+ function getInstallMode(installPath, config = loadConfigForInstallPath(installPath)) {
6732
+ if (config?.installMode) return config.installMode;
6733
+ if (existsSync5(join3(installPath, IMAGE_COMPOSE_FILE))) return "image";
6734
+ return "source";
6735
+ }
6736
+ function shouldPullImages() {
6737
+ return process.env.OPENMATES_SKIP_IMAGE_PULL !== "1";
6738
+ }
6739
+ function composeArgs(installPath, withOverrides, installMode = getInstallMode(installPath)) {
6740
+ const composeFile = installMode === "image" ? IMAGE_COMPOSE_FILE : SOURCE_COMPOSE_FILE;
6741
+ const args = ["compose", "--env-file", ".env", "-f", composeFile];
6610
6742
  if (withOverrides && existsSync5(join3(installPath, COMPOSE_OVERRIDE))) {
6611
6743
  args.push("-f", COMPOSE_OVERRIDE);
6612
6744
  }
@@ -6651,6 +6783,80 @@ function getPackageVersion() {
6651
6783
  return "";
6652
6784
  }
6653
6785
  }
6786
+ function getDefaultImageTag() {
6787
+ const version = getPackageVersion();
6788
+ return version ? `v${version}` : "dev";
6789
+ }
6790
+ function defaultTemplateRefForVersion(version) {
6791
+ return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : `v${version}`;
6792
+ }
6793
+ function randomHex(bytes) {
6794
+ return randomBytes2(bytes).toString("hex");
6795
+ }
6796
+ function generateInviteCode() {
6797
+ const digits = Array.from(randomBytes2(12), (byte) => String(byte % 10)).join("");
6798
+ return `${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
6799
+ }
6800
+ function getEnvVar(content, name) {
6801
+ const match = content.match(new RegExp(`^${name}=(.*)$`, "m"));
6802
+ return match?.[1]?.replace(/^"|"$/g, "") ?? "";
6803
+ }
6804
+ function setEnvVar(content, name, value) {
6805
+ const line = `${name}=${value}`;
6806
+ const pattern = new RegExp(`^${name}=.*$`, "m");
6807
+ if (pattern.test(content)) {
6808
+ return content.replace(pattern, line);
6809
+ }
6810
+ const separator = content.endsWith("\n") ? "" : "\n";
6811
+ return `${content}${separator}${line}
6812
+ `;
6813
+ }
6814
+ function setEnvIfEmpty(content, name, value) {
6815
+ return getEnvVar(content, name) ? content : setEnvVar(content, name, value);
6816
+ }
6817
+ async function fetchText(url) {
6818
+ const response = await fetch(url);
6819
+ if (!response.ok) {
6820
+ throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
6821
+ }
6822
+ return response.text();
6823
+ }
6824
+ async function loadSelfHostComposeTemplate(version) {
6825
+ const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
6826
+ if (templateDir) {
6827
+ return readFileSync5(join3(resolve3(templateDir), IMAGE_COMPOSE_FILE), "utf-8");
6828
+ }
6829
+ const overrideUrl = process.env.OPENMATES_SELFHOST_COMPOSE_URL;
6830
+ if (overrideUrl) {
6831
+ return fetchText(overrideUrl);
6832
+ }
6833
+ const ref = defaultTemplateRefForVersion(version);
6834
+ return fetchText(
6835
+ `https://raw.githubusercontent.com/glowingkitty/OpenMates/${ref}/backend/core/docker-compose.selfhost.yml`
6836
+ );
6837
+ }
6838
+ async function writeImageModeRuntimeFiles(installPath, imageTag) {
6839
+ const coreDir = join3(installPath, "backend", "core");
6840
+ const vaultConfigDir = join3(coreDir, "vault", "config");
6841
+ mkdirSync3(vaultConfigDir, { recursive: true });
6842
+ writeFileSync3(join3(coreDir, "docker-compose.selfhost.yml"), await loadSelfHostComposeTemplate(getPackageVersion()));
6843
+ writeFileSync3(join3(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
6844
+ const envPath = join3(installPath, ".env");
6845
+ let envContent = existsSync5(envPath) ? readFileSync5(envPath, "utf-8") : MINIMAL_ENV_TEMPLATE;
6846
+ envContent = setEnvIfEmpty(envContent, "DATABASE_ADMIN_PASSWORD", randomHex(12));
6847
+ envContent = setEnvIfEmpty(envContent, "DATABASE_PASSWORD", randomHex(12));
6848
+ envContent = setEnvIfEmpty(envContent, "DIRECTUS_TOKEN", randomHex(32));
6849
+ envContent = setEnvIfEmpty(envContent, "DIRECTUS_SECRET", randomHex(32));
6850
+ envContent = setEnvIfEmpty(envContent, "DRAGONFLY_PASSWORD", randomHex(12));
6851
+ envContent = setEnvIfEmpty(envContent, "OPENOBSERVE_ROOT_PASSWORD", randomHex(32));
6852
+ envContent = setEnvIfEmpty(envContent, "INTERNAL_API_SHARED_TOKEN", randomHex(32));
6853
+ envContent = setEnvIfEmpty(envContent, "SELF_HOST_FIRST_INVITE_CODE", generateInviteCode());
6854
+ envContent = setEnvVar(envContent, "OPENMATES_IMAGE_TAG", imageTag);
6855
+ envContent = setEnvVar(envContent, "OPENMATES_IMAGE_REGISTRY", DEFAULT_IMAGE_REGISTRY);
6856
+ envContent = setEnvVar(envContent, "GIT_WORK_DIR", installPath);
6857
+ writeFileSync3(envPath, envContent.endsWith("\n") ? envContent : `${envContent}
6858
+ `);
6859
+ }
6654
6860
  function defaultCloneBranchForVersion(version) {
6655
6861
  return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : null;
6656
6862
  }
@@ -6700,9 +6906,9 @@ async function serverStatus(flags) {
6700
6906
  requireDocker();
6701
6907
  const installPath = resolveServerPath(flags);
6702
6908
  ensureGitWorkDirEnv(installPath);
6703
- const config = loadServerConfig();
6909
+ const config = loadConfigForInstallPath(installPath);
6704
6910
  const withOverrides = config?.composeProfile === "full";
6705
- const args = [...composeArgs(installPath, withOverrides), "ps"];
6911
+ const args = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "ps"];
6706
6912
  if (flags.json === true) {
6707
6913
  args.push("--format", "json");
6708
6914
  }
@@ -6715,13 +6921,20 @@ async function serverStart(flags) {
6715
6921
  ensureGitWorkDirEnv(installPath);
6716
6922
  warnIfMissingLlmCredentials(installPath);
6717
6923
  const withOverrides = flags["with-overrides"] === true;
6718
- const args = [...composeArgs(installPath, withOverrides), "up", "-d"];
6719
- const config = loadServerConfig();
6924
+ const config = loadConfigForInstallPath(installPath);
6925
+ const installMode = getInstallMode(installPath, config);
6926
+ const pullArgs = [...composeArgs(installPath, withOverrides, installMode), "pull"];
6927
+ const args = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6720
6928
  if (config && withOverrides && config.composeProfile !== "full") {
6721
6929
  saveServerConfig({ ...config, composeProfile: "full" });
6722
6930
  }
6723
6931
  console.error("Starting OpenMates server...");
6724
- const code = await runInteractive("docker", args, installPath);
6932
+ let code = 0;
6933
+ if (installMode === "image" && shouldPullImages()) {
6934
+ code = await runInteractive("docker", pullArgs, installPath);
6935
+ if (code !== 0) process.exit(code);
6936
+ }
6937
+ code = await runInteractive("docker", args, installPath);
6725
6938
  if (code !== 0) process.exit(code);
6726
6939
  if (flags.json === true) {
6727
6940
  printJson({ command: "start", status: "success", path: installPath });
@@ -6739,9 +6952,9 @@ async function serverStop(flags) {
6739
6952
  requireDocker();
6740
6953
  const installPath = resolveServerPath(flags);
6741
6954
  ensureGitWorkDirEnv(installPath);
6742
- const config = loadServerConfig();
6955
+ const config = loadConfigForInstallPath(installPath);
6743
6956
  const withOverrides = config?.composeProfile === "full";
6744
- const args = [...composeArgs(installPath, withOverrides), "down"];
6957
+ const args = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "down"];
6745
6958
  console.error("Stopping OpenMates server...");
6746
6959
  const code = await runInteractive("docker", args, installPath);
6747
6960
  if (code !== 0) process.exit(code);
@@ -6755,26 +6968,32 @@ async function serverRestart(flags) {
6755
6968
  requireDocker();
6756
6969
  const installPath = resolveServerPath(flags);
6757
6970
  ensureGitWorkDirEnv(installPath);
6758
- const config = loadServerConfig();
6971
+ const config = loadConfigForInstallPath(installPath);
6759
6972
  const withOverrides = config?.composeProfile === "full";
6973
+ const installMode = getInstallMode(installPath, config);
6760
6974
  if (flags.rebuild === true) {
6975
+ if (installMode === "image") {
6976
+ throw new Error(
6977
+ "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."
6978
+ );
6979
+ }
6761
6980
  console.error("Rebuilding OpenMates server (this may take a few minutes)...");
6762
- const downArgs = [...composeArgs(installPath, withOverrides), "down"];
6981
+ const downArgs = [...composeArgs(installPath, withOverrides, installMode), "down"];
6763
6982
  let code = await runInteractive("docker", downArgs, installPath);
6764
6983
  if (code !== 0) process.exit(code);
6765
6984
  try {
6766
6985
  exec("docker volume rm openmates-cache-data", installPath);
6767
6986
  } catch {
6768
6987
  }
6769
- const buildArgs = [...composeArgs(installPath, withOverrides), "build"];
6988
+ const buildArgs = [...composeArgs(installPath, withOverrides, installMode), "build"];
6770
6989
  code = await runInteractive("docker", buildArgs, installPath);
6771
6990
  if (code !== 0) process.exit(code);
6772
- const upArgs = [...composeArgs(installPath, withOverrides), "up", "-d"];
6991
+ const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6773
6992
  code = await runInteractive("docker", upArgs, installPath);
6774
6993
  if (code !== 0) process.exit(code);
6775
6994
  } else {
6776
6995
  console.error("Restarting OpenMates server...");
6777
- const args = [...composeArgs(installPath, withOverrides), "restart"];
6996
+ const args = [...composeArgs(installPath, withOverrides, installMode), "restart"];
6778
6997
  const code = await runInteractive("docker", args, installPath);
6779
6998
  if (code !== 0) process.exit(code);
6780
6999
  }
@@ -6788,9 +7007,9 @@ async function serverLogs(flags) {
6788
7007
  requireDocker();
6789
7008
  const installPath = resolveServerPath(flags);
6790
7009
  ensureGitWorkDirEnv(installPath);
6791
- const config = loadServerConfig();
7010
+ const config = loadConfigForInstallPath(installPath);
6792
7011
  const withOverrides = config?.composeProfile === "full";
6793
- const args = [...composeArgs(installPath, withOverrides), "logs"];
7012
+ const args = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "logs"];
6794
7013
  if (flags.follow === true || flags.f === true) {
6795
7014
  args.push("--follow");
6796
7015
  }
@@ -6808,14 +7027,56 @@ async function serverLogs(flags) {
6808
7027
  if (code !== 0) process.exit(code);
6809
7028
  }
6810
7029
  async function serverInstall(flags) {
6811
- requireGit();
6812
7030
  const installPath = typeof flags.path === "string" ? resolve3(flags.path) : DEFAULT_INSTALL_PATH;
6813
7031
  const sourcePath = typeof flags["source-path"] === "string" ? resolve3(flags["source-path"]) : null;
6814
- if (existsSync5(join3(installPath, "setup.sh"))) {
7032
+ const fromSource = flags["from-source"] === true || sourcePath !== null;
7033
+ if (existsSync5(join3(installPath, SOURCE_COMPOSE_FILE)) || existsSync5(join3(installPath, IMAGE_COMPOSE_FILE))) {
6815
7034
  console.error(`OpenMates already exists at ${installPath}.`);
6816
7035
  console.error("Use 'openmates server update' to update, or choose a different --path.");
6817
7036
  process.exit(1);
6818
7037
  }
7038
+ if (!fromSource) {
7039
+ requireDocker();
7040
+ mkdirSync3(installPath, { recursive: true });
7041
+ if (typeof flags["env-path"] === "string") {
7042
+ const envSource = resolve3(flags["env-path"]);
7043
+ if (!existsSync5(envSource)) {
7044
+ throw new Error(`Env file not found: ${envSource}`);
7045
+ }
7046
+ copyFileSync(envSource, join3(installPath, ".env"));
7047
+ console.error(`Copied ${envSource} to ${installPath}/.env`);
7048
+ }
7049
+ const imageTag = typeof flags["image-tag"] === "string" ? flags["image-tag"] : getDefaultImageTag();
7050
+ console.error(`Preparing OpenMates image-mode install at ${installPath}...`);
7051
+ await writeImageModeRuntimeFiles(installPath, imageTag);
7052
+ try {
7053
+ exec("docker network create openmates", installPath);
7054
+ } catch {
7055
+ }
7056
+ saveServerConfig({
7057
+ installPath,
7058
+ installedAt: Date.now(),
7059
+ composeProfile: "core",
7060
+ installMode: "image",
7061
+ imageTag
7062
+ });
7063
+ if (flags.json === true) {
7064
+ printJson({ command: "install", status: "success", path: installPath, mode: "image", imageTag });
7065
+ } else {
7066
+ const firstInvite = getEnvVar(readFileSync5(join3(installPath, ".env"), "utf-8"), "SELF_HOST_FIRST_INVITE_CODE");
7067
+ console.log(`
7068
+ OpenMates installed at ${installPath}`);
7069
+ console.log(`Mode: image (${DEFAULT_IMAGE_REGISTRY}, tag ${imageTag})`);
7070
+ console.log("\nNext steps:");
7071
+ console.log(" 1. Run: openmates server start");
7072
+ console.log(" 2. Open http://localhost:5173");
7073
+ if (firstInvite) console.log(` 3. Sign up with invite code: ${firstInvite}`);
7074
+ console.log(" 4. After signup, make yourself admin: openmates server make-admin your@email.com");
7075
+ console.log("\nOptional: edit .env first to add LLM provider API keys. Source builds are available with --from-source.");
7076
+ }
7077
+ return;
7078
+ }
7079
+ requireGit();
6819
7080
  const cloneSource = sourcePath ?? REPO_URL;
6820
7081
  const cloneBranch = sourcePath ? null : defaultCloneBranchForVersion(getPackageVersion());
6821
7082
  const cloneArgs = ["clone"];
@@ -6856,10 +7117,11 @@ async function serverInstall(flags) {
6856
7117
  saveServerConfig({
6857
7118
  installPath,
6858
7119
  installedAt: Date.now(),
6859
- composeProfile: "core"
7120
+ composeProfile: "core",
7121
+ installMode: "source"
6860
7122
  });
6861
7123
  if (flags.json === true) {
6862
- printJson({ command: "install", status: "success", path: installPath });
7124
+ printJson({ command: "install", status: "success", path: installPath, mode: "source" });
6863
7125
  } else {
6864
7126
  console.log(`
6865
7127
  OpenMates installed at ${installPath}`);
@@ -6871,11 +7133,29 @@ OpenMates installed at ${installPath}`);
6871
7133
  }
6872
7134
  }
6873
7135
  async function serverUpdate(flags) {
6874
- requireGit();
6875
7136
  requireDocker();
6876
7137
  const installPath = resolveServerPath(flags);
6877
7138
  ensureGitWorkDirEnv(installPath);
6878
7139
  console.error("Updating OpenMates...");
7140
+ const config = loadConfigForInstallPath(installPath);
7141
+ const withOverrides = config?.composeProfile === "full";
7142
+ const installMode = getInstallMode(installPath, config);
7143
+ if (installMode === "image") {
7144
+ const pullArgs = [...composeArgs(installPath, withOverrides, installMode), "pull"];
7145
+ console.error("Pulling prebuilt images...");
7146
+ let code2 = await runInteractive("docker", pullArgs, installPath);
7147
+ if (code2 !== 0) process.exit(code2);
7148
+ const upArgs2 = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
7149
+ code2 = await runInteractive("docker", upArgs2, installPath);
7150
+ if (code2 !== 0) process.exit(code2);
7151
+ if (flags.json === true) {
7152
+ printJson({ command: "update", status: "success", path: installPath, mode: "image" });
7153
+ } else {
7154
+ console.log("Server images pulled and containers restarted.");
7155
+ }
7156
+ return;
7157
+ }
7158
+ requireGit();
6879
7159
  if (flags.force === true) {
6880
7160
  console.error("Stashing local changes...");
6881
7161
  try {
@@ -6903,13 +7183,11 @@ async function serverUpdate(flags) {
6903
7183
  } catch {
6904
7184
  }
6905
7185
  }
6906
- const config = loadServerConfig();
6907
- const withOverrides = config?.composeProfile === "full";
6908
- const buildArgs = [...composeArgs(installPath, withOverrides), "build"];
7186
+ const buildArgs = [...composeArgs(installPath, withOverrides, installMode), "build"];
6909
7187
  console.error("Rebuilding containers...");
6910
7188
  let code = await runInteractive("docker", buildArgs, installPath);
6911
7189
  if (code !== 0) process.exit(code);
6912
- const upArgs = [...composeArgs(installPath, withOverrides), "up", "-d"];
7190
+ const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6913
7191
  code = await runInteractive("docker", upArgs, installPath);
6914
7192
  if (code !== 0) process.exit(code);
6915
7193
  if (flags.json === true) {
@@ -6922,8 +7200,9 @@ async function serverReset(flags) {
6922
7200
  requireDocker();
6923
7201
  const installPath = resolveServerPath(flags);
6924
7202
  ensureGitWorkDirEnv(installPath);
6925
- const config = loadServerConfig();
7203
+ const config = loadConfigForInstallPath(installPath);
6926
7204
  const withOverrides = config?.composeProfile === "full";
7205
+ const installMode = getInstallMode(installPath, config);
6927
7206
  const userDataOnly = flags["delete-user-data-only"] === true;
6928
7207
  if (userDataOnly) {
6929
7208
  console.error("\nWARNING: This will delete all user data (database and cache).");
@@ -6943,24 +7222,26 @@ async function serverReset(flags) {
6943
7222
  }
6944
7223
  console.error("Resetting server...");
6945
7224
  if (userDataOnly) {
6946
- const downArgs = [...composeArgs(installPath, withOverrides), "down"];
7225
+ const downArgs = [...composeArgs(installPath, withOverrides, installMode), "down"];
6947
7226
  let code = await runInteractive("docker", downArgs, installPath);
6948
7227
  if (code !== 0) process.exit(code);
6949
- for (const vol of ["openmates-cache-data", "openmates-cms-database-data"]) {
7228
+ for (const vol of ["openmates-cache-data", "openmates-postgres-data", "openmates-cms-database-data"]) {
6950
7229
  try {
6951
7230
  exec(`docker volume rm ${vol}`, installPath);
6952
7231
  console.error(` Removed volume: ${vol}`);
6953
7232
  } catch {
6954
7233
  }
6955
7234
  }
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"];
7235
+ if (installMode === "source") {
7236
+ const buildArgs = [...composeArgs(installPath, withOverrides, installMode), "build"];
7237
+ code = await runInteractive("docker", buildArgs, installPath);
7238
+ if (code !== 0) process.exit(code);
7239
+ }
7240
+ const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
6960
7241
  code = await runInteractive("docker", upArgs, installPath);
6961
7242
  if (code !== 0) process.exit(code);
6962
7243
  } else {
6963
- const args = [...composeArgs(installPath, withOverrides), "down", "-v"];
7244
+ const args = [...composeArgs(installPath, withOverrides, installMode), "down", "-v"];
6964
7245
  const code = await runInteractive("docker", args, installPath);
6965
7246
  if (code !== 0) process.exit(code);
6966
7247
  }
@@ -7003,7 +7284,7 @@ async function serverUninstall(flags) {
7003
7284
  requireDocker();
7004
7285
  const installPath = resolveServerPath(flags);
7005
7286
  ensureGitWorkDirEnv(installPath);
7006
- const config = loadServerConfig();
7287
+ const config = loadConfigForInstallPath(installPath);
7007
7288
  const withOverrides = config?.composeProfile === "full";
7008
7289
  const keepData = flags["keep-data"] === true;
7009
7290
  console.error("\nWARNING: This will completely uninstall OpenMates:");
@@ -7025,7 +7306,7 @@ async function serverUninstall(flags) {
7025
7306
  }
7026
7307
  }
7027
7308
  console.error("Uninstalling OpenMates...");
7028
- const downArgs = [...composeArgs(installPath, withOverrides), "down", "--rmi", "local"];
7309
+ const downArgs = [...composeArgs(installPath, withOverrides, getInstallMode(installPath, config)), "down", "--rmi", "local"];
7029
7310
  if (!keepData) {
7030
7311
  downArgs.push("-v");
7031
7312
  }
@@ -7058,13 +7339,13 @@ OpenMates Server Management
7058
7339
  Usage: openmates server <command> [options]
7059
7340
 
7060
7341
  Commands:
7061
- install Install OpenMates server (clone repo + run setup)
7342
+ install Install OpenMates server (prebuilt GHCR images by default)
7062
7343
  start Start the server
7063
7344
  stop Stop the server
7064
7345
  restart Restart the server
7065
7346
  status Show server status (container health)
7066
7347
  logs Display server logs
7067
- update Update to latest version (git pull + rebuild)
7348
+ update Update to latest version (pull images, or git pull + rebuild for source installs)
7068
7349
  make-admin Grant admin privileges to a user
7069
7350
  reset Reset server data (requires confirmation)
7070
7351
  uninstall Completely remove OpenMates (requires confirmation)
@@ -7078,7 +7359,9 @@ Command Options:
7078
7359
  install:
7079
7360
  --path <dir> Install directory (default: ~/openmates)
7080
7361
  --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)
7362
+ --image-tag <tag> Prebuilt image tag (default: CLI version tag)
7363
+ --from-source Clone/build from source instead of using prebuilt GHCR images
7364
+ --source-path <dir> Clone from a local checkout instead of GitHub (implies --from-source)
7082
7365
 
7083
7366
  start:
7084
7367
  --with-overrides Include admin UIs (Directus CMS, Grafana)
@@ -9338,8 +9621,44 @@ async function sendMessageStreaming(client, params, redactor) {
9338
9621
  try {
9339
9622
  const session = client.getSession();
9340
9623
  const uploadResult = await uploadFile(fe.localPath, session);
9341
- fe.embed.content = toonEncodeContent({
9342
- type: fe.embed.type === "pdf" ? "pdf" : "image",
9624
+ const embedType = fe.embed.type;
9625
+ const audioTranscription = embedType === "audio-recording" ? await transcribeUploadedAudio(
9626
+ uploadResult,
9627
+ fe.displayName,
9628
+ session,
9629
+ { chatId: params.chatId, requestId: uploadResult.embed_id }
9630
+ ) : null;
9631
+ const uploadedContent = embedType === "audio-recording" ? {
9632
+ app_id: "audio",
9633
+ skill_id: "transcribe",
9634
+ type: "audio-recording",
9635
+ status: "finished",
9636
+ filename: fe.displayName,
9637
+ mime_type: uploadResult.content_type,
9638
+ transcript: audioTranscription?.transcript ?? null,
9639
+ transcript_original: audioTranscription?.transcript_original ?? null,
9640
+ transcript_corrected: audioTranscription?.transcript_corrected ?? null,
9641
+ use_corrected: audioTranscription?.use_corrected ?? null,
9642
+ correction_model: audioTranscription?.correction_model ?? null,
9643
+ model: audioTranscription?.model ?? null,
9644
+ s3_base_url: uploadResult.s3_base_url,
9645
+ files: uploadResult.files,
9646
+ aes_key: uploadResult.aes_key,
9647
+ aes_nonce: uploadResult.aes_nonce,
9648
+ vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key
9649
+ } : embedType === "pdf" ? {
9650
+ type: "pdf",
9651
+ status: "processing",
9652
+ filename: fe.displayName,
9653
+ page_count: uploadResult.page_count ?? null,
9654
+ content_hash: uploadResult.content_hash,
9655
+ s3_base_url: uploadResult.s3_base_url,
9656
+ files: uploadResult.files,
9657
+ aes_key: uploadResult.aes_key,
9658
+ aes_nonce: uploadResult.aes_nonce,
9659
+ vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key
9660
+ } : {
9661
+ type: "image",
9343
9662
  app_id: "images",
9344
9663
  skill_id: "upload",
9345
9664
  status: "finished",
@@ -9351,10 +9670,15 @@ async function sendMessageStreaming(client, params, redactor) {
9351
9670
  aes_nonce: uploadResult.aes_nonce,
9352
9671
  vault_wrapped_aes_key: uploadResult.vault_wrapped_aes_key,
9353
9672
  ai_detection: uploadResult.ai_detection
9354
- });
9355
- fe.embed.status = fe.embed.type === "pdf" ? "processing" : "finished";
9673
+ };
9674
+ fe.embed.content = toonEncodeContent(uploadedContent);
9675
+ fe.embed.status = embedType === "pdf" ? "processing" : "finished";
9356
9676
  fe.embed.contentHash = uploadResult.content_hash;
9357
9677
  fe.embed.embedId = uploadResult.embed_id;
9678
+ fe.referenceBlock = createEmbedReferenceBlock(
9679
+ embedType,
9680
+ uploadResult.embed_id
9681
+ );
9358
9682
  if (!params.json) {
9359
9683
  process.stderr.write(
9360
9684
  `\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-LOVSX3MQ.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-LOVSX3MQ.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.17",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",