openmates 0.15.0-alpha.37 → 0.15.0-alpha.38

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.
@@ -21788,6 +21788,243 @@ function redactUrl(rawUrl) {
21788
21788
  return url.toString();
21789
21789
  }
21790
21790
 
21791
+ // src/accountExportArchive.ts
21792
+ var ACCOUNT_EXPORT_FORBIDDEN_FIELD_NAMES = /* @__PURE__ */ new Set([
21793
+ "access_token",
21794
+ "api_key",
21795
+ "backup_code_hash",
21796
+ "encrypted_master_key",
21797
+ "lookup_hash",
21798
+ "password_hash",
21799
+ "private_key",
21800
+ "raw_key",
21801
+ "refresh_token",
21802
+ "share_key",
21803
+ "signing_secret",
21804
+ "token_hash",
21805
+ "totp_seed",
21806
+ "webhook_secret"
21807
+ ]);
21808
+ var ACCOUNT_EXPORT_REDACTION_CATEGORIES = [
21809
+ "api_credentials",
21810
+ "authentication_tokens",
21811
+ "key_material",
21812
+ "password_and_recovery_hashes",
21813
+ "webhook_secrets"
21814
+ ];
21815
+ var ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS = [
21816
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
21817
+ /(?:^|[^a-z0-9])sk-(?:api|proj|live|test)[-_a-z0-9]{6,}/i,
21818
+ /#key=[A-Za-z0-9_-]{8,}/
21819
+ ];
21820
+ async function writeAccountExportArchive(bundle, flags = {}) {
21821
+ const { mkdir: mkdir2, mkdtemp, rm, writeFile: writeFile2 } = await import("fs/promises");
21822
+ const { tmpdir: tmpdir2 } = await import("os");
21823
+ const { execFileSync: execFileSync3 } = await import("child_process");
21824
+ const { join: join7, dirname: dirname6 } = await import("path");
21825
+ const exportId = safeArchiveSegment(String(bundle.export.export_id ?? `export-${Date.now()}`));
21826
+ const archiveFormat = flags.format === "directory" ? "directory" : "zip";
21827
+ const outputPath = typeof flags.output === "string" ? flags.output : join7(process.cwd(), archiveFormat === "zip" ? `openmates-account-export-${exportId}.zip` : `openmates-account-export-${exportId}`);
21828
+ const stagingDir = archiveFormat === "directory" ? outputPath : await mkdtemp(join7(tmpdir2(), `openmates-account-export-${exportId}-`));
21829
+ const writtenFiles = [];
21830
+ async function writeArchiveText(relativePath, content) {
21831
+ assertAccountExportTextSafe(content, relativePath);
21832
+ const fullPath = join7(stagingDir, relativePath);
21833
+ await mkdir2(dirname6(fullPath), { recursive: true });
21834
+ await writeFile2(fullPath, content, "utf-8");
21835
+ writtenFiles.push(relativePath);
21836
+ }
21837
+ await writeArchiveText("README.md", buildAccountExportReadme(bundle));
21838
+ await writeArchiveText("manifest.yml", serializeArchiveYaml(bundle.manifest));
21839
+ await writeArchiveText("manifest.json", `${JSON.stringify(bundle.manifest, null, 2)}
21840
+ `);
21841
+ await writeArchiveText("export-report.yml", serializeArchiveYaml(buildAccountExportReport(bundle)));
21842
+ const domainPayloads = collectAccountExportDomainPayloads(bundle.chunks);
21843
+ for (const [domain, payloads] of Object.entries(domainPayloads)) {
21844
+ const domainFile = payloads.length === 1 ? payloads[0] : { chunks: payloads };
21845
+ await writeArchiveText(`domains/${safeArchiveSegment(domain)}.json`, `${JSON.stringify(domainFile, null, 2)}
21846
+ `);
21847
+ }
21848
+ await writeAccountExportChatFiles(domainPayloads.chats ?? [], writeArchiveText);
21849
+ if (archiveFormat === "directory") return { output: outputPath, format: "directory", files: writtenFiles.length };
21850
+ await mkdir2(dirname6(outputPath), { recursive: true });
21851
+ try {
21852
+ execFileSync3("zip", ["-r", outputPath, "."], { cwd: stagingDir, stdio: "pipe" });
21853
+ return { output: outputPath, format: "zip", files: writtenFiles.length };
21854
+ } finally {
21855
+ await rm(stagingDir, { recursive: true, force: true });
21856
+ }
21857
+ }
21858
+ function assertAccountExportPayloadSafe(value, path = "$export") {
21859
+ if (value === null || value === void 0) return;
21860
+ if (typeof value === "string") {
21861
+ for (const pattern of ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS) {
21862
+ if (pattern.test(value)) throw new Error(`Account export contains forbidden secret-like value at ${path}`);
21863
+ }
21864
+ return;
21865
+ }
21866
+ if (typeof value !== "object") return;
21867
+ if (Array.isArray(value)) {
21868
+ value.forEach((item, index) => assertAccountExportPayloadSafe(item, `${path}[${index}]`));
21869
+ return;
21870
+ }
21871
+ for (const [key, child] of Object.entries(value)) {
21872
+ const normalizedKey = key.toLowerCase();
21873
+ if (ACCOUNT_EXPORT_FORBIDDEN_FIELD_NAMES.has(normalizedKey)) {
21874
+ throw new Error(`Account export contains forbidden secret field '${key}' at ${path}`);
21875
+ }
21876
+ assertAccountExportPayloadSafe(child, `${path}.${key}`);
21877
+ }
21878
+ }
21879
+ function sanitizeAccountExportManifest(manifest) {
21880
+ const copy = JSON.parse(JSON.stringify(manifest));
21881
+ const report = copy.report;
21882
+ if (report && typeof report === "object" && !Array.isArray(report)) {
21883
+ const reportObject = report;
21884
+ if (Array.isArray(reportObject.redactions)) {
21885
+ reportObject.redactions = ACCOUNT_EXPORT_REDACTION_CATEGORIES;
21886
+ }
21887
+ }
21888
+ assertAccountExportPayloadSafe(copy, "$manifest");
21889
+ return copy;
21890
+ }
21891
+ function assertAccountExportTextSafe(content, relativePath) {
21892
+ for (const pattern of ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS) {
21893
+ if (pattern.test(content)) throw new Error(`Account export file ${relativePath} contains forbidden secret-like content`);
21894
+ }
21895
+ for (const field of ACCOUNT_EXPORT_FORBIDDEN_FIELD_NAMES) {
21896
+ if (new RegExp(`"?${field}"?\\s*:`, "i").test(content)) {
21897
+ throw new Error(`Account export file ${relativePath} contains forbidden secret field '${field}'`);
21898
+ }
21899
+ }
21900
+ }
21901
+ function buildAccountExportReadme(bundle) {
21902
+ const selectedDomains = Array.isArray(bundle.manifest.selected_domains) ? bundle.manifest.selected_domains.map(String) : [];
21903
+ return [
21904
+ "# OpenMates Account Export",
21905
+ "",
21906
+ `Export ID: ${String(bundle.export.export_id ?? "unknown")}`,
21907
+ `Status: ${String(bundle.export.status ?? "unknown")}`,
21908
+ `Schema: ${String(bundle.manifest.schema_version ?? "account-export-v1")}`,
21909
+ `Exported at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
21910
+ "",
21911
+ "## Contents",
21912
+ "",
21913
+ "- `manifest.yml` records selected domains, filters, and domain status.",
21914
+ "- `export-report.yml` summarizes completion, failures, and redacted categories.",
21915
+ "- `domains/` contains one JSON file per exported domain.",
21916
+ "- `chats/` contains one Markdown and one YAML file per chat when chat rows include readable content.",
21917
+ "",
21918
+ "## Selected Domains",
21919
+ "",
21920
+ ...selectedDomains.length > 0 ? selectedDomains.map((domain) => `- ${domain}`) : ["- none"],
21921
+ "",
21922
+ "Reusable credentials, token hashes, private keys, and raw secrets are intentionally excluded.",
21923
+ ""
21924
+ ].join("\n");
21925
+ }
21926
+ function buildAccountExportReport(bundle) {
21927
+ const report = bundle.manifest.report && typeof bundle.manifest.report === "object" && !Array.isArray(bundle.manifest.report) ? bundle.manifest.report : {};
21928
+ return {
21929
+ export_id: bundle.export.export_id ?? null,
21930
+ status: bundle.export.status ?? report.status ?? "unknown",
21931
+ selected_domains: Array.isArray(bundle.manifest.selected_domains) ? bundle.manifest.selected_domains : [],
21932
+ failures: Array.isArray(report.failures) ? report.failures : [],
21933
+ redacted_secret_categories: ACCOUNT_EXPORT_REDACTION_CATEGORIES,
21934
+ partial_requires_acceptance: Boolean(report.partial_requires_acceptance)
21935
+ };
21936
+ }
21937
+ function collectAccountExportDomainPayloads(chunks) {
21938
+ const grouped = {};
21939
+ for (const chunk of chunks) {
21940
+ const domain = safeArchiveSegment(String(chunk.domain ?? "unknown"));
21941
+ const payload = chunk.payload && typeof chunk.payload === "object" && !Array.isArray(chunk.payload) ? chunk.payload : { value: chunk.payload ?? null };
21942
+ grouped[domain] = grouped[domain] ?? [];
21943
+ grouped[domain].push({
21944
+ chunk_id: chunk.chunk_id ?? null,
21945
+ sequence: chunk.sequence ?? null,
21946
+ status: chunk.status ?? null,
21947
+ ...payload
21948
+ });
21949
+ }
21950
+ return grouped;
21951
+ }
21952
+ async function writeAccountExportChatFiles(chatPayloads, writeArchiveText) {
21953
+ const chats = [];
21954
+ for (const payload of chatPayloads) {
21955
+ if (Array.isArray(payload.items)) {
21956
+ chats.push(...payload.items.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item)));
21957
+ }
21958
+ }
21959
+ for (const [index, chat] of chats.entries()) {
21960
+ const chatId = safeArchiveSegment(String(chat.id ?? chat.chat_id ?? `chat-${index + 1}`));
21961
+ await writeArchiveText(`chats/${chatId}.yml`, serializeArchiveYaml({ chat, messages: Array.isArray(chat.messages) ? chat.messages : [] }));
21962
+ await writeArchiveText(`chats/${chatId}.md`, buildAccountExportChatMarkdown(chat));
21963
+ }
21964
+ }
21965
+ function buildAccountExportChatMarkdown(chat) {
21966
+ const title = String(chat.title ?? chat.name ?? chat.id ?? chat.chat_id ?? "Untitled Chat");
21967
+ const lines = [`# ${title}`, ""];
21968
+ if (typeof chat.summary === "string" && chat.summary.trim()) {
21969
+ lines.push("## Summary", "", chat.summary.trim(), "");
21970
+ }
21971
+ const messages = Array.isArray(chat.messages) ? chat.messages : [];
21972
+ if (messages.length > 0) {
21973
+ lines.push("## Messages", "");
21974
+ for (const message of messages) {
21975
+ if (!message || typeof message !== "object" || Array.isArray(message)) continue;
21976
+ const record = message;
21977
+ lines.push(`### ${String(record.role ?? record.sender ?? "message")}`, "", String(record.content ?? record.text ?? ""), "");
21978
+ }
21979
+ } else {
21980
+ lines.push("No readable message records were included in this export chunk.", "");
21981
+ }
21982
+ return `${lines.join("\n")}
21983
+ `;
21984
+ }
21985
+ function serializeArchiveYaml(data, indent = 0) {
21986
+ const pad2 = " ".repeat(indent);
21987
+ let out = "";
21988
+ for (const [key, val] of Object.entries(data)) {
21989
+ if (val === null || val === void 0) {
21990
+ out += `${pad2}${key}: null
21991
+ `;
21992
+ } else if (typeof val === "boolean" || typeof val === "number") {
21993
+ out += `${pad2}${key}: ${val}
21994
+ `;
21995
+ } else if (typeof val === "string") {
21996
+ out += val.includes("\n") ? `${pad2}${key}: |
21997
+ ${val.split("\n").map((line) => `${pad2} ${line}`).join("\n")}
21998
+ ` : `${pad2}${key}: ${quoteYamlString(val)}
21999
+ `;
22000
+ } else if (Array.isArray(val)) {
22001
+ out += `${pad2}${key}:
22002
+ `;
22003
+ for (const item of val) {
22004
+ if (item && typeof item === "object" && !Array.isArray(item)) {
22005
+ out += `${pad2}-
22006
+ ${serializeArchiveYaml(item, indent + 2)}`;
22007
+ } else {
22008
+ out += `${pad2}- ${typeof item === "string" ? quoteYamlString(item) : String(item)}
22009
+ `;
22010
+ }
22011
+ }
22012
+ } else if (typeof val === "object") {
22013
+ out += `${pad2}${key}:
22014
+ ${serializeArchiveYaml(val, indent + 1)}`;
22015
+ }
22016
+ }
22017
+ return out;
22018
+ }
22019
+ function quoteYamlString(value) {
22020
+ const needsQuote = value.includes(":") || value.includes("#") || value.startsWith("{") || value.startsWith("[") || value === "" || ["true", "false", "null"].includes(value);
22021
+ return needsQuote ? `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : value;
22022
+ }
22023
+ function safeArchiveSegment(value) {
22024
+ const cleaned = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
22025
+ return cleaned || "unknown";
22026
+ }
22027
+
21791
22028
  // ../ui/src/demo_chats/data/example_chats/gigantic-airplanes.ts
21792
22029
  var giganticAirplanesChat = {
21793
22030
  chat_id: "example-gigantic-airplanes",
@@ -40085,6 +40322,15 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
40085
40322
  new_chat: {
40086
40323
  text: "New chat"
40087
40324
  },
40325
+ ideabucket_badge: {
40326
+ text: "IdeaBucket"
40327
+ },
40328
+ ideabucket_draft: {
40329
+ text: "IdeaBucket draft"
40330
+ },
40331
+ ideabucket_input_pill: {
40332
+ text: "IdeaBucket for today"
40333
+ },
40088
40334
  request_feature: {
40089
40335
  text: "Request feature"
40090
40336
  },
@@ -62962,26 +63208,44 @@ async function runAccountExport(client, flags) {
62962
63208
  client.getAccountExportManifest(exportId),
62963
63209
  client.listAccountExportChunks(exportId)
62964
63210
  ]);
62965
- const completed = await client.completeAccountExport(exportId);
62966
- return {
63211
+ const downloadedChunks = [];
63212
+ try {
63213
+ for (const chunk of chunks.chunks) {
63214
+ const chunkId = String(chunk.chunk_id ?? "");
63215
+ const downloaded = chunkId ? await client.getAccountExportChunk(exportId, chunkId) : chunk;
63216
+ assertAccountExportPayloadSafe(downloaded);
63217
+ downloadedChunks.push(downloaded);
63218
+ }
63219
+ } catch (error) {
63220
+ await client.cancelAccountExport(exportId).catch(() => void 0);
63221
+ throw error;
63222
+ }
63223
+ let completed = await client.completeAccountExport(exportId);
63224
+ const completedStatus = String(completed.export.status ?? "");
63225
+ if (completedStatus === "partial") {
63226
+ if (flags["accept-partial"] !== true) {
63227
+ throw new Error(`Account export ${exportId} is partial. Re-run with --accept-partial to accept it, or inspect/cancel the job first.`);
63228
+ }
63229
+ completed = await client.acceptPartialAccountExport(exportId);
63230
+ }
63231
+ const bundle = {
62967
63232
  export: completed.export,
62968
- manifest: manifest.manifest,
62969
- chunks: chunks.chunks
63233
+ manifest: sanitizeAccountExportManifest(manifest.manifest),
63234
+ chunks: downloadedChunks
62970
63235
  };
63236
+ const archive = await writeAccountExportArchive(bundle, flags);
63237
+ return { ...bundle, archive };
62971
63238
  }
62972
63239
  function printAccountExportBundle(bundle, flags) {
62973
- if (typeof flags.output === "string") {
62974
- writeFileSync7(flags.output, `${JSON.stringify(bundle, null, 2)}
62975
- `, "utf-8");
62976
- }
62977
63240
  if (flags.json === true) {
62978
- printJson2(typeof flags.output === "string" ? { ...bundle, output: flags.output } : bundle);
63241
+ printJson2(bundle);
62979
63242
  return;
62980
63243
  }
62981
63244
  const exportRecord = bundle.export && typeof bundle.export === "object" ? bundle.export : {};
63245
+ const archive = bundle.archive && typeof bundle.archive === "object" ? bundle.archive : {};
62982
63246
  process.stdout.write(`Account export ${String(exportRecord.export_id ?? "")} ${String(exportRecord.status ?? "unknown")}
62983
63247
  `);
62984
- if (typeof flags.output === "string") process.stdout.write(`Wrote ${flags.output}
63248
+ if (typeof archive.output === "string") process.stdout.write(`Wrote ${archive.output}
62985
63249
  `);
62986
63250
  }
62987
63251
  function printApiKeyList(result, flags) {
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getExtForLang,
4
4
  serializeToYaml
5
- } from "./chunk-AAMJQMNZ.js";
5
+ } from "./chunk-N2TP5WZI.js";
6
6
  import "./chunk-AXNRPVLE.js";
7
7
  export {
8
8
  getExtForLang,
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  reconcileAuthoritativeChats,
19
19
  renderSupportInfo,
20
20
  serializeToYaml
21
- } from "./chunk-AAMJQMNZ.js";
21
+ } from "./chunk-N2TP5WZI.js";
22
22
  import "./chunk-AXNRPVLE.js";
23
23
  export {
24
24
  APP_SKILL_METADATA,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmates",
3
- "version": "0.15.0-alpha.37",
3
+ "version": "0.15.0-alpha.38",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",