openmates 0.15.0-alpha.39 → 0.15.0-alpha.40

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.
@@ -15122,6 +15122,249 @@ async function writeExportFile(path, data) {
15122
15122
  await writeFile(path, data);
15123
15123
  }
15124
15124
 
15125
+ // src/accountExportArchive.ts
15126
+ import JSZip from "jszip";
15127
+ var ACCOUNT_EXPORT_FORBIDDEN_FIELD_NAMES = /* @__PURE__ */ new Set([
15128
+ "access_token",
15129
+ "api_key",
15130
+ "backup_code_hash",
15131
+ "chat_key",
15132
+ "credential_secret",
15133
+ "device_key",
15134
+ "encrypted_master_key",
15135
+ "lookup_hash",
15136
+ "master_key",
15137
+ "password_hash",
15138
+ "private_key",
15139
+ "raw_key",
15140
+ "refresh_token",
15141
+ "share_key",
15142
+ "signing_secret",
15143
+ "token_hash",
15144
+ "totp_seed",
15145
+ "webhook_secret"
15146
+ ]);
15147
+ var ACCOUNT_EXPORT_REDACTION_CATEGORIES = [
15148
+ "api_credentials",
15149
+ "authentication_tokens",
15150
+ "key_material",
15151
+ "password_and_recovery_hashes",
15152
+ "webhook_secrets"
15153
+ ];
15154
+ var ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS = [
15155
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
15156
+ /(?:^|[^a-z0-9])sk-(?:api|proj|live|test)[-_a-z0-9]{6,}/i,
15157
+ /#key=[A-Za-z0-9_-]{8,}/
15158
+ ];
15159
+ async function writeAccountExportArchive(bundle, flags = {}) {
15160
+ const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
15161
+ const { join: join7, dirname: dirname6 } = await import("path");
15162
+ const exportId = safeArchiveSegment(String(bundle.export.export_id ?? `export-${Date.now()}`));
15163
+ const archiveFormat = flags.format === "directory" ? "directory" : "zip";
15164
+ const outputPath = typeof flags.output === "string" ? flags.output : join7(process.cwd(), archiveFormat === "zip" ? `openmates-account-export-${exportId}.zip` : `openmates-account-export-${exportId}`);
15165
+ const zip = archiveFormat === "zip" ? new JSZip() : null;
15166
+ const writtenFiles = [];
15167
+ async function writeArchiveText(relativePath, content) {
15168
+ assertAccountExportTextSafe(content, relativePath);
15169
+ if (zip) {
15170
+ zip.file(relativePath, content);
15171
+ writtenFiles.push(relativePath);
15172
+ return;
15173
+ }
15174
+ const stagingDir = outputPath;
15175
+ const fullPath = join7(stagingDir, relativePath);
15176
+ await mkdir2(dirname6(fullPath), { recursive: true });
15177
+ await writeFile2(fullPath, content, "utf-8");
15178
+ writtenFiles.push(relativePath);
15179
+ }
15180
+ await writeArchiveText("README.md", buildAccountExportReadme(bundle));
15181
+ await writeArchiveText("manifest.yml", serializeArchiveYaml(bundle.manifest));
15182
+ await writeArchiveText("manifest.json", `${JSON.stringify(bundle.manifest, null, 2)}
15183
+ `);
15184
+ await writeArchiveText("export-report.yml", serializeArchiveYaml(buildAccountExportReport(bundle)));
15185
+ const domainPayloads = collectAccountExportDomainPayloads(bundle.chunks);
15186
+ for (const [domain, payloads] of Object.entries(domainPayloads)) {
15187
+ const domainFile = payloads.length === 1 ? payloads[0] : { chunks: payloads };
15188
+ await writeArchiveText(`domains/${safeArchiveSegment(domain)}.json`, `${JSON.stringify(domainFile, null, 2)}
15189
+ `);
15190
+ }
15191
+ await writeAccountExportChatFiles(domainPayloads.chats ?? [], writeArchiveText);
15192
+ if (archiveFormat === "directory") return { output: outputPath, format: "directory", files: writtenFiles.length };
15193
+ await mkdir2(dirname6(outputPath), { recursive: true });
15194
+ const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
15195
+ await writeFile2(outputPath, zipBuffer);
15196
+ return { output: outputPath, format: "zip", files: writtenFiles.length };
15197
+ }
15198
+ function assertAccountExportPayloadSafe(value, path = "$export") {
15199
+ if (value === null || value === void 0) return;
15200
+ if (typeof value === "string") {
15201
+ for (const pattern of ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS) {
15202
+ if (pattern.test(value)) throw new Error(`Account export contains forbidden secret-like value at ${path}`);
15203
+ }
15204
+ return;
15205
+ }
15206
+ if (typeof value !== "object") return;
15207
+ if (Array.isArray(value)) {
15208
+ value.forEach((item, index) => assertAccountExportPayloadSafe(item, `${path}[${index}]`));
15209
+ return;
15210
+ }
15211
+ for (const [key, child] of Object.entries(value)) {
15212
+ const normalizedKey = key.toLowerCase();
15213
+ if (ACCOUNT_EXPORT_FORBIDDEN_FIELD_NAMES.has(normalizedKey)) {
15214
+ throw new Error(`Account export contains forbidden secret field '${key}' at ${path}`);
15215
+ }
15216
+ assertAccountExportPayloadSafe(child, `${path}.${key}`);
15217
+ }
15218
+ }
15219
+ function sanitizeAccountExportManifest(manifest) {
15220
+ const copy = JSON.parse(JSON.stringify(manifest));
15221
+ const report = copy.report;
15222
+ if (report && typeof report === "object" && !Array.isArray(report)) {
15223
+ const reportObject = report;
15224
+ if (Array.isArray(reportObject.redactions)) {
15225
+ reportObject.redactions = ACCOUNT_EXPORT_REDACTION_CATEGORIES;
15226
+ }
15227
+ }
15228
+ assertAccountExportPayloadSafe(copy, "$manifest");
15229
+ return copy;
15230
+ }
15231
+ function assertAccountExportTextSafe(content, relativePath) {
15232
+ for (const pattern of ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS) {
15233
+ if (pattern.test(content)) throw new Error(`Account export file ${relativePath} contains forbidden secret-like content`);
15234
+ }
15235
+ for (const field of ACCOUNT_EXPORT_FORBIDDEN_FIELD_NAMES) {
15236
+ if (new RegExp(`"?${field}"?\\s*:`, "i").test(content)) {
15237
+ throw new Error(`Account export file ${relativePath} contains forbidden secret field '${field}'`);
15238
+ }
15239
+ }
15240
+ }
15241
+ function buildAccountExportReadme(bundle) {
15242
+ const selectedDomains = Array.isArray(bundle.manifest.selected_domains) ? bundle.manifest.selected_domains.map(String) : [];
15243
+ return [
15244
+ "# OpenMates Account Export",
15245
+ "",
15246
+ `Export ID: ${String(bundle.export.export_id ?? "unknown")}`,
15247
+ `Status: ${String(bundle.export.status ?? "unknown")}`,
15248
+ `Schema: ${String(bundle.manifest.schema_version ?? "account-export-v1")}`,
15249
+ `Exported at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
15250
+ "",
15251
+ "## Contents",
15252
+ "",
15253
+ "- `manifest.yml` records selected domains, filters, and domain status.",
15254
+ "- `export-report.yml` summarizes completion, failures, and redacted categories.",
15255
+ "- `domains/` contains one JSON file per exported domain.",
15256
+ "- `chats/` contains one Markdown and one YAML file per chat when chat rows include readable content.",
15257
+ "",
15258
+ "## Selected Domains",
15259
+ "",
15260
+ ...selectedDomains.length > 0 ? selectedDomains.map((domain) => `- ${domain}`) : ["- none"],
15261
+ "",
15262
+ "Reusable credentials, token hashes, private keys, and raw secrets are intentionally excluded.",
15263
+ ""
15264
+ ].join("\n");
15265
+ }
15266
+ function buildAccountExportReport(bundle) {
15267
+ const report = bundle.manifest.report && typeof bundle.manifest.report === "object" && !Array.isArray(bundle.manifest.report) ? bundle.manifest.report : {};
15268
+ return {
15269
+ export_id: bundle.export.export_id ?? null,
15270
+ status: bundle.export.status ?? report.status ?? "unknown",
15271
+ selected_domains: Array.isArray(bundle.manifest.selected_domains) ? bundle.manifest.selected_domains : [],
15272
+ failures: Array.isArray(report.failures) ? report.failures : [],
15273
+ redacted_secret_categories: ACCOUNT_EXPORT_REDACTION_CATEGORIES,
15274
+ partial_requires_acceptance: Boolean(report.partial_requires_acceptance)
15275
+ };
15276
+ }
15277
+ function collectAccountExportDomainPayloads(chunks) {
15278
+ const grouped = {};
15279
+ for (const chunk of chunks) {
15280
+ const domain = safeArchiveSegment(String(chunk.domain ?? "unknown"));
15281
+ const payload = chunk.payload && typeof chunk.payload === "object" && !Array.isArray(chunk.payload) ? chunk.payload : { value: chunk.payload ?? null };
15282
+ grouped[domain] = grouped[domain] ?? [];
15283
+ grouped[domain].push({
15284
+ chunk_id: chunk.chunk_id ?? null,
15285
+ sequence: chunk.sequence ?? null,
15286
+ status: chunk.status ?? null,
15287
+ ...payload
15288
+ });
15289
+ }
15290
+ return grouped;
15291
+ }
15292
+ async function writeAccountExportChatFiles(chatPayloads, writeArchiveText) {
15293
+ const chats = [];
15294
+ for (const payload of chatPayloads) {
15295
+ if (Array.isArray(payload.items)) {
15296
+ chats.push(...payload.items.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item)));
15297
+ }
15298
+ }
15299
+ for (const [index, chat] of chats.entries()) {
15300
+ const chatId = safeArchiveSegment(String(chat.id ?? chat.chat_id ?? `chat-${index + 1}`));
15301
+ await writeArchiveText(`chats/${chatId}.yml`, serializeArchiveYaml({ chat, messages: Array.isArray(chat.messages) ? chat.messages : [] }));
15302
+ await writeArchiveText(`chats/${chatId}.md`, buildAccountExportChatMarkdown(chat));
15303
+ }
15304
+ }
15305
+ function buildAccountExportChatMarkdown(chat) {
15306
+ const title = String(chat.title ?? chat.name ?? chat.id ?? chat.chat_id ?? "Untitled Chat");
15307
+ const lines = [`# ${title}`, ""];
15308
+ if (typeof chat.summary === "string" && chat.summary.trim()) {
15309
+ lines.push("## Summary", "", chat.summary.trim(), "");
15310
+ }
15311
+ const messages = Array.isArray(chat.messages) ? chat.messages : [];
15312
+ if (messages.length > 0) {
15313
+ lines.push("## Messages", "");
15314
+ for (const message of messages) {
15315
+ if (!message || typeof message !== "object" || Array.isArray(message)) continue;
15316
+ const record = message;
15317
+ lines.push(`### ${String(record.role ?? record.sender ?? "message")}`, "", String(record.content ?? record.text ?? ""), "");
15318
+ }
15319
+ } else {
15320
+ lines.push("No readable message records were included in this export chunk.", "");
15321
+ }
15322
+ return `${lines.join("\n")}
15323
+ `;
15324
+ }
15325
+ function serializeArchiveYaml(data, indent = 0) {
15326
+ const pad2 = " ".repeat(indent);
15327
+ let out = "";
15328
+ for (const [key, val] of Object.entries(data)) {
15329
+ if (val === null || val === void 0) {
15330
+ out += `${pad2}${key}: null
15331
+ `;
15332
+ } else if (typeof val === "boolean" || typeof val === "number") {
15333
+ out += `${pad2}${key}: ${val}
15334
+ `;
15335
+ } else if (typeof val === "string") {
15336
+ out += val.includes("\n") ? `${pad2}${key}: |
15337
+ ${val.split("\n").map((line) => `${pad2} ${line}`).join("\n")}
15338
+ ` : `${pad2}${key}: ${quoteYamlString(val)}
15339
+ `;
15340
+ } else if (Array.isArray(val)) {
15341
+ out += `${pad2}${key}:
15342
+ `;
15343
+ for (const item of val) {
15344
+ if (item && typeof item === "object" && !Array.isArray(item)) {
15345
+ out += `${pad2}-
15346
+ ${serializeArchiveYaml(item, indent + 2)}`;
15347
+ } else {
15348
+ out += `${pad2}- ${typeof item === "string" ? quoteYamlString(item) : String(item)}
15349
+ `;
15350
+ }
15351
+ }
15352
+ } else if (typeof val === "object") {
15353
+ out += `${pad2}${key}:
15354
+ ${serializeArchiveYaml(val, indent + 1)}`;
15355
+ }
15356
+ }
15357
+ return out;
15358
+ }
15359
+ function quoteYamlString(value) {
15360
+ const needsQuote = value.includes(":") || value.includes("#") || value.startsWith("{") || value.startsWith("[") || value === "" || ["true", "false", "null"].includes(value);
15361
+ return needsQuote ? `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : value;
15362
+ }
15363
+ function safeArchiveSegment(value) {
15364
+ const cleaned = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
15365
+ return cleaned || "unknown";
15366
+ }
15367
+
15125
15368
  // src/sdk.ts
15126
15369
  var DEFAULT_API_URL2 = "https://api.openmates.org";
15127
15370
  var DEFAULT_RECOVERY_POLL_INTERVAL_MS = 500;
@@ -15934,7 +16177,9 @@ var OpenMatesAccount = class {
15934
16177
  }
15935
16178
  async exportChunk(exportId, chunkId) {
15936
16179
  const result = await this.client.get(`/v1/account-exports/${encodeURIComponent(exportId)}/chunks/${encodeURIComponent(chunkId)}`);
15937
- return result.chunk ?? {};
16180
+ const chunk = result.chunk ?? {};
16181
+ assertAccountExportPayloadSafe(chunk);
16182
+ return chunk;
15938
16183
  }
15939
16184
  async *iterExportChunks(exportId) {
15940
16185
  const listed = await this.exportChunks(exportId);
@@ -15959,8 +16204,23 @@ var OpenMatesAccount = class {
15959
16204
  this.exportJobManifest(exportId),
15960
16205
  this.exportChunks(exportId)
15961
16206
  ]);
15962
- const completed = await this.completeExport(exportId);
15963
- return { export: completed.export, manifest: manifest.manifest, chunks: chunks.chunks };
16207
+ const downloadedChunks = [];
16208
+ try {
16209
+ for (const chunk of chunks.chunks) {
16210
+ const chunkId = String(chunk.chunk_id ?? "");
16211
+ downloadedChunks.push(chunkId ? await this.exportChunk(exportId, chunkId) : chunk);
16212
+ }
16213
+ } catch (error) {
16214
+ await this.cancelExport(exportId).catch(() => void 0);
16215
+ throw error;
16216
+ }
16217
+ let completed = await this.completeExport(exportId);
16218
+ const status = String(completed.export.status ?? "");
16219
+ if (status === "partial") {
16220
+ if (options.acceptPartial !== true) throw new Error(`Account export ${exportId} is partial. Pass acceptPartial: true to accept it explicitly.`);
16221
+ completed = await this.acceptPartialExport(exportId);
16222
+ }
16223
+ return { export: completed.export, manifest: sanitizeAccountExportManifest(manifest.manifest), chunks: downloadedChunks };
15964
16224
  }
15965
16225
  async exportManifest() {
15966
16226
  return this.client.get("/v1/sdk/account/export/manifest");
@@ -21788,243 +22048,6 @@ function redactUrl(rawUrl) {
21788
22048
  return url.toString();
21789
22049
  }
21790
22050
 
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
-
22028
22051
  // ../ui/src/demo_chats/data/example_chats/gigantic-airplanes.ts
22029
22052
  var giganticAirplanesChat = {
22030
22053
  chat_id: "example-gigantic-airplanes",
@@ -63099,6 +63122,8 @@ var SETTINGS_EXECUTABLE_COMMANDS = [
63099
63122
  { path: ["account", "export", "manifest"], description: "Show account export manifest", examples: ["openmates settings account export manifest <export-id> --json"] },
63100
63123
  { path: ["account", "export", "chunks"], description: "List account export chunks", examples: ["openmates settings account export chunks <export-id> --json"] },
63101
63124
  { path: ["account", "export", "data"], description: "Fetch account export chunks", examples: ["openmates settings account export data <export-id> --json"] },
63125
+ { path: ["account", "export", "accept-partial"], description: "Accept a partial account export", examples: ["openmates settings account export accept-partial <export-id> --json"] },
63126
+ { path: ["account", "export", "cancel"], description: "Cancel an account export job", examples: ["openmates settings account export cancel <export-id> --json"] },
63102
63127
  { path: ["account", "import-chat"], description: "Import a CLI chat export file", examples: ["openmates settings account import-chat ./chat.yml", "openmates settings account import-chat ./payload.json"] },
63103
63128
  { path: ["account", "username", "set"], description: "Change account username", examples: ["openmates settings account username set alice_123"] },
63104
63129
  { path: ["account", "profile-picture", "set"], description: "Upload a profile picture", examples: ["openmates settings account profile-picture set ./avatar.jpg"] },
@@ -63958,7 +63983,7 @@ async function handleSettings(client, subcommand, rest, flags) {
63958
63983
  printTopicPreferences(preferences, flags, "Interests cleared");
63959
63984
  return;
63960
63985
  }
63961
- if (matches(tokens, ["account", "export"]) || matches(tokens, ["account", "export", "start"])) {
63986
+ if (tokens.length === 2 && matches(tokens, ["account", "export"]) || tokens.length === 3 && matches(tokens, ["account", "export", "start"])) {
63962
63987
  printAccountExportBundle(await runAccountExport(client, flags), flags);
63963
63988
  return;
63964
63989
  }
@@ -63989,7 +64014,26 @@ async function handleSettings(client, subcommand, rest, flags) {
63989
64014
  await printSettingsResult(client.settingsGet("export-account-data"), flags);
63990
64015
  return;
63991
64016
  }
63992
- await printSettingsResult(client.listAccountExportChunks(exportId), flags);
64017
+ const listed = await client.listAccountExportChunks(exportId);
64018
+ const chunks = [];
64019
+ for (const chunk of listed.chunks) {
64020
+ const chunkId = String(chunk.chunk_id ?? "");
64021
+ chunks.push(chunkId ? await client.getAccountExportChunk(exportId, chunkId) : chunk);
64022
+ }
64023
+ if (flags.json === true) printJson2({ chunks });
64024
+ else printGenericObject({ chunks });
64025
+ return;
64026
+ }
64027
+ if (matches(tokens, ["account", "export", "accept-partial"])) {
64028
+ const exportId = rest[2];
64029
+ if (!exportId) throw new Error("Missing export ID. Example: openmates settings account export accept-partial <export-id>");
64030
+ await printSettingsResult(client.acceptPartialAccountExport(exportId), flags);
64031
+ return;
64032
+ }
64033
+ if (matches(tokens, ["account", "export", "cancel"])) {
64034
+ const exportId = rest[2];
64035
+ if (!exportId) throw new Error("Missing export ID. Example: openmates settings account export cancel <export-id>");
64036
+ await printSettingsResult(client.cancelAccountExport(exportId), flags);
63993
64037
  return;
63994
64038
  }
63995
64039
  if (matches(tokens, ["account", "import-chat"])) {
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getExtForLang,
4
4
  serializeToYaml
5
- } from "./chunk-N2TP5WZI.js";
5
+ } from "./chunk-PAGWFAZH.js";
6
6
  import "./chunk-AXNRPVLE.js";
7
7
  export {
8
8
  getExtForLang,
package/dist/index.d.ts CHANGED
@@ -5164,6 +5164,7 @@ interface AccountExportStartOptions {
5164
5164
  filters?: Record<string, unknown>;
5165
5165
  format?: "zip" | "directory";
5166
5166
  includeAdvancedMetadata?: boolean;
5167
+ acceptPartial?: boolean;
5167
5168
  }
5168
5169
  interface AccountExportResponse {
5169
5170
  export: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  reconcileAuthoritativeChats,
19
19
  renderSupportInfo,
20
20
  serializeToYaml
21
- } from "./chunk-N2TP5WZI.js";
21
+ } from "./chunk-PAGWFAZH.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.39",
3
+ "version": "0.15.0-alpha.40",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -68,6 +68,7 @@
68
68
  "@resvg/resvg-js": "^2.6.2",
69
69
  "@toon-format/toon": "2.1.0",
70
70
  "ahocorasick": "1.0.2",
71
+ "jszip": "^3.10.1",
71
72
  "qrcode-terminal": "^0.12.0",
72
73
  "tweetnacl": "^1.0.3",
73
74
  "ws": "8.21.0"