openmates 0.27.0-alpha.3 → 0.27.0-alpha.5

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.
@@ -1859,6 +1859,7 @@ function purgeLocalTeamKeys(hashedEmail) {
1859
1859
  writeJsonFile(filePath, keys);
1860
1860
  }
1861
1861
  function purgeSyncCaches(stateDir2) {
1862
+ rmSync(join2(stateDir2, "project_file_privacy"), { recursive: true, force: true });
1862
1863
  for (const fileName of readdirSync(stateDir2)) {
1863
1864
  if (fileName === SYNC_CACHE_FILE || fileName.startsWith("sync_cache.team.") && fileName.endsWith(".json")) {
1864
1865
  rmSync(join2(stateDir2, fileName), { force: true });
@@ -2448,7 +2449,7 @@ function assertCodexClaimCaller(threadId, currentThread = process.env.CODEX_THRE
2448
2449
  }
2449
2450
 
2450
2451
  // src/client.ts
2451
- import { createHash as createHash14, randomBytes as randomBytes5, randomUUID as randomUUID10, timingSafeEqual } from "crypto";
2452
+ import { createHash as createHash15, randomBytes as randomBytes5, randomUUID as randomUUID10, timingSafeEqual } from "crypto";
2452
2453
  import { arch, platform as platform2, release } from "os";
2453
2454
  import { createInterface as createInterface2 } from "readline/promises";
2454
2455
  import { stdin as stdin2, stdout } from "process";
@@ -3256,7 +3257,9 @@ function resolveServerPath(flags) {
3256
3257
  }
3257
3258
 
3258
3259
  // src/projectFileExecutor.ts
3259
- import { randomUUID as randomUUID6 } from "crypto";
3260
+ import { randomUUID as randomUUID6, createHash as createHash10 } from "crypto";
3261
+ import { mkdir as mkdir2, readFile, writeFile, rename as rename2 } from "fs/promises";
3262
+ import { join as join6 } from "path";
3260
3263
 
3261
3264
  // src/projectRequester.ts
3262
3265
  import { randomUUID as randomUUID4 } from "crypto";
@@ -3633,12 +3636,14 @@ function createProjectFileJobExecutor(options) {
3633
3636
  let proposalCommitment;
3634
3637
  if (isProjectFileMutationOperation(job.operation)) {
3635
3638
  mutation = validateProjectFileMutation({ ...job.arguments, operation: job.operation, operation_id: job.operation_id });
3639
+ if (context.privacy) mutation = context.privacy.restoreMutation(mutation);
3636
3640
  if (!context.writeMode) throw Object.assign(new Error(), { code: "write_policy_required" });
3637
3641
  const digest3 = await projectFileMutationDigest(context.projectKey, job.project_id, job.chat_id, mutation);
3638
3642
  proposalCommitment = digest3;
3639
3643
  if (context.writeMode === "always_ask" && approved.get(job.operation_id) !== digest3) {
3640
3644
  const approvalRequest = { projectId: job.project_id, chatId: job.chat_id, mutation };
3641
- await result("awaiting_approval", { proposal_commitment: digest3, proposal: mutation });
3645
+ const proposal = context.privacy ? await context.privacy.redactResult(mutation) : mutation;
3646
+ await result("awaiting_approval", { proposal_commitment: digest3, proposal });
3642
3647
  leaseReleased = true;
3643
3648
  options.onWaitingForUser?.(approvalRequest);
3644
3649
  if (!options.requestApproval) return;
@@ -3656,7 +3661,8 @@ function createProjectFileJobExecutor(options) {
3656
3661
  }
3657
3662
  }
3658
3663
  if (job.lease_expires_at * 1e3 <= Date.now()) throw Object.assign(new Error(), { code: "lease_expired" });
3659
- const requestedPath = job.operation === "read_text" && typeof job.arguments.path === "string" ? job.arguments.path : null;
3664
+ const executionJob = context.privacy ? { ...job, arguments: context.privacy.restoreArguments(job.arguments) } : job;
3665
+ const requestedPath = job.operation === "read_text" && typeof executionJob.arguments.path === "string" ? executionJob.arguments.path : null;
3660
3666
  const readApprovalRequest = requestedPath ? {
3661
3667
  projectId: job.project_id,
3662
3668
  sourceId: context.sourceId !== void 0 ? context.sourceId : job.source_id ?? null,
@@ -3668,14 +3674,15 @@ function createProjectFileJobExecutor(options) {
3668
3674
  let output;
3669
3675
  try {
3670
3676
  output = await context.execute(
3671
- job,
3677
+ executionJob,
3672
3678
  mutation,
3673
3679
  readApprovalRequest && readApprovalKey && approvedIgnoredReads.has(readApprovalKey) ? { path: readApprovalRequest.path, chatId: job.chat_id, operationId: job.operation_id } : void 0
3674
3680
  );
3675
3681
  } catch (error) {
3676
3682
  const code = error?.code;
3677
3683
  if (code !== "ignored_path_requires_approval" || !readApprovalRequest || !readApprovalKey) throw error;
3678
- await result("awaiting_approval", { reason: "ignored_path_requires_approval", path: readApprovalRequest.path });
3684
+ const waiting = { reason: "ignored_path_requires_approval", path: readApprovalRequest.path };
3685
+ await result("awaiting_approval", context.privacy ? await context.privacy.redactResult(waiting) : waiting);
3679
3686
  leaseReleased = true;
3680
3687
  options.onWaitingForRead?.(readApprovalRequest);
3681
3688
  if (!options.requestReadApproval) return;
@@ -3691,7 +3698,8 @@ function createProjectFileJobExecutor(options) {
3691
3698
  return;
3692
3699
  }
3693
3700
  approved.delete(job.operation_id);
3694
- await result("completed", { ...output && typeof output === "object" ? output : { value: output }, ...proposalCommitment ? { proposal_commitment: proposalCommitment } : {} });
3701
+ const safeOutput = context.privacy ? await context.privacy.redactResult(output) : output;
3702
+ await result("completed", { ...safeOutput && typeof safeOutput === "object" ? safeOutput : { value: safeOutput }, ...proposalCommitment ? { proposal_commitment: proposalCommitment } : {} });
3695
3703
  if (mutation) {
3696
3704
  try {
3697
3705
  options.onMutationApplied?.({ projectId: job.project_id, chatId: job.chat_id, mutation });
@@ -4590,6 +4598,746 @@ async function encryptEmbed(embed, masterKey, chatKey, chatId, messageId, userId
4590
4598
  }
4591
4599
  }
4592
4600
 
4601
+ // ../secret-scanner/src/identifierRanges.ts
4602
+ function codingIdentifierRanges(text4) {
4603
+ const ranges = [];
4604
+ const identifiers2 = /\b[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\b|\b[\da-f]{40,64}\b|\[[A-Z][A-Z\d_]*\]/gi;
4605
+ for (const match of Array.from(text4.matchAll(identifiers2))) {
4606
+ ranges.push({ start: match.index, end: match.index + match[0].length });
4607
+ }
4608
+ return ranges;
4609
+ }
4610
+
4611
+ // ../ui/src/components/enter_message/services/piiDetectionService.ts
4612
+ function luhnCheck(cardNumber) {
4613
+ const digits = cardNumber.replace(/\D/g, "");
4614
+ if (digits.length < 13 || digits.length > 19) return false;
4615
+ let sum = 0;
4616
+ let isEven = false;
4617
+ for (let i = digits.length - 1; i >= 0; i--) {
4618
+ let digit = parseInt(digits[i], 10);
4619
+ if (isEven) {
4620
+ digit *= 2;
4621
+ if (digit > 9) digit -= 9;
4622
+ }
4623
+ sum += digit;
4624
+ isEven = !isEven;
4625
+ }
4626
+ return sum % 10 === 0;
4627
+ }
4628
+ function ibanCheck(iban) {
4629
+ const cleaned = iban.replace(/\s/g, "").toUpperCase();
4630
+ if (cleaned.length < 15 || cleaned.length > 34) return false;
4631
+ const rearranged = cleaned.substring(4) + cleaned.substring(0, 4);
4632
+ let numericStr = "";
4633
+ for (const char of rearranged) {
4634
+ const code = char.charCodeAt(0);
4635
+ if (code >= 65 && code <= 90) {
4636
+ numericStr += (code - 55).toString();
4637
+ } else {
4638
+ numericStr += char;
4639
+ }
4640
+ }
4641
+ let remainder = 0;
4642
+ for (const digit of numericStr) {
4643
+ remainder = (remainder * 10 + parseInt(digit, 10)) % 97;
4644
+ }
4645
+ return remainder === 1;
4646
+ }
4647
+ function getSecretSuffix(match, n = 3) {
4648
+ const cleaned = match.replace(/['";\s]+$/g, "");
4649
+ if (cleaned.length <= n) return cleaned;
4650
+ return cleaned.slice(-n);
4651
+ }
4652
+ function generateMatchId(type, startIndex) {
4653
+ return `pii-${type}-${startIndex}`;
4654
+ }
4655
+ var PII_PATTERNS = [
4656
+ // API Keys (most specific - check first to avoid partial matches)
4657
+ {
4658
+ type: "AWS_ACCESS_KEY",
4659
+ regex: /\bAKIA[0-9A-Z]{16}\b/g,
4660
+ label: "AWS Access Key",
4661
+ getPlaceholder: (match, counter) => `[AWS_KEY_${counter}_${getSecretSuffix(match)}]`
4662
+ },
4663
+ {
4664
+ type: "OPENAI_KEY",
4665
+ // Matches: sk-proj-..., sk-svcacct-..., sk-... (legacy)
4666
+ regex: /\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,200}\b/g,
4667
+ label: "OpenAI API Key",
4668
+ getPlaceholder: (match, counter) => `[OPENAI_KEY_${counter}_${getSecretSuffix(match)}]`
4669
+ },
4670
+ {
4671
+ type: "ANTHROPIC_KEY",
4672
+ regex: /\bsk-ant-api03-[A-Za-z0-9_-]{90,110}\b/g,
4673
+ label: "Anthropic API Key",
4674
+ getPlaceholder: (match, counter) => `[ANTHROPIC_KEY_${counter}_${getSecretSuffix(match)}]`
4675
+ },
4676
+ {
4677
+ type: "GITHUB_PAT",
4678
+ // Classic PAT, fine-grained PAT, and OAuth tokens
4679
+ regex: /\b(?:ghp_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}|gho_[a-zA-Z0-9]{36})\b/g,
4680
+ label: "GitHub Token",
4681
+ getPlaceholder: (match, counter) => `[GITHUB_TOKEN_${counter}_${getSecretSuffix(match)}]`
4682
+ },
4683
+ {
4684
+ type: "STRIPE_KEY",
4685
+ // Live, test, and restricted keys
4686
+ regex: /\b[sr]k_(?:live|test)_[0-9a-zA-Z]{24,99}\b/g,
4687
+ label: "Stripe API Key",
4688
+ getPlaceholder: (match, counter) => `[STRIPE_KEY_${counter}_${getSecretSuffix(match)}]`
4689
+ },
4690
+ {
4691
+ type: "GOOGLE_API_KEY",
4692
+ regex: /\bAIza[0-9A-Za-z\-_]{35}\b/g,
4693
+ label: "Google API Key",
4694
+ getPlaceholder: (match, counter) => `[GOOGLE_KEY_${counter}_${getSecretSuffix(match)}]`
4695
+ },
4696
+ {
4697
+ type: "SLACK_TOKEN",
4698
+ // Bot, user, and app tokens
4699
+ regex: /\bxox[bpras]-[0-9a-zA-Z-]{10,250}\b/g,
4700
+ label: "Slack Token",
4701
+ getPlaceholder: (match, counter) => `[SLACK_TOKEN_${counter}_${getSecretSuffix(match)}]`
4702
+ },
4703
+ {
4704
+ type: "AWS_SECRET_KEY",
4705
+ // 40-character base64-like string - only match if preceded by common context keywords
4706
+ // This reduces false positives significantly
4707
+ regex: /(?:aws_secret|secret_key|secretkey|secret_access_key)['":\s=]+([0-9a-zA-Z/+=]{40})\b/gi,
4708
+ label: "AWS Secret Key",
4709
+ getPlaceholder: (match, counter) => `[AWS_SECRET_${counter}_${getSecretSuffix(match)}]`
4710
+ },
4711
+ {
4712
+ type: "TWILIO_KEY",
4713
+ // Twilio Account SID (AC...) and Auth Token (SK...)
4714
+ regex: /\b(?:AC[a-f0-9]{32}|SK[a-f0-9]{32})\b/g,
4715
+ label: "Twilio Key",
4716
+ getPlaceholder: (match, counter) => `[TWILIO_KEY_${counter}_${getSecretSuffix(match)}]`
4717
+ },
4718
+ {
4719
+ type: "SENDGRID_KEY",
4720
+ // SendGrid API keys start with SG.
4721
+ regex: /\bSG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}\b/g,
4722
+ label: "SendGrid API Key",
4723
+ getPlaceholder: (match, counter) => `[SENDGRID_KEY_${counter}_${getSecretSuffix(match)}]`
4724
+ },
4725
+ {
4726
+ type: "AZURE_KEY",
4727
+ // Azure subscription keys are 32-character hex strings, matched only when
4728
+ // preceded by a contextual keyword to avoid false positives on random hex.
4729
+ regex: /(?:azure|subscription|ocp-apim)[_-]?(?:key|secret|token)['":\s=]+([0-9a-f]{32})\b/gi,
4730
+ label: "Azure Key",
4731
+ getPlaceholder: (match, counter) => `[AZURE_KEY_${counter}_${getSecretSuffix(match)}]`
4732
+ },
4733
+ {
4734
+ type: "HUGGINGFACE_KEY",
4735
+ // Hugging Face API tokens: hf_...
4736
+ regex: /\bhf_[a-zA-Z0-9]{34,}\b/g,
4737
+ label: "Hugging Face Token",
4738
+ getPlaceholder: (match, counter) => `[HF_TOKEN_${counter}_${getSecretSuffix(match)}]`
4739
+ },
4740
+ {
4741
+ type: "DATABRICKS_TOKEN",
4742
+ // Databricks personal access tokens: dapi...
4743
+ regex: /\bdapi[a-f0-9]{32,40}\b/g,
4744
+ label: "Databricks Token",
4745
+ getPlaceholder: (match, counter) => `[DATABRICKS_TOKEN_${counter}_${getSecretSuffix(match)}]`
4746
+ },
4747
+ {
4748
+ type: "FIREBASE_KEY",
4749
+ // Firebase server keys typically start with AAAA and are ~152 chars
4750
+ regex: /\bAAAA[A-Za-z0-9_-]{100,200}\b/g,
4751
+ label: "Firebase Key",
4752
+ getPlaceholder: (match, counter) => `[FIREBASE_KEY_${counter}_${getSecretSuffix(match)}]`
4753
+ },
4754
+ {
4755
+ type: "GENERIC_SECRET",
4756
+ // Generic secret/key/token/password patterns — catches assignment-style secrets
4757
+ // that don't match any vendor-specific pattern above. Requires a keyword like
4758
+ // "api_key", "secret", "password", "token", "auth", "credential", "access_key",
4759
+ // followed by an assignment operator and a value of 8+ alphanumeric characters.
4760
+ // The value must be 8+ chars to avoid matching trivial assignments like key="id".
4761
+ regex: /(?:api[_-]?key|api[_-]?secret|secret[_-]?key|auth[_-]?token|access[_-]?token|bearer[_-]?token|private[_-]?key|password|passwd|credential|client[_-]?secret|app[_-]?secret|signing[_-]?key|encryption[_-]?key)['":\s=]+['"]?([A-Za-z0-9_\-/.+=]{8,200})['"]?/gi,
4762
+ label: "Secret/Credential",
4763
+ getPlaceholder: (match, counter) => `[SECRET_${counter}_${getSecretSuffix(match)}]`
4764
+ },
4765
+ // Private keys and tokens
4766
+ {
4767
+ type: "PRIVATE_KEY",
4768
+ regex: /-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,
4769
+ label: "Private Key",
4770
+ getPlaceholder: (match, counter) => `[PRIVATE_KEY_${counter}_${getSecretSuffix(match)}]`
4771
+ },
4772
+ {
4773
+ type: "JWT",
4774
+ // JWT format: base64.base64.base64
4775
+ regex: /\beyJ[A-Za-z0-9_-]*\.eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+/g,
4776
+ label: "JWT Token",
4777
+ getPlaceholder: (match, counter) => `[JWT_TOKEN_${counter}_${getSecretSuffix(match)}]`
4778
+ },
4779
+ // Personal identifiers
4780
+ {
4781
+ type: "EMAIL",
4782
+ regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
4783
+ label: "Email Address",
4784
+ getPlaceholder: (match, counter) => `[EMAIL_${counter}_${getSecretSuffix(match)}]`
4785
+ },
4786
+ {
4787
+ type: "CREDIT_CARD",
4788
+ // Major card formats: Visa, Mastercard, Amex, Discover
4789
+ // With optional spaces or dashes between groups
4790
+ regex: /\b(?:4[0-9]{3}[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|5[1-5][0-9]{2}[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4}|3[47][0-9]{2}[-\s]?[0-9]{6}[-\s]?[0-9]{5}|6(?:011|5[0-9]{2})[-\s]?[0-9]{4}[-\s]?[0-9]{4}[-\s]?[0-9]{4})\b/g,
4791
+ label: "Credit Card",
4792
+ getPlaceholder: (match, counter) => `[CARD_${counter}_${getSecretSuffix(match)}]`,
4793
+ validate: luhnCheck
4794
+ },
4795
+ {
4796
+ type: "SSN",
4797
+ // US Social Security Number: XXX-XX-XXXX or XXX XX XXXX or XXXXXXXXX
4798
+ regex: /\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/g,
4799
+ label: "SSN",
4800
+ getPlaceholder: (match, counter) => `[SSN_${counter}_${getSecretSuffix(match)}]`,
4801
+ // Additional validation: first 3 digits can't be 000, 666, or 900-999
4802
+ validate: (match) => {
4803
+ const digits = match.replace(/\D/g, "");
4804
+ if (digits.length !== 9) return false;
4805
+ const area = parseInt(digits.substring(0, 3), 10);
4806
+ if (area === 0 || area === 666 || area >= 900) return false;
4807
+ const group = parseInt(digits.substring(3, 5), 10);
4808
+ if (group === 0) return false;
4809
+ const serial = parseInt(digits.substring(5, 9), 10);
4810
+ if (serial === 0) return false;
4811
+ return true;
4812
+ }
4813
+ },
4814
+ {
4815
+ type: "PHONE",
4816
+ // Comprehensive phone number detection covering multiple international formats:
4817
+ //
4818
+ // Branch 1 — International with country code (+ or 00 prefix):
4819
+ // Matches +49 2123 21234, +44 20 7946 0958, +1 (555) 123-4567,
4820
+ // 0049 2123 21234, 0044-20-7946-0958, +492123212234 (E.164)
4821
+ // Requires country code digit [1-9] then 6-14 more digits with optional
4822
+ // separators (space, dash, dot, slash, parens) between groups.
4823
+ //
4824
+ // Branch 2 — US/Canada format without country code:
4825
+ // Matches (555) 123-4567, 555-123-4567, 555.123.4567
4826
+ // Area code starts with [2-9] per NANP rules.
4827
+ //
4828
+ // Branch 3 — Local format with leading 0 (common in DE, UK, FR, etc.):
4829
+ // Matches 01123/121/31222, 030 1234 5678, 0171-123-4567
4830
+ // Leading 0 + at least 7 more digits with separators.
4831
+ //
4832
+ // Validation function filters false positives (plain numbers, years, etc.)
4833
+ regex: /(?:(?:\+|00)[1-9]\d{0,2}[-.\s/]?(?:\(?\d{1,5}\)?[-.\s/]?){1,4}\d{2,4})|(?:\+?1[-.\s]?)?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}|(?:0\d[-.\s/]?(?:\(?\d{1,5}\)?[-.\s/]?){1,4}\d{2,4})/g,
4834
+ label: "Phone Number",
4835
+ getPlaceholder: (match, counter) => `[PHONE_${counter}_${getSecretSuffix(match)}]`,
4836
+ // Validate: require at least 7 digits total (country code excluded) to avoid
4837
+ // matching short number-like strings (zip codes, years, short IDs).
4838
+ validate: (match) => {
4839
+ const trimmed = match.trim();
4840
+ const digits = trimmed.replace(/\D/g, "");
4841
+ if (digits.length < 7 || digits.length > 15) return false;
4842
+ if (/^\d{4}$/.test(trimmed)) return false;
4843
+ if (/^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}$/.test(trimmed)) return false;
4844
+ if (/^\d{1,2}[-/.]\d{1,2}[-/.]\d{4}$/.test(trimmed)) return false;
4845
+ if (/^(?:19|20)\d{6}$/.test(trimmed)) return false;
4846
+ if (/^0\d{1,2}[-/.]\d{1,2}[-/.]\d{1,2}$/.test(trimmed)) return false;
4847
+ return true;
4848
+ }
4849
+ },
4850
+ // IP Addresses
4851
+ {
4852
+ type: "IPV4",
4853
+ regex: /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g,
4854
+ label: "IP Address",
4855
+ getPlaceholder: (match, counter) => `[IP_${counter}_${getSecretSuffix(match)}]`,
4856
+ // Exclude common non-sensitive IPs
4857
+ validate: (match) => {
4858
+ if (match === "127.0.0.1" || match === "0.0.0.0") return false;
4859
+ if (match.startsWith("192.168.") || match.startsWith("10.") || match.startsWith("172."))
4860
+ return false;
4861
+ return true;
4862
+ }
4863
+ },
4864
+ {
4865
+ type: "IPV6",
4866
+ // Full IPv6 format
4867
+ regex: /\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b/g,
4868
+ label: "IPv6 Address",
4869
+ getPlaceholder: (match, counter) => `[IPV6_${counter}_${getSecretSuffix(match)}]`
4870
+ },
4871
+ // Bank account numbers (IBAN)
4872
+ {
4873
+ type: "IBAN",
4874
+ // IBAN format: 2-letter country code + 2 check digits + up to 30 alphanumeric chars.
4875
+ // Supports both compact (DE89370400440532013000) and spaced (DE89 3704 0044 0532 0130 00).
4876
+ // Country code is required to reduce false positives on random alphanumeric strings.
4877
+ regex: /\b[A-Z]{2}\d{2}[\s]?[\dA-Z]{4}[\s]?(?:[\dA-Z]{4}[\s]?){1,7}[\dA-Z]{1,4}\b/g,
4878
+ label: "IBAN",
4879
+ getPlaceholder: (match, counter) => `[IBAN_${counter}_${getSecretSuffix(match)}]`,
4880
+ validate: ibanCheck
4881
+ },
4882
+ // Home folder / user directory paths that leak the local username.
4883
+ // /home/marco/projects/foo, /Users/marco/.ssh/config, C:\Users\Marco\Documents
4884
+ // PS C:\Users\Marco>, PS /home/marco>
4885
+ {
4886
+ type: "HOME_FOLDER",
4887
+ // Two branches:
4888
+ // Branch 1 — Unix/macOS/Windows home directory paths:
4889
+ // /home/user, /Users/user, C:\Users\user
4890
+ // Username: 1-64 alphanumeric/dash/underscore/dot chars.
4891
+ // Requires trailing slash, backslash, or word boundary.
4892
+ //
4893
+ // Branch 2 — PowerShell prompt with home path:
4894
+ // PS C:\Users\user> or PS /home/user>
4895
+ regex: /(?:\/home\/|\/Users\/|[A-Z]:\\Users\\)[a-zA-Z0-9_.-]{1,64}(?=[/\\]|\b)|PS [A-Z]:\\Users\\[a-zA-Z0-9_.-]{1,64}(?=[\\>]|\b)|PS \/(?:home|Users)\/[a-zA-Z0-9_.-]{1,64}(?=[/>]|\b)/g,
4896
+ label: "Home Folder",
4897
+ getPlaceholder: (match, counter) => `[HOME_PATH_${counter}_${getSecretSuffix(match)}]`,
4898
+ // Exclude common system/service accounts that are not personal
4899
+ validate: (match) => {
4900
+ const username = match.replace(/^(?:PS\s+)?(?:\/home\/|\/Users\/|[A-Z]:\\Users\\)/i, "").toLowerCase();
4901
+ const systemAccounts = /* @__PURE__ */ new Set([
4902
+ "root",
4903
+ "admin",
4904
+ "shared",
4905
+ "public",
4906
+ "default",
4907
+ "guest",
4908
+ "nobody",
4909
+ "daemon",
4910
+ "www-data",
4911
+ "ubuntu"
4912
+ ]);
4913
+ return !systemAccounts.has(username);
4914
+ }
4915
+ },
4916
+ // Terminal prompt user@hostname patterns that leak username and machine name.
4917
+ // marco@Marcos-MacBook-Pro-3 ~ % (zsh on macOS)
4918
+ // marco@devserver:~/projects$ (bash on Linux)
4919
+ // [marco@centos8 ~]$ (RHEL/CentOS bash)
4920
+ {
4921
+ type: "USER_AT_HOSTNAME",
4922
+ // Matches user@hostname followed by common terminal prompt context characters
4923
+ // (colon, space, tilde, ~). The hostname may contain dots (FQDN) and dashes.
4924
+ // The lookahead for [:~ \s] ensures we only match in prompt-like contexts,
4925
+ // not in email addresses (which are caught earlier by the EMAIL pattern
4926
+ // due to pattern ordering — EMAIL is checked before USER_AT_HOSTNAME).
4927
+ regex: /\b[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,31}@[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,63}(?=[\s:~])/g,
4928
+ label: "User@Hostname",
4929
+ getPlaceholder: (match, counter) => `[USER_HOST_${counter}_${getSecretSuffix(match)}]`,
4930
+ // Exclude well-known service accounts and non-personal user@host patterns
4931
+ validate: (match) => {
4932
+ const username = match.split("@")[0].toLowerCase();
4933
+ const host = (match.split("@")[1] ?? "").toLowerCase();
4934
+ const serviceHosts = /* @__PURE__ */ new Set([
4935
+ "github.com",
4936
+ "gitlab.com",
4937
+ "bitbucket.org",
4938
+ "ssh.dev.azure.com"
4939
+ ]);
4940
+ if (serviceHosts.has(host)) return false;
4941
+ const systemAccounts = /* @__PURE__ */ new Set([
4942
+ "root",
4943
+ "admin",
4944
+ "guest",
4945
+ "nobody",
4946
+ "daemon",
4947
+ "www-data",
4948
+ "noreply",
4949
+ "no-reply",
4950
+ "git",
4951
+ "svn",
4952
+ "user",
4953
+ "test",
4954
+ "ubuntu"
4955
+ ]);
4956
+ return !systemAccounts.has(username);
4957
+ }
4958
+ },
4959
+ // MAC addresses (network hardware identifiers)
4960
+ {
4961
+ type: "MAC_ADDRESS",
4962
+ // Standard MAC address formats:
4963
+ // - Colon-separated: AA:BB:CC:DD:EE:FF
4964
+ // - Dash-separated: AA-BB-CC-DD-EE-FF
4965
+ // Case-insensitive, requires word boundaries to avoid matching inside hex strings.
4966
+ regex: /\b(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\b/g,
4967
+ label: "MAC Address",
4968
+ getPlaceholder: (match, counter) => `[MAC_${counter}_${getSecretSuffix(match)}]`,
4969
+ // Exclude broadcast and zero addresses
4970
+ validate: (match) => {
4971
+ const normalized = match.replace(/[:-]/g, "").toUpperCase();
4972
+ if (normalized === "000000000000" || normalized === "FFFFFFFFFFFF")
4973
+ return false;
4974
+ return true;
4975
+ }
4976
+ },
4977
+ // Passport numbers (major countries — context-dependent to reduce false positives)
4978
+ {
4979
+ type: "PASSPORT",
4980
+ // Matches passport numbers when preceded by a context keyword like "passport",
4981
+ // "reisepass", "passeport", or passport-number-like labels.
4982
+ // Formats covered:
4983
+ // - US: 9 digits (C + 8 digits, or 9 digits)
4984
+ // - DE: 9 alphanumeric (C followed by 8 chars, or 9 uppercase alphanumeric)
4985
+ // - UK: 9 digits
4986
+ // - FR: 2 digits + 2 letters + 5 digits
4987
+ // - Generic: 6-9 alphanumeric (with context keyword requirement)
4988
+ // The context keyword requirement is critical to avoid matching random short strings.
4989
+ regex: /(?:passport|reisepass|passeport|pass(?:port)?[\s._-]?(?:no|nr|num(?:ber)?|#))[:\s#=]*([A-Z0-9]{6,9})\b/gi,
4990
+ label: "Passport Number",
4991
+ getPlaceholder: (match, counter) => `[PASSPORT_${counter}_${getSecretSuffix(match)}]`
4992
+ },
4993
+ // Tax ID / VAT numbers (EU VAT + context-dependent national formats)
4994
+ {
4995
+ type: "TAX_ID",
4996
+ // Two branches:
4997
+ // Branch 1 — EU VAT numbers: 2-letter country prefix + 8-12 alphanumeric digits.
4998
+ // e.g., DE123456789, GB123456789, FR12345678901, ATU12345678
4999
+ // Requires \b boundaries to avoid matching inside longer strings.
5000
+ //
5001
+ // Branch 2 — Context-dependent: matches when preceded by keywords like
5002
+ // "tax id", "tax number", "steuernummer", "steuer-nr", "tin number",
5003
+ // "vat id", "vat number", "tax identification", followed by a
5004
+ // colon/equals/space and a number. All keywords require a qualifier
5005
+ // suffix (e.g., "vat id" not bare "vat") to prevent false positives
5006
+ // on common words like "vat Konto" or "tin can".
5007
+ // This catches national tax IDs like US EIN (XX-XXXXXXX), German
5008
+ // Steuernummer (XXX/XXX/XXXXX), etc.
5009
+ regex: /\b(?:AT ?U\d{8}|BE ?0?\d{9,10}|BG ?\d{9,10}|HR ?\d{11}|CY ?\d{8}[A-Z]|CZ ?\d{8,10}|DK ?\d{8}|EE ?\d{9}|FI ?\d{8}|FR ?[0-9A-Z]{2}\d{9}|DE ?\d{9}|EL ?\d{9}|HU ?\d{8}|IE ?\d{7}[A-Z]{1,2}|IT ?\d{11}|LV ?\d{11}|LT ?\d{9,12}|LU ?\d{8}|MT ?\d{8}|NL ?\d{9}B\d{2}|PL ?\d{10}|PT ?\d{9}|RO ?\d{2,10}|SK ?\d{10}|SI ?\d{8}|ES ?[A-Z0-9]\d{7}[A-Z0-9]|SE ?\d{12}|GB ?\d{9}(?:\d{3})?)\b|(?:tax[\s_-]?(?:id|number|no|nr)|steuer(?:nummer|identifikationsnummer|nr|ident(?:nummer)?)|tin[\s_-]?(?:number|no|nr)|vat[\s_-]?(?:id|number|no|nr)|tax[\s_-]?identification(?:[\s_-]?number)?)[:\s#=]+([A-Z0-9\s/-]{5,20})/gi,
5010
+ label: "Tax ID",
5011
+ getPlaceholder: (match, counter) => `[TAX_ID_${counter}_${getSecretSuffix(match)}]`
5012
+ },
5013
+ // Vehicle license plate numbers (DE, AT, CH, UK, FR, NL, IT, ES, PL, US)
5014
+ {
5015
+ type: "VEHICLE_PLATE",
5016
+ // Context-dependent: requires a preceding keyword like "license plate",
5017
+ // "kennzeichen", "nummernschild", "immatriculation", "plate number", etc.
5018
+ // to avoid false positives on random letter-number combinations.
5019
+ //
5020
+ // Without context, plate formats (e.g., "B AB 1234") are too ambiguous
5021
+ // and would match many non-plate strings. The keyword requirement
5022
+ // eliminates nearly all false positives while still catching explicit mentions.
5023
+ regex: /(?:license[\s_-]?plate|plate[\s_-]?(?:number|no|nr)|kennzeichen|nummernschild|kfz[\s_-]?kennzeichen|immatriculation|registration[\s_-]?(?:number|no|nr|plate)|vrm|numberplate)[:\s#=]*([A-Z0-9]{1,4}[\s-]?[A-Z0-9]{1,4}[\s-]?[A-Z0-9]{1,6})\b/gi,
5024
+ label: "Vehicle Plate",
5025
+ getPlaceholder: (match, counter) => `[PLATE_${counter}_${getSecretSuffix(match)}]`
5026
+ },
5027
+ // Cryptocurrency wallet addresses (Bitcoin + Ethereum)
5028
+ {
5029
+ type: "CRYPTO_WALLET",
5030
+ // Three branches:
5031
+ // Branch 1 — Bitcoin Bech32 (SegWit): bc1 + 25-87 lowercase alphanumeric chars
5032
+ // e.g., bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
5033
+ //
5034
+ // Branch 2 — Bitcoin Legacy (P2PKH/P2SH): starts with 1 or 3, 25-34 base58 chars
5035
+ // e.g., 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
5036
+ // Base58 excludes 0, O, I, l to avoid ambiguity.
5037
+ //
5038
+ // Branch 3 — Ethereum: 0x + 40 hex characters
5039
+ // e.g., 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18
5040
+ regex: /\b(?:bc1[a-z0-9]{25,87}|[13][a-km-zA-HJ-NP-Z1-9]{25,34}|0x[0-9a-fA-F]{40})\b/g,
5041
+ label: "Crypto Wallet",
5042
+ getPlaceholder: (match, counter) => `[WALLET_${counter}_${getSecretSuffix(match)}]`
5043
+ }
5044
+ ];
5045
+ var MIN_PII_TEXT_LENGTH = 6;
5046
+ var PII_TYPE_TO_CATEGORY = {
5047
+ EMAIL: "email_addresses",
5048
+ ADDRESS: "addresses",
5049
+ PHONE: "phone_numbers",
5050
+ CREDIT_CARD: "credit_card_numbers",
5051
+ SSN: "social_security_numbers",
5052
+ IBAN: "iban_bank_account",
5053
+ TAX_ID: "tax_id_vat",
5054
+ PASSPORT: "passport_numbers",
5055
+ CRYPTO_WALLET: "crypto_wallets",
5056
+ VEHICLE_PLATE: "vehicle_plate",
5057
+ // No toggle in settings — always enabled if master is on
5058
+ // API keys & secrets → "api_keys" category
5059
+ AWS_ACCESS_KEY: "api_keys",
5060
+ AWS_SECRET_KEY: "api_keys",
5061
+ OPENAI_KEY: "api_keys",
5062
+ ANTHROPIC_KEY: "api_keys",
5063
+ GITHUB_PAT: "api_keys",
5064
+ STRIPE_KEY: "api_keys",
5065
+ GOOGLE_API_KEY: "api_keys",
5066
+ SLACK_TOKEN: "api_keys",
5067
+ TWILIO_KEY: "api_keys",
5068
+ SENDGRID_KEY: "api_keys",
5069
+ AZURE_KEY: "api_keys",
5070
+ HUGGINGFACE_KEY: "api_keys",
5071
+ DATABRICKS_TOKEN: "api_keys",
5072
+ FIREBASE_KEY: "api_keys",
5073
+ // Developer patterns
5074
+ GENERIC_SECRET: "generic_secrets",
5075
+ PRIVATE_KEY: "private_keys",
5076
+ JWT: "jwt_tokens",
5077
+ IPV4: "ip_addresses",
5078
+ IPV6: "ip_addresses",
5079
+ HOME_FOLDER: "home_folder",
5080
+ USER_AT_HOSTNAME: "user_at_hostname",
5081
+ MAC_ADDRESS: "mac_addresses"
5082
+ };
5083
+ function detectPII(text4, excludedIdsOrOptions = /* @__PURE__ */ new Set()) {
5084
+ const options = excludedIdsOrOptions instanceof Set ? { excludedIds: excludedIdsOrOptions } : excludedIdsOrOptions;
5085
+ const excludedIds = options.excludedIds ?? /* @__PURE__ */ new Set();
5086
+ const disabledCategories = options.disabledCategories ?? /* @__PURE__ */ new Set();
5087
+ const personalEntries = options.personalDataEntries ?? [];
5088
+ if (!text4 || text4.length < MIN_PII_TEXT_LENGTH && personalEntries.length === 0)
5089
+ return [];
5090
+ const matches2 = [];
5091
+ const coveredRanges = [];
5092
+ const identifierRanges = codingIdentifierRanges(text4);
5093
+ const typeCounts = {};
5094
+ const hasExclusions = excludedIds.size > 0;
5095
+ const hasDisabledCategories = disabledCategories.size > 0;
5096
+ const URL_EXCLUSION_REGEX = /https?:\/\/[^\s]+/g;
5097
+ const urlExclusionZones = [];
5098
+ URL_EXCLUSION_REGEX.lastIndex = 0;
5099
+ let urlMatch;
5100
+ while ((urlMatch = URL_EXCLUSION_REGEX.exec(text4)) !== null) {
5101
+ urlExclusionZones.push({
5102
+ start: urlMatch.index,
5103
+ end: urlMatch.index + urlMatch[0].length
5104
+ });
5105
+ }
5106
+ const hasUrlExclusions = urlExclusionZones.length > 0;
5107
+ if (text4.length >= MIN_PII_TEXT_LENGTH) {
5108
+ for (const pattern of PII_PATTERNS) {
5109
+ if (hasDisabledCategories) {
5110
+ const category = PII_TYPE_TO_CATEGORY[pattern.type];
5111
+ if (category && disabledCategories.has(category)) continue;
5112
+ }
5113
+ pattern.regex.lastIndex = 0;
5114
+ let regexMatch;
5115
+ while ((regexMatch = pattern.regex.exec(text4)) !== null) {
5116
+ const matchText = regexMatch[0];
5117
+ const startIndex = regexMatch.index;
5118
+ const endIndex = startIndex + matchText.length;
5119
+ if (pattern.type === "PHONE" && identifierRanges.some((range) => startIndex < range.end && endIndex > range.start)) continue;
5120
+ if (hasUrlExclusions) {
5121
+ const insideUrl = urlExclusionZones.some(
5122
+ (zone) => startIndex >= zone.start && endIndex <= zone.end
5123
+ );
5124
+ if (insideUrl) continue;
5125
+ }
5126
+ const overlaps = coveredRanges.some(
5127
+ (range) => startIndex >= range.start && startIndex < range.end || endIndex > range.start && endIndex <= range.end || startIndex <= range.start && endIndex >= range.end
5128
+ );
5129
+ if (overlaps) continue;
5130
+ if (pattern.validate && !pattern.validate(matchText)) {
5131
+ continue;
5132
+ }
5133
+ const matchId = generateMatchId(pattern.type, startIndex);
5134
+ if (hasExclusions && excludedIds.has(matchId)) continue;
5135
+ typeCounts[pattern.type] = (typeCounts[pattern.type] || 0) + 1;
5136
+ matches2.push({
5137
+ type: pattern.type,
5138
+ match: matchText,
5139
+ startIndex,
5140
+ endIndex,
5141
+ placeholder: pattern.getPlaceholder(matchText, typeCounts[pattern.type]),
5142
+ id: matchId
5143
+ });
5144
+ coveredRanges.push({ start: startIndex, end: endIndex });
5145
+ }
5146
+ }
5147
+ }
5148
+ for (const entry of personalEntries) {
5149
+ const textsToSearch = [];
5150
+ if (entry.textToHide && entry.textToHide.trim().length > 0) {
5151
+ textsToSearch.push(entry.textToHide);
5152
+ }
5153
+ if (entry.additionalTexts) {
5154
+ for (const additional of entry.additionalTexts) {
5155
+ if (additional && additional.trim().length > 0) {
5156
+ textsToSearch.push(additional);
5157
+ }
5158
+ }
5159
+ }
5160
+ for (const searchText of textsToSearch) {
5161
+ const lowerText = text4.toLowerCase();
5162
+ const lowerSearch = searchText.toLowerCase();
5163
+ let searchFrom = 0;
5164
+ while (searchFrom < lowerText.length) {
5165
+ const idx = lowerText.indexOf(lowerSearch, searchFrom);
5166
+ if (idx === -1) break;
5167
+ const startIndex = idx;
5168
+ const endIndex = idx + searchText.length;
5169
+ const overlaps = coveredRanges.some(
5170
+ (range) => startIndex >= range.start && startIndex < range.end || endIndex > range.start && endIndex <= range.end || startIndex <= range.start && endIndex >= range.end
5171
+ );
5172
+ if (!overlaps) {
5173
+ const matchText = text4.substring(startIndex, endIndex);
5174
+ const matchId = `pii-PERSONAL_DATA-${entry.id}-${startIndex}`;
5175
+ if (!hasExclusions || !excludedIds.has(matchId)) {
5176
+ const placeholder = entry.replaceWith.startsWith("[") ? entry.replaceWith : `[${entry.replaceWith}]`;
5177
+ matches2.push({
5178
+ type: entry.type ?? "EMAIL",
5179
+ match: matchText,
5180
+ startIndex,
5181
+ endIndex,
5182
+ placeholder,
5183
+ id: matchId
5184
+ });
5185
+ coveredRanges.push({ start: startIndex, end: endIndex });
5186
+ }
5187
+ }
5188
+ searchFrom = idx + searchText.length;
5189
+ }
5190
+ }
5191
+ }
5192
+ matches2.sort((a, b) => a.startIndex - b.startIndex);
5193
+ return matches2;
5194
+ }
5195
+
5196
+ // ../ui/src/services/projectFilePrivacy.ts
5197
+ function fail2(code) {
5198
+ throw Object.assign(new Error(code), { code });
5199
+ }
5200
+ var TOKEN = /\[[A-Za-z][A-Za-z0-9_]*\]/g;
5201
+ var GENERATED_TOKEN = /^\[OM_PII_[A-F0-9]{32}\]$/;
5202
+ function legacyToken(token) {
5203
+ const match = /^\[([A-Z_]+)_\d+_[A-Za-z0-9]{1,3}\]$/.exec(token);
5204
+ return Boolean(match && Object.prototype.hasOwnProperty.call(PII_TYPE_TO_CATEGORY, match[1]));
5205
+ }
5206
+ var ProjectFilePrivacy = class {
5207
+ values = /* @__PURE__ */ new Map();
5208
+ ambiguous = /* @__PURE__ */ new Set();
5209
+ conflicts = [];
5210
+ options;
5211
+ savePending = Promise.resolve();
5212
+ constructor(options) {
5213
+ this.options = options;
5214
+ this.addMappings(options.mappings ?? []);
5215
+ }
5216
+ addMappings(mappings) {
5217
+ for (const mapping of mappings) {
5218
+ if (!mapping || typeof mapping.original !== "string" || !mapping.original || typeof mapping.placeholder !== "string" || !/^\[[A-Za-z][A-Za-z0-9_]*\]$/.test(mapping.placeholder)) continue;
5219
+ const previous = this.values.get(mapping.placeholder);
5220
+ if (previous && previous.original !== mapping.original) {
5221
+ this.ambiguous.add(mapping.placeholder);
5222
+ if (!this.conflicts.some((value) => value.placeholder === mapping.placeholder && value.original === mapping.original)) this.conflicts.push({ ...mapping });
5223
+ } else this.values.set(mapping.placeholder, { ...mapping });
5224
+ }
5225
+ if (this.values.size + this.conflicts.length > 1e4) fail2("pii_mapping_limit");
5226
+ }
5227
+ restoreText(text4) {
5228
+ return text4.replace(TOKEN, (token) => {
5229
+ if (this.ambiguous.has(token)) fail2("pii_mapping_ambiguous");
5230
+ const mapping = this.values.get(token);
5231
+ const customToken = this.options.detection?.personalDataEntries?.some((entry) => (entry.replaceWith.startsWith("[") ? entry.replaceWith : `[${entry.replaceWith}]`) === token);
5232
+ if (!mapping && (GENERATED_TOKEN.test(token) || legacyToken(token) || customToken)) fail2("pii_mapping_unavailable");
5233
+ return mapping?.original ?? token;
5234
+ });
5235
+ }
5236
+ restoreArguments(arguments_) {
5237
+ const result = { ...arguments_ };
5238
+ for (const key of ["path", "query", "glob"]) {
5239
+ if (typeof result[key] === "string") result[key] = this.restoreText(result[key]);
5240
+ }
5241
+ return result;
5242
+ }
5243
+ restoreMutation(mutation) {
5244
+ const result = { ...mutation, path: this.restoreText(mutation.path) };
5245
+ if (mutation.content !== void 0) result.content = this.restoreText(mutation.content);
5246
+ if (mutation.patch !== void 0) {
5247
+ result.patch = mutation.patch.split("\n").map((line) => {
5248
+ const restored = this.restoreText(line);
5249
+ if (restored.includes("\n")) fail2("pii_multiline_patch_token");
5250
+ return restored;
5251
+ }).join("\n");
5252
+ }
5253
+ return validateProjectFileMutation(result);
5254
+ }
5255
+ token(original, type) {
5256
+ for (const [token, mapping] of Array.from(this.values)) {
5257
+ if (!this.ambiguous.has(token) && mapping.original === original) return token;
5258
+ }
5259
+ let placeholder;
5260
+ do {
5261
+ placeholder = `[OM_PII_${crypto.randomUUID().replace(/-/g, "").toUpperCase()}]`;
5262
+ } while (this.values.has(placeholder));
5263
+ this.addMappings([{ placeholder, original, type }]);
5264
+ return placeholder;
5265
+ }
5266
+ redactText(text4) {
5267
+ const ranges = [];
5268
+ const tokens = Array.from(text4.matchAll(TOKEN)).map((match) => ({ start: match.index, end: match.index + match[0].length }));
5269
+ for (const range of tokens) {
5270
+ const literal = text4.slice(range.start, range.end);
5271
+ if (this.values.has(literal) || GENERATED_TOKEN.test(literal) || legacyToken(literal)) ranges.push(range);
5272
+ }
5273
+ const overlaps = (start, end) => [...tokens, ...ranges].some((r) => start < r.end && end > r.start);
5274
+ for (const mapping of Array.from(this.values.values()).sort((a, b) => b.original.length - a.original.length)) {
5275
+ let start = text4.indexOf(mapping.original);
5276
+ while (start !== -1) {
5277
+ const end = start + mapping.original.length;
5278
+ if (!overlaps(start, end)) ranges.push({ start, end, type: mapping.type });
5279
+ start = text4.indexOf(mapping.original, end);
5280
+ }
5281
+ }
5282
+ if (this.options.enabled !== false) {
5283
+ for (const match of detectPII(text4, this.options.detection)) {
5284
+ if (!overlaps(match.startIndex, match.endIndex)) ranges.push({ start: match.startIndex, end: match.endIndex, type: match.type });
5285
+ }
5286
+ }
5287
+ let cursor = 0;
5288
+ let result = "";
5289
+ for (const range of ranges.sort((a, b) => a.start - b.start)) {
5290
+ result += text4.slice(cursor, range.start);
5291
+ result += text4.slice(range.start, range.end).split(/(\r?\n)/).map((part) => !part || /^(?:\r?\n)$/.test(part) ? part : this.token(part, range.type)).join("");
5292
+ cursor = range.end;
5293
+ }
5294
+ return result + text4.slice(cursor);
5295
+ }
5296
+ async redactResult(value) {
5297
+ const visit = (item) => {
5298
+ if (typeof item === "string") return this.redactText(item);
5299
+ if (Array.isArray(item)) return item.map(visit);
5300
+ if (item && typeof item === "object") return Object.fromEntries(Object.entries(item).map(([key, child]) => {
5301
+ if (["expected_base", "content_hash", "proposal_commitment"].includes(key)) return [key, child];
5302
+ return [key, visit(child)];
5303
+ }));
5304
+ return item;
5305
+ };
5306
+ const result = visit(value);
5307
+ const save = this.savePending.catch(() => {
5308
+ }).then(() => this.options.save([...Array.from(this.values.values()), ...this.conflicts]));
5309
+ this.savePending = save;
5310
+ await save;
5311
+ return result;
5312
+ }
5313
+ };
5314
+
5315
+ // ../ui/src/services/projectFilePrivacyStorage.ts
5316
+ async function loadProjectFilePrivacy(options) {
5317
+ const ciphertext = await options.read();
5318
+ let mappings = [];
5319
+ if (ciphertext) {
5320
+ const plaintext = await options.decrypt(ciphertext, options.key);
5321
+ try {
5322
+ const saved = JSON.parse(plaintext ?? "null");
5323
+ if (saved?.chat_id !== options.chatId || saved?.project_id !== options.projectId || !Array.isArray(saved?.mappings)) throw new Error();
5324
+ mappings = saved.mappings;
5325
+ } catch {
5326
+ throw Object.assign(new Error(), { code: "pii_mapping_unavailable" });
5327
+ }
5328
+ }
5329
+ return new ProjectFilePrivacy({
5330
+ mappings: [...mappings, ...options.mappings],
5331
+ detection: options.detection,
5332
+ enabled: options.enabled,
5333
+ save: async (current) => {
5334
+ const plaintext = JSON.stringify({ chat_id: options.chatId, project_id: options.projectId, mappings: current });
5335
+ if (new TextEncoder().encode(plaintext).byteLength > 2 * 1024 * 1024) throw Object.assign(new Error(), { code: "pii_mapping_limit" });
5336
+ await options.write(await options.encrypt(plaintext, options.key));
5337
+ }
5338
+ });
5339
+ }
5340
+
4593
5341
  // src/projectFileExecutor.ts
4594
5342
  function projectPrivatePaths(settingsText) {
4595
5343
  if (!settingsText) return [];
@@ -4650,6 +5398,7 @@ async function prepareCliProjectFocusForPreflight(options) {
4650
5398
  }
4651
5399
  function registerCliProjectFileExecutor(options) {
4652
5400
  let closed = false;
5401
+ const privacyContexts = /* @__PURE__ */ new Map();
4653
5402
  const executor = createProjectFileJobExecutor({
4654
5403
  isActiveChat: (chatId) => !closed && chatId === options.chatId,
4655
5404
  send: (event, payload) => options.ws.sendAsync(event, payload),
@@ -4664,6 +5413,49 @@ function registerCliProjectFileExecutor(options) {
4664
5413
  const focus = await options.client.getActiveProjectFocus(job.chat_id);
4665
5414
  if (focus?.project_id !== job.project_id) throw Object.assign(new Error(), { code: "project_focus_required" });
4666
5415
  const context = { teamId: focus.team_id, personal: !focus.team_id };
5416
+ if (!privacyContexts.has(job.project_id)) {
5417
+ const loaded = (async () => {
5418
+ const directory = join6(resolveStateDir(), "project_file_privacy");
5419
+ const filename = createHash10("sha256").update(JSON.stringify([options.chatId, job.project_id])).digest("hex") + ".ciphertext";
5420
+ const path2 = join6(directory, filename);
5421
+ const history = await options.client.getChatMessages(options.chatId, context);
5422
+ const memories = options.memories ?? [];
5423
+ const settings2 = memories.find((entry) => entry.app_id === "privacy" && entry.item_type === "pii_detection_settings")?.data;
5424
+ return loadProjectFilePrivacy({
5425
+ chatId: job.chat_id,
5426
+ projectId: job.project_id,
5427
+ key: options.chatKey,
5428
+ mappings: [...history.messages.flatMap((message) => message.piiMappings ?? []), ...options.piiMappings ?? []],
5429
+ enabled: settings2?.masterEnabled,
5430
+ detection: {
5431
+ disabledCategories: new Set(Object.entries(settings2?.categories ?? {}).filter(([, enabled]) => !enabled).map(([key]) => key)),
5432
+ personalDataEntries: memories.filter((entry) => entry.app_id === "privacy" && entry.item_type === "personal_data_entry").flatMap((entry) => {
5433
+ const data = entry.data;
5434
+ return data.enabled && data.textToHide && data.replaceWith ? [{ id: entry.id, textToHide: data.textToHide, replaceWith: data.replaceWith, additionalTexts: Object.values(data.addressLines ?? {}) }] : [];
5435
+ })
5436
+ },
5437
+ read: async () => {
5438
+ try {
5439
+ return await readFile(path2, "utf8");
5440
+ } catch (error) {
5441
+ if (error.code === "ENOENT") return null;
5442
+ throw error;
5443
+ }
5444
+ },
5445
+ write: async (ciphertext) => {
5446
+ await mkdir2(directory, { recursive: true, mode: 448 });
5447
+ const temporary = path2 + "." + randomUUID6();
5448
+ await writeFile(temporary, ciphertext, { mode: 384, flag: "wx" });
5449
+ await rename2(temporary, path2);
5450
+ },
5451
+ encrypt: encryptWithAesGcmCombined,
5452
+ decrypt: decryptWithAesGcmCombined
5453
+ });
5454
+ })();
5455
+ privacyContexts.set(job.project_id, loaded);
5456
+ void loaded.catch(() => privacyContexts.delete(job.project_id));
5457
+ }
5458
+ const privacy = await privacyContexts.get(job.project_id);
4667
5459
  const [detail, settings, sources] = await Promise.all([
4668
5460
  options.client.getProject(job.project_id, context),
4669
5461
  options.client.getProjectSettings(job.project_id, context),
@@ -4679,6 +5471,7 @@ function registerCliProjectFileExecutor(options) {
4679
5471
  if (job.source_id && !source || !job.source_id && sources.length > 1) throw Object.assign(new Error(), { code: "source_selection_required" });
4680
5472
  return {
4681
5473
  projectKey,
5474
+ privacy,
4682
5475
  sourceId: source?.source_id ?? null,
4683
5476
  writeMode: settings.selection_required ? null : settings.write_mode,
4684
5477
  execute: async (currentJob, mutation, approvedIgnoredRead) => {
@@ -4757,12 +5550,12 @@ function registerCliProjectFileExecutor(options) {
4757
5550
  // src/remoteCommandClient.ts
4758
5551
  import { webcrypto as webcrypto3 } from "crypto";
4759
5552
  import { chmodSync as chmodSync3, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync6, readFileSync as readFileSync8, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "fs";
4760
- import { join as join7 } from "path";
5553
+ import { join as join8 } from "path";
4761
5554
 
4762
5555
  // src/remoteCommandPermissions.ts
4763
- import { createHash as createHash10 } from "crypto";
5556
+ import { createHash as createHash11 } from "crypto";
4764
5557
  import { closeSync as closeSync2, constants, fstatSync, openSync as openSync2, readFileSync as readFileSync7, realpathSync } from "fs";
4765
- import { isAbsolute as isAbsolute3, join as join6, posix, relative } from "path";
5558
+ import { isAbsolute as isAbsolute3, join as join7, posix, relative } from "path";
4766
5559
  import { parseDocument } from "yaml";
4767
5560
  var REMOTE_COMMAND_PERMISSIONS_PATH = ".openmates/permissions.yml";
4768
5561
  var RemoteCommandPermissionsError = class extends Error {
@@ -4780,7 +5573,7 @@ var IDENTIFIER = /^[a-z][a-z0-9-]{0,63}$/;
4780
5573
  var ENVIRONMENT_NAME = /^[A-Z_][A-Z0-9_]{0,127}$/;
4781
5574
  function loadRemoteCommandPermissions(projectRoot) {
4782
5575
  const root = realpathSync(projectRoot);
4783
- const path2 = join6(root, REMOTE_COMMAND_PERMISSIONS_PATH);
5576
+ const path2 = join7(root, REMOTE_COMMAND_PERMISSIONS_PATH);
4784
5577
  let descriptor;
4785
5578
  try {
4786
5579
  descriptor = openSync2(path2, constants.O_RDONLY | constants.O_NOFOLLOW);
@@ -5050,7 +5843,7 @@ function stableJson(value) {
5050
5843
  return JSON.stringify(value);
5051
5844
  }
5052
5845
  function sha2562(value) {
5053
- return createHash10("sha256").update(value).digest("hex");
5846
+ return createHash11("sha256").update(value).digest("hex");
5054
5847
  }
5055
5848
  function isInside(root, candidate) {
5056
5849
  const path2 = relative(root, candidate);
@@ -5100,7 +5893,7 @@ var RemoteCommandOutputAccumulator = class {
5100
5893
  }
5101
5894
  };
5102
5895
  function listRemoteCommandPresetGrants() {
5103
- const path2 = join7(resolveStateDir(), PRESET_GRANTS_FILE);
5896
+ const path2 = join8(resolveStateDir(), PRESET_GRANTS_FILE);
5104
5897
  if (!existsSync5(path2)) return [];
5105
5898
  const stat3 = lstatSync(path2);
5106
5899
  if (!stat3.isFile() || stat3.isSymbolicLink() || (stat3.mode & 63) !== 0) throw new Error("Remote command preset grant store is not private");
@@ -5111,8 +5904,8 @@ function listRemoteCommandPresetGrants() {
5111
5904
  return {
5112
5905
  project_id: identifier2(item.project_id, "project_id"),
5113
5906
  preset_id: identifier2(item.preset_id, "preset_id"),
5114
- definition_digest: typeof item.definition_digest === "string" && /^[a-f0-9]{64}$/i.test(item.definition_digest) ? item.definition_digest : fail2("invalid preset definition digest"),
5115
- enabled: item.enabled === true ? true : fail2("invalid preset grant state")
5907
+ definition_digest: typeof item.definition_digest === "string" && /^[a-f0-9]{64}$/i.test(item.definition_digest) ? item.definition_digest : fail3("invalid preset definition digest"),
5908
+ enabled: item.enabled === true ? true : fail3("invalid preset grant state")
5116
5909
  };
5117
5910
  });
5118
5911
  }
@@ -5318,7 +6111,7 @@ function validateRemoteCommandReview(value) {
5318
6111
  state: "REVIEW_REQUIRED",
5319
6112
  created_at: integer2(item.created_at, "created_at"),
5320
6113
  review_expires_at: integer2(item.review_expires_at, "review_expires_at"),
5321
- review_token: typeof item.review_token === "string" && item.review_token.length >= 32 ? item.review_token : fail2("invalid review token"),
6114
+ review_token: typeof item.review_token === "string" && item.review_token.length >= 32 ? item.review_token : fail3("invalid review token"),
5322
6115
  approval_requirement: oneOf2(item.approval_requirement, ["one_run", "one_run_or_preset"], "approval requirement"),
5323
6116
  command,
5324
6117
  explanation: {
@@ -5368,7 +6161,7 @@ function writePresetGrants(grants) {
5368
6161
  const directory = resolveStateDir();
5369
6162
  mkdirSync6(directory, { recursive: true, mode: 448 });
5370
6163
  chmodSync3(directory, 448);
5371
- const path2 = join7(directory, PRESET_GRANTS_FILE);
6164
+ const path2 = join8(directory, PRESET_GRANTS_FILE);
5372
6165
  if (existsSync5(path2) && lstatSync(path2).isSymbolicLink()) throw new Error("Remote command preset grant store cannot be a symbolic link");
5373
6166
  const temporary = `${path2}.${process.pid}.tmp`;
5374
6167
  writeFileSync4(temporary, `${JSON.stringify({ version: 1, grants }, null, 2)}
@@ -5419,7 +6212,7 @@ function oneOf2(value, choices, name) {
5419
6212
  function assertProjectKey(value) {
5420
6213
  if (!(value instanceof Uint8Array) || value.byteLength !== 32) throw new Error("Project key must be 32 bytes");
5421
6214
  }
5422
- function fail2(message) {
6215
+ function fail3(message) {
5423
6216
  throw new Error(message);
5424
6217
  }
5425
6218
 
@@ -7023,7 +7816,7 @@ function escapeRegExp(s) {
7023
7816
  import { readFileSync as readFileSync9, statSync, existsSync as existsSync6 } from "fs";
7024
7817
  import { basename as basename2, extname as extname2, resolve as resolve4 } from "path";
7025
7818
  import { homedir as homedir4 } from "os";
7026
- import { createHash as createHash11 } from "crypto";
7819
+ import { createHash as createHash12 } from "crypto";
7027
7820
  import JSZip2 from "jszip";
7028
7821
  var MAX_PER_FILE_SIZE = 100 * 1024 * 1024;
7029
7822
  var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -7560,7 +8353,7 @@ function processCodeFile(filePath, filename, redactor) {
7560
8353
  line_count: lineCount
7561
8354
  });
7562
8355
  const textPreview = `${filename} (${language}, ${lineCount} lines)`;
7563
- const contentHash = createHash11("sha256").update(content).digest("hex");
8356
+ const contentHash = createHash12("sha256").update(content).digest("hex");
7564
8357
  const embed = {
7565
8358
  embedId,
7566
8359
  embedRef,
@@ -7627,7 +8420,7 @@ async function processDocxFile(filePath, filename, redactor) {
7627
8420
  textPreview: filename,
7628
8421
  status: "finished",
7629
8422
  filePath: filename,
7630
- contentHash: createHash11("sha256").update(content).digest("hex"),
8423
+ contentHash: createHash12("sha256").update(content).digest("hex"),
7631
8424
  textLengthChars: content.length
7632
8425
  },
7633
8426
  referenceBlock: createEmbedReferenceBlock(embedRef),
@@ -7670,7 +8463,7 @@ function createSheetFileEmbed(tableMarkdown, filename, redactor) {
7670
8463
  textPreview: filename,
7671
8464
  status: "finished",
7672
8465
  filePath: filename,
7673
- contentHash: createHash11("sha256").update(content).digest("hex"),
8466
+ contentHash: createHash12("sha256").update(content).digest("hex"),
7674
8467
  textLengthChars: content.length
7675
8468
  },
7676
8469
  referenceBlock: createEmbedReferenceBlock(embedRef),
@@ -7915,7 +8708,7 @@ function buildEmbedShareUrl(origin, embedId, blob) {
7915
8708
  }
7916
8709
 
7917
8710
  // src/connectedAccountImport.ts
7918
- import { createHash as createHash12, randomUUID as randomUUID7, webcrypto as webcrypto5 } from "crypto";
8711
+ import { createHash as createHash13, randomUUID as randomUUID7, webcrypto as webcrypto5 } from "crypto";
7919
8712
  var TRANSFER_PREFIX = "OMCA1.";
7920
8713
  var SUPPORTED_KDF_ITERATIONS = 1e5;
7921
8714
  async function decryptConnectedAccountCliTransferPayload(encryptedPayload, passcode) {
@@ -8060,7 +8853,7 @@ function defaultProviderLabel(providerId) {
8060
8853
  return providerId === "google_calendar" ? "Google Calendar" : "Connected account";
8061
8854
  }
8062
8855
  function sha256Hex2(value) {
8063
- return createHash12("sha256").update(value).digest("hex");
8856
+ return createHash13("sha256").update(value).digest("hex");
8064
8857
  }
8065
8858
  function base64UrlToBytes2(value) {
8066
8859
  return base64ToBytes(value.replace(/-/g, "+").replace(/_/g, "/"));
@@ -9027,7 +9820,7 @@ function stripTimer(job) {
9027
9820
  }
9028
9821
 
9029
9822
  // src/plansCli.ts
9030
- import { createHash as createHash13, randomBytes as randomBytes3, randomUUID as randomUUID9 } from "crypto";
9823
+ import { createHash as createHash14, randomBytes as randomBytes3, randomUUID as randomUUID9 } from "crypto";
9031
9824
  var PLAN_STATUSES = ["draft", "checking_assumptions", "awaiting_confirmation", "active", "executing", "running_checks", "blocked", "completed", "archived"];
9032
9825
  var LEARNING_TYPES = ["workflow_improvement", "agent_instruction_improvement"];
9033
9826
  var LEARNING_TARGET_KINDS = ["workflow", "project_agent_instructions"];
@@ -9551,7 +10344,7 @@ async function planKeyFromRecord(record7, masterKey) {
9551
10344
  return planKey;
9552
10345
  }
9553
10346
  function sha256Hex3(value) {
9554
- return createHash13("sha256").update(value).digest("hex");
10347
+ return createHash14("sha256").update(value).digest("hex");
9555
10348
  }
9556
10349
  async function decryptOptional2(value, key) {
9557
10350
  if (!value) return "";
@@ -9574,7 +10367,7 @@ function parseUserFlows(value) {
9574
10367
  }
9575
10368
  function deriveShortId2(record7) {
9576
10369
  const source = record7.plan_id || `${record7.created_at ?? ""}-${record7.updated_at ?? ""}`;
9577
- const digest3 = createHash13("sha256").update(source).digest("hex").slice(0, 6).toUpperCase();
10370
+ const digest3 = createHash14("sha256").update(source).digest("hex").slice(0, 6).toUpperCase();
9578
10371
  return `${DEFAULT_PLAN_PREFIX}-${digest3}`;
9579
10372
  }
9580
10373
  function nowSeconds2() {
@@ -11483,7 +12276,7 @@ var OpenMatesClient = class _OpenMatesClient {
11483
12276
  }
11484
12277
  payload.team_project_key_wrapper = {
11485
12278
  key_type: "team",
11486
- hashed_team_id: createHash14("sha256").update(teamId).digest("hex"),
12279
+ hashed_team_id: createHash15("sha256").update(teamId).digest("hex"),
11487
12280
  team_key_epoch: 1,
11488
12281
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, teamKey),
11489
12282
  wrapper_version: 1,
@@ -11793,7 +12586,7 @@ var OpenMatesClient = class _OpenMatesClient {
11793
12586
  if (!userId) {
11794
12587
  throw new Error("Could not resolve current user id for local connected-account connector.");
11795
12588
  }
11796
- const { createHash: createHash30 } = await import("crypto");
12589
+ const { createHash: createHash31 } = await import("crypto");
11797
12590
  const accountId = randomUUID10();
11798
12591
  const masterKey = this.getMasterKeyBytes();
11799
12592
  const encryptLocalConnectorValue = async (value) => {
@@ -11802,9 +12595,9 @@ var OpenMatesClient = class _OpenMatesClient {
11802
12595
  };
11803
12596
  const payload = {
11804
12597
  id: accountId,
11805
- hashed_user_id: createHash30("sha256").update(userId).digest("hex"),
12598
+ hashed_user_id: createHash31("sha256").update(userId).digest("hex"),
11806
12599
  encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
11807
- provider_type_hash: createHash30("sha256").update(input.provider_id).digest("hex"),
12600
+ provider_type_hash: createHash31("sha256").update(input.provider_id).digest("hex"),
11808
12601
  encrypted_account_label: await encryptLocalConnectorValue(input.label),
11809
12602
  encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
11810
12603
  encrypted_app_permissions: await encryptLocalConnectorValue({
@@ -12553,8 +13346,8 @@ var OpenMatesClient = class _OpenMatesClient {
12553
13346
  return decryptBytesWithAesGcm(encryptedChatKey, masterKey);
12554
13347
  }
12555
13348
  getContextWrapperEncryptedChatKey(cache, chatId, teamId = null) {
12556
- const hashedChatId = createHash14("sha256").update(chatId).digest("hex");
12557
- const hashedTeamId = teamId ? createHash14("sha256").update(teamId).digest("hex") : null;
13349
+ const hashedChatId = createHash15("sha256").update(chatId).digest("hex");
13350
+ const hashedTeamId = teamId ? createHash15("sha256").update(teamId).digest("hex") : null;
12558
13351
  const wrapper = (cache?.chatKeyWrappers ?? []).find(
12559
13352
  (entry) => entry.key_type === (teamId ? "team" : "master") && entry.hashed_chat_id === hashedChatId && (!hashedTeamId || entry.hashed_team_id === hashedTeamId) && typeof entry.encrypted_chat_key === "string"
12560
13353
  );
@@ -12827,7 +13620,7 @@ var OpenMatesClient = class _OpenMatesClient {
12827
13620
  processing_window_id: processingWindowId,
12828
13621
  ideas: params.ideas.map((idea, index) => ({ index: index + 1, ...idea.payload }))
12829
13622
  });
12830
- const payloadHash = createHash14("sha256").update(serverProcessablePayload).digest("hex");
13623
+ const payloadHash = createHash15("sha256").update(serverProcessablePayload).digest("hex");
12831
13624
  const masterKey = this.getMasterKeyBytes();
12832
13625
  const chatKey = new Uint8Array(randomBytes5(32));
12833
13626
  const encryptedChatKey = await encryptBytesWithAesGcm(chatKey, masterKey);
@@ -13178,7 +13971,7 @@ var OpenMatesClient = class _OpenMatesClient {
13178
13971
  );
13179
13972
  let msgEmbedIds = [];
13180
13973
  if (clientMsgId && embeds.length > 0) {
13181
- const hashed = createHash14("sha256").update(clientMsgId).digest("hex");
13974
+ const hashed = createHash15("sha256").update(clientMsgId).digest("hex");
13182
13975
  msgEmbedIds = embeds.filter(
13183
13976
  (e) => e.hashed_message_id === hashed && !e.parent_embed_id
13184
13977
  ).map((e) => String(e.embed_id ?? e.id ?? "")).filter(Boolean);
@@ -13193,7 +13986,10 @@ var OpenMatesClient = class _OpenMatesClient {
13193
13986
  category: msgCategory,
13194
13987
  modelName,
13195
13988
  createdAt: typeof m.created_at === "number" ? m.created_at : 0,
13196
- embedIds: msgEmbedIds
13989
+ embedIds: msgEmbedIds,
13990
+ ...typeof m.encrypted_pii_mappings === "string" && chatKeyBytes ? {
13991
+ piiMappings: JSON.parse(await decryptWithAesGcmCombined(m.encrypted_pii_mappings, chatKeyBytes) ?? "[]")
13992
+ } : {}
13197
13993
  });
13198
13994
  }
13199
13995
  messages.sort((a, b) => a.createdAt - b.createdAt);
@@ -13330,7 +14126,7 @@ var OpenMatesClient = class _OpenMatesClient {
13330
14126
  source_version: 1,
13331
14127
  sequence: segment.sequence,
13332
14128
  kind: segment.kind,
13333
- source_hash: createHash14("sha256").update(segment.speakableText).digest("hex"),
14129
+ source_hash: createHash15("sha256").update(segment.speakableText).digest("hex"),
13334
14130
  speakable_text: segment.speakableText
13335
14131
  }));
13336
14132
  const messageId = message.clientMessageId || message.id;
@@ -13595,8 +14391,8 @@ var OpenMatesClient = class _OpenMatesClient {
13595
14391
  );
13596
14392
  }
13597
14393
  const embedId = String(embed.embed_id ?? embed.id ?? "");
13598
- const { createHash: createHash30 } = await import("crypto");
13599
- const hashedEmbedId = createHash30("sha256").update(embedId).digest("hex");
14394
+ const { createHash: createHash31 } = await import("crypto");
14395
+ const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
13600
14396
  const embedKeyBytes = await this.resolveEmbedKey(
13601
14397
  cache,
13602
14398
  masterKey,
@@ -13698,7 +14494,7 @@ var OpenMatesClient = class _OpenMatesClient {
13698
14494
  async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
13699
14495
  if (visited.has(embedId)) return null;
13700
14496
  visited.add(embedId);
13701
- const { createHash: createHash30 } = await import("crypto");
14497
+ const { createHash: createHash31 } = await import("crypto");
13702
14498
  const masterKeyEntry = cache.embedKeys.find(
13703
14499
  (ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
13704
14500
  );
@@ -13715,7 +14511,7 @@ var OpenMatesClient = class _OpenMatesClient {
13715
14511
  if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
13716
14512
  const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
13717
14513
  const owningChat = cache.chats.find((c) => {
13718
- const chatHash = createHash30("sha256").update(String(c.details.id ?? "")).digest("hex");
14514
+ const chatHash = createHash31("sha256").update(String(c.details.id ?? "")).digest("hex");
13719
14515
  return chatHash === hashedChatId;
13720
14516
  });
13721
14517
  if (owningChat) {
@@ -13744,7 +14540,7 @@ var OpenMatesClient = class _OpenMatesClient {
13744
14540
  const parentFullId = String(
13745
14541
  parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
13746
14542
  );
13747
- const parentHashedId = createHash30("sha256").update(parentFullId).digest("hex");
14543
+ const parentHashedId = createHash31("sha256").update(parentFullId).digest("hex");
13748
14544
  const parentKey = await this.resolveEmbedKey(
13749
14545
  cache,
13750
14546
  masterKey,
@@ -14046,6 +14842,8 @@ var OpenMatesClient = class _OpenMatesClient {
14046
14842
  ws,
14047
14843
  chatId,
14048
14844
  chatKey: chatKeyBytes,
14845
+ piiMappings,
14846
+ memories: availableMemories,
14049
14847
  requestApproval: params.onProjectWriteApproval,
14050
14848
  requestReadApproval: params.onProjectReadApproval
14051
14849
  });
@@ -16406,7 +17204,7 @@ var OpenMatesClient = class _OpenMatesClient {
16406
17204
  let key = wrapper ? await decryptBytesWithAesGcm(String(wrapper.encrypted_embed_key), projectKey) : null;
16407
17205
  if (!key) {
16408
17206
  const cache = await this.ensureSynced();
16409
- key = await this.resolveEmbedKey(cache, this.getMasterKeyBytes(), embed, embedId, createHash14("sha256").update(embedId).digest("hex"));
17207
+ key = await this.resolveEmbedKey(cache, this.getMasterKeyBytes(), embed, embedId, createHash15("sha256").update(embedId).digest("hex"));
16410
17208
  }
16411
17209
  if (!key || typeof embed.encrypted_content !== "string") throw Object.assign(new Error("Project file key unavailable"), { code: "file_key_unavailable" });
16412
17210
  const content = await decryptWithAesGcmCombined(embed.encrypted_content, key);
@@ -18352,7 +19150,7 @@ Required: ${schema.required.join(", ")}`
18352
19150
  const session = this.requireSession();
18353
19151
  const masterKey = base64ToBytes(session.masterKeyExportedB64);
18354
19152
  const cache = await this.ensureSynced();
18355
- const { createHash: createHash30 } = await import("crypto");
19153
+ const { createHash: createHash31 } = await import("crypto");
18356
19154
  const embed = cache.embeds.find(
18357
19155
  (e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
18358
19156
  );
@@ -18360,7 +19158,7 @@ Required: ${schema.required.join(", ")}`
18360
19158
  throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
18361
19159
  }
18362
19160
  const embedId = String(embed.embed_id ?? embed.id ?? "");
18363
- const hashedEmbedId = createHash30("sha256").update(embedId).digest("hex");
19161
+ const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
18364
19162
  const embedKeyBytes = await this.resolveEmbedKey(
18365
19163
  cache,
18366
19164
  masterKey,
@@ -18468,8 +19266,8 @@ Required: ${schema.required.join(", ")}`
18468
19266
  if (!embed) {
18469
19267
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
18470
19268
  }
18471
- const { createHash: createHash30 } = await import("crypto");
18472
- const hashedEmbedId = createHash30("sha256").update(embedId).digest("hex");
19269
+ const { createHash: createHash31 } = await import("crypto");
19270
+ const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
18473
19271
  const embedKey = await this.resolveEmbedKey(
18474
19272
  cache,
18475
19273
  masterKey,
@@ -18585,8 +19383,8 @@ Required: ${schema.required.join(", ")}`
18585
19383
  if (!embed) {
18586
19384
  throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
18587
19385
  }
18588
- const { createHash: createHash30 } = await import("crypto");
18589
- const hashedEmbedId = createHash30("sha256").update(embedId).digest("hex");
19386
+ const { createHash: createHash31 } = await import("crypto");
19387
+ const hashedEmbedId = createHash31("sha256").update(embedId).digest("hex");
18590
19388
  const embedKey = await this.resolveEmbedKey(
18591
19389
  cache,
18592
19390
  masterKey,
@@ -19118,9 +19916,9 @@ Required: ${schema.required.join(", ")}`
19118
19916
  chats.splice(0, chats.length, ...reconciledChats);
19119
19917
  if (totalChatCount === 0) totalChatCount = chats.length;
19120
19918
  if (reconciliation.deleted_chat_ids?.length) {
19121
- const { createHash: createHash30 } = await import("crypto");
19919
+ const { createHash: createHash31 } = await import("crypto");
19122
19920
  const deletedHashes = new Set(
19123
- reconciliation.deleted_chat_ids.map((id) => createHash30("sha256").update(id).digest("hex"))
19921
+ reconciliation.deleted_chat_ids.map((id) => createHash31("sha256").update(id).digest("hex"))
19124
19922
  );
19125
19923
  const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
19126
19924
  const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
@@ -25792,13 +26590,13 @@ var GeneratedAppSkills = class {
25792
26590
 
25793
26591
  // src/sdk.ts
25794
26592
  import { decode as toonDecode2 } from "@toon-format/toon";
25795
- import { createHash as createHash15, createHmac as createHmac4, randomBytes as randomBytes7, randomUUID as randomUUID11 } from "crypto";
26593
+ import { createHash as createHash16, createHmac as createHmac4, randomBytes as randomBytes7, randomUUID as randomUUID11 } from "crypto";
25796
26594
  import { chmodSync as chmodSync4, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
25797
26595
  import { homedir as homedir5 } from "os";
25798
- import { dirname as dirname3, join as join8 } from "path";
26596
+ import { dirname as dirname3, join as join9 } from "path";
25799
26597
 
25800
26598
  // src/designIcons.ts
25801
- import { mkdir as mkdir2, writeFile } from "fs/promises";
26599
+ import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
25802
26600
  import { dirname as dirname2, extname as extname3 } from "path";
25803
26601
  import { Resvg } from "@resvg/resvg-js";
25804
26602
  var ICON_SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
@@ -25891,9 +26689,9 @@ function normalizePngSize(value, label3) {
25891
26689
  async function writeExportFile(path2, data) {
25892
26690
  const directory = dirname2(path2);
25893
26691
  if (directory && directory !== ".") {
25894
- await mkdir2(directory, { recursive: true });
26692
+ await mkdir3(directory, { recursive: true });
25895
26693
  }
25896
- await writeFile(path2, data);
26694
+ await writeFile2(path2, data);
25897
26695
  }
25898
26696
 
25899
26697
  // src/accountExportArchive.ts
@@ -25954,13 +26752,13 @@ var ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS = [
25954
26752
  /#key=[A-Za-z0-9_-]{8,}/
25955
26753
  ];
25956
26754
  async function writeAccountExportArchive(bundle, flags = {}) {
25957
- const { mkdir: mkdir3, writeFile: writeFile2 } = await import("fs/promises");
25958
- const { join: join29, dirname: dirname17 } = await import("path");
26755
+ const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
26756
+ const { join: join30, dirname: dirname17 } = await import("path");
25959
26757
  const exportId = safeArchiveSegment(String(bundle.export.export_id ?? `export-${Date.now()}`));
25960
26758
  const archiveFormat = flags.format === "directory" ? "directory" : "zip";
25961
26759
  const password = typeof flags.password === "string" && flags.password.length > 0 ? flags.password : null;
25962
26760
  if (password && archiveFormat !== "zip") throw new Error("Password-protected account export is only supported for zip format.");
25963
- const outputPath = typeof flags.output === "string" ? flags.output : join29(process.cwd(), archiveFormat === "zip" ? `openmates-account-export-${exportId}${password ? ".zip.enc" : ".zip"}` : `openmates-account-export-${exportId}`);
26761
+ const outputPath = typeof flags.output === "string" ? flags.output : join30(process.cwd(), archiveFormat === "zip" ? `openmates-account-export-${exportId}${password ? ".zip.enc" : ".zip"}` : `openmates-account-export-${exportId}`);
25964
26762
  const zip = archiveFormat === "zip" ? new JSZip3() : null;
25965
26763
  const writtenFiles = [];
25966
26764
  async function writeArchiveText(relativePath, content) {
@@ -25971,9 +26769,9 @@ async function writeAccountExportArchive(bundle, flags = {}) {
25971
26769
  return;
25972
26770
  }
25973
26771
  const stagingDir = outputPath;
25974
- const fullPath = join29(stagingDir, relativePath);
25975
- await mkdir3(dirname17(fullPath), { recursive: true });
25976
- await writeFile2(fullPath, content, "utf-8");
26772
+ const fullPath = join30(stagingDir, relativePath);
26773
+ await mkdir4(dirname17(fullPath), { recursive: true });
26774
+ await writeFile3(fullPath, content, "utf-8");
25977
26775
  writtenFiles.push(relativePath);
25978
26776
  }
25979
26777
  await writeArchiveText("README.md", buildAccountExportReadme(bundle));
@@ -25989,9 +26787,9 @@ async function writeAccountExportArchive(bundle, flags = {}) {
25989
26787
  }
25990
26788
  await writeAccountExportChatFiles(domainPayloads.chats ?? [], writeArchiveText);
25991
26789
  if (archiveFormat === "directory") return { output: outputPath, format: "directory", files: writtenFiles.length };
25992
- await mkdir3(dirname17(outputPath), { recursive: true });
26790
+ await mkdir4(dirname17(outputPath), { recursive: true });
25993
26791
  const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
25994
- await writeFile2(outputPath, password ? encryptZipBuffer(zipBuffer, password) : zipBuffer);
26792
+ await writeFile3(outputPath, password ? encryptZipBuffer(zipBuffer, password) : zipBuffer);
25995
26793
  return { output: outputPath, format: "zip", files: writtenFiles.length, ...password ? { encrypted: true } : {} };
25996
26794
  }
25997
26795
  function encryptZipBuffer(zipBuffer, password) {
@@ -26427,7 +27225,7 @@ var OpenMates = class {
26427
27225
  }
26428
27226
  async resolveEmbedKeyForShare(embedKeys, embedId) {
26429
27227
  const masterKey = await this.getMasterKey();
26430
- const hashedEmbedId = createHash15("sha256").update(embedId).digest("hex");
27228
+ const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
26431
27229
  return this.resolveLoadedEmbedKey(embedKeys, hashedEmbedId, masterKey, masterKey);
26432
27230
  }
26433
27231
  async decryptChatMetadata(chat, chatKeyWrappers) {
@@ -26486,7 +27284,7 @@ var OpenMates = class {
26486
27284
  return { ...payload, chat: decryptedChat, messages, embeds };
26487
27285
  }
26488
27286
  async resolveLoadedChatKey(chat, chatKeyWrappers) {
26489
- const hashedChatId = createHash15("sha256").update(chat.id).digest("hex");
27287
+ const hashedChatId = createHash16("sha256").update(chat.id).digest("hex");
26490
27288
  const wrapper = (chatKeyWrappers ?? []).find(
26491
27289
  (entry) => entry.key_type === "master" && entry.hashed_chat_id === hashedChatId && typeof entry.encrypted_chat_key === "string"
26492
27290
  );
@@ -26504,9 +27302,9 @@ var OpenMates = class {
26504
27302
  if (!embedId) {
26505
27303
  return { ...embed };
26506
27304
  }
26507
- let hashedEmbedId = createHash15("sha256").update(embedId).digest("hex");
27305
+ let hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
26508
27306
  if (!embedKeys.some((key) => key.hashed_embed_id === hashedEmbedId) && typeof embed.parent_embed_id === "string" && embed.parent_embed_id) {
26509
- hashedEmbedId = createHash15("sha256").update(embed.parent_embed_id).digest("hex");
27307
+ hashedEmbedId = createHash16("sha256").update(embed.parent_embed_id).digest("hex");
26510
27308
  }
26511
27309
  const embedKey = await this.resolveLoadedEmbedKey(embedKeys, hashedEmbedId, masterKey, chatKey);
26512
27310
  if (!embedKey) {
@@ -26752,7 +27550,7 @@ var OpenMates = class {
26752
27550
  }
26753
27551
  };
26754
27552
  function loadOrCreateDeviceId(customPath) {
26755
- const path2 = customPath ?? join8(homedir5(), ".openmates", "sdk-device-id");
27553
+ const path2 = customPath ?? join9(homedir5(), ".openmates", "sdk-device-id");
26756
27554
  if (existsSync8(path2)) {
26757
27555
  const stored = readFileSync10(path2, "utf8").trim();
26758
27556
  if (stored) return stored;
@@ -26866,7 +27664,7 @@ async function buildProjectCreatePayload(wrappingKey, input, teamId = null) {
26866
27664
  last_opened_at: timestamp,
26867
27665
  key_wrappers: teamId ? [{
26868
27666
  key_type: "team",
26869
- hashed_team_id: createHash15("sha256").update(teamId).digest("hex"),
27667
+ hashed_team_id: createHash16("sha256").update(teamId).digest("hex"),
26870
27668
  team_key_epoch: 1,
26871
27669
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrappingKey),
26872
27670
  wrapper_version: 1,
@@ -26876,7 +27674,7 @@ async function buildProjectCreatePayload(wrappingKey, input, teamId = null) {
26876
27674
  return { payload, projectKey };
26877
27675
  }
26878
27676
  async function decryptSdkProject(record7, wrappingKey, teamId = null) {
26879
- const encryptedProjectKey = teamId ? record7.key_wrappers?.find((wrapper) => wrapper.key_type === "team" && wrapper.hashed_team_id === createHash15("sha256").update(teamId).digest("hex") && wrapper.team_key_epoch === 1)?.encrypted_project_key : record7.encrypted_project_key;
27677
+ const encryptedProjectKey = teamId ? record7.key_wrappers?.find((wrapper) => wrapper.key_type === "team" && wrapper.hashed_team_id === createHash16("sha256").update(teamId).digest("hex") && wrapper.team_key_epoch === 1)?.encrypted_project_key : record7.encrypted_project_key;
26880
27678
  if (typeof encryptedProjectKey !== "string") throw new OpenMatesConfigError(`Project ${record7.project_id} is missing encrypted project key`);
26881
27679
  const projectKey = await decryptBytesWithAesGcm(encryptedProjectKey, wrappingKey);
26882
27680
  if (!projectKey) throw new OpenMatesConfigError(`Failed to decrypt Project key for ${record7.project_id}`);
@@ -26953,7 +27751,7 @@ async function resolveSdkProject(client2, projectId, context = { personal: true
26953
27751
  const response = await client2.get(withQuery(`/v1/projects/${encodeURIComponent(resolvedProjectId)}`, { team_id: crypto3.teamId }));
26954
27752
  const record7 = response.project;
26955
27753
  if (!record7) throw new OpenMatesApiError(404, { detail: "Project not found" });
26956
- const teamHash = crypto3.teamId ? createHash15("sha256").update(crypto3.teamId).digest("hex") : null;
27754
+ const teamHash = crypto3.teamId ? createHash16("sha256").update(crypto3.teamId).digest("hex") : null;
26957
27755
  const encryptedProjectKey = crypto3.teamId ? record7.key_wrappers?.find((wrapper) => wrapper.key_type === "team" && wrapper.hashed_team_id === teamHash && wrapper.team_key_epoch === 1)?.encrypted_project_key : record7.encrypted_project_key;
26958
27756
  if (typeof encryptedProjectKey !== "string") throw new OpenMatesConfigError("Project response is missing encrypted Project key wrapper");
26959
27757
  const projectKey = await decryptBytesWithAesGcm(encryptedProjectKey, crypto3.key);
@@ -26983,7 +27781,7 @@ async function resolveSdkChatKey(client2, chatId) {
26983
27781
  const payload = await client2.get(`/v1/sdk/chats/${encodeURIComponent(resolvedChatId)}`);
26984
27782
  const chat = payload.chat;
26985
27783
  if (!chat) throw new OpenMatesConfigError("Saved chat payload did not include chat metadata");
26986
- const hashedChatId = createHash15("sha256").update(chat.id).digest("hex");
27784
+ const hashedChatId = createHash16("sha256").update(chat.id).digest("hex");
26987
27785
  const wrappers = Array.isArray(payload.chat_key_wrappers) ? payload.chat_key_wrappers : Array.isArray(chat.chat_key_wrappers) ? chat.chat_key_wrappers : [];
26988
27786
  const wrapper = wrappers.find(
26989
27787
  (entry) => entry.key_type === "master" && entry.hashed_chat_id === hashedChatId && typeof entry.encrypted_chat_key === "string"
@@ -28038,7 +28836,7 @@ var OpenMatesIdeaBucket = class {
28038
28836
  processing_window_id: bucketId,
28039
28837
  ideas: [{ index: 1, type: "text", text: ideaText }]
28040
28838
  });
28041
- const payloadHash = createHash15("sha256").update(serverProcessablePayload).digest("hex");
28839
+ const payloadHash = createHash16("sha256").update(serverProcessablePayload).digest("hex");
28042
28840
  const masterKey = await this.client.masterKey();
28043
28841
  const chatKey = new Uint8Array(randomBytes7(32));
28044
28842
  const encryptedChatKey = await encryptBytesWithAesGcm(chatKey, masterKey);
@@ -30219,12 +31017,12 @@ async function readActivityHistory(options) {
30219
31017
  }
30220
31018
 
30221
31019
  // src/taskActivityDelivery.ts
30222
- import { createHash as createHash16 } from "crypto";
31020
+ import { createHash as createHash17 } from "crypto";
30223
31021
  import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, readdirSync as readdirSync3, rmSync as rmSync3, existsSync as existsSync9 } from "fs";
30224
- import { join as join9 } from "path";
31022
+ import { join as join10 } from "path";
30225
31023
  import lockfile4 from "proper-lockfile";
30226
31024
  function activityDeliveryStore(scope2, stateDir2 = resolveStateDir(), now = Date.now, ownerHash) {
30227
- const directory = join9(stateDir2, "task-activity-delivery", createHash16("sha256").update(scope2).digest("hex"));
31025
+ const directory = join10(stateDir2, "task-activity-delivery", createHash17("sha256").update(scope2).digest("hex"));
30228
31026
  mkdirSync8(directory, { recursive: true, mode: 448 });
30229
31027
  let parts = null;
30230
31028
  try {
@@ -30233,9 +31031,9 @@ function activityDeliveryStore(scope2, stateDir2 = resolveStateDir(), now = Date
30233
31031
  } catch {
30234
31032
  }
30235
31033
  const accountScope = parts ? JSON.stringify(parts.slice(0, 3)) : scope2;
30236
- if (parts) atomicPrivateFile(join9(directory, "scope.json"), JSON.stringify(parts));
30237
- const pendingDirectory = join9(directory, "pending");
30238
- const acknowledgedDirectory = join9(directory, "acknowledged");
31034
+ if (parts) atomicPrivateFile(join10(directory, "scope.json"), JSON.stringify(parts));
31035
+ const pendingDirectory = join10(directory, "pending");
31036
+ const acknowledgedDirectory = join10(directory, "acknowledged");
30239
31037
  mkdirSync8(pendingDirectory, { recursive: true, mode: 448 });
30240
31038
  mkdirSync8(acknowledgedDirectory, { recursive: true, mode: 448 });
30241
31039
  const save = (path2, record7) => {
@@ -30259,7 +31057,7 @@ function activityDeliveryStore(scope2, stateDir2 = resolveStateDir(), now = Date
30259
31057
  throw error;
30260
31058
  }
30261
31059
  record7.acknowledged = acknowledged;
30262
- save(join9(acknowledgedDirectory, `${record7.input.entry_id}.json`), record7);
31060
+ save(join10(acknowledgedDirectory, `${record7.input.entry_id}.json`), record7);
30263
31061
  rmSync3(path2, { force: true });
30264
31062
  }
30265
31063
  return record7.acknowledged;
@@ -30269,8 +31067,8 @@ function activityDeliveryStore(scope2, stateDir2 = resolveStateDir(), now = Date
30269
31067
  if (!/^[a-f0-9]{64}$/.test(id)) throw new Error("--delivery-id must be a SHA-256 hex identifier");
30270
31068
  const release2 = await lockTaskDeliveryRecord(directory, id);
30271
31069
  try {
30272
- const acknowledgedPath = join9(acknowledgedDirectory, `${id}.json`);
30273
- const path2 = existsSync9(acknowledgedPath) ? acknowledgedPath : join9(pendingDirectory, `${id}.json`);
31070
+ const acknowledgedPath = join10(acknowledgedDirectory, `${id}.json`);
31071
+ const path2 = existsSync9(acknowledgedPath) ? acknowledgedPath : join10(pendingDirectory, `${id}.json`);
30274
31072
  let record7;
30275
31073
  try {
30276
31074
  record7 = JSON.parse(readFileSync11(path2, "utf8"));
@@ -30293,7 +31091,7 @@ function activityDeliveryStore(scope2, stateDir2 = resolveStateDir(), now = Date
30293
31091
  let rejected = 0;
30294
31092
  let notice;
30295
31093
  for (const name of names) {
30296
- const path2 = join9(pendingDirectory, name);
31094
+ const path2 = join10(pendingDirectory, name);
30297
31095
  const record7 = JSON.parse(readFileSync11(path2, "utf8"));
30298
31096
  if (record7.acknowledged) continue;
30299
31097
  if (record7.rejected) {
@@ -30330,13 +31128,13 @@ function activityDeliveryStore(scope2, stateDir2 = resolveStateDir(), now = Date
30330
31128
  async function flushPendingTaskActivities(client2, teamId, stateDir2 = resolveStateDir(), onNotice) {
30331
31129
  const session = client2.getSession();
30332
31130
  const expected = JSON.stringify([session.apiUrl, session.hashedEmail, teamId || "personal"]);
30333
- const base = join9(stateDir2, "task-activity-delivery");
31131
+ const base = join10(stateDir2, "task-activity-delivery");
30334
31132
  if (!existsSync9(base)) return;
30335
31133
  for (const folder of readdirSync3(base)) {
30336
31134
  if (!/^[a-f0-9]{64}$/.test(folder)) continue;
30337
31135
  let scope2;
30338
31136
  try {
30339
- scope2 = JSON.parse(readFileSync11(join9(base, folder, "scope.json"), "utf8"));
31137
+ scope2 = JSON.parse(readFileSync11(join10(base, folder, "scope.json"), "utf8"));
30340
31138
  } catch (error) {
30341
31139
  if (error.code === "ENOENT") continue;
30342
31140
  throw error;
@@ -30359,8 +31157,8 @@ async function flushPendingTaskActivities(client2, teamId, stateDir2 = resolveSt
30359
31157
  // src/cli.ts
30360
31158
  import { existsSync as existsSync26, readFileSync as readFileSync24, realpathSync as realpathSync12, readdirSync as readdirSync14, statSync as statSync11, writeFileSync as writeFileSync16 } from "fs";
30361
31159
  import { fileURLToPath as fileURLToPath4 } from "url";
30362
- import { basename as basename9, dirname as dirname16, join as join28 } from "path";
30363
- import { createHash as createHash29, randomBytes as randomBytes14, randomUUID as randomUUID23 } from "crypto";
31160
+ import { basename as basename9, dirname as dirname16, join as join29 } from "path";
31161
+ import { createHash as createHash30, randomBytes as randomBytes14, randomUUID as randomUUID23 } from "crypto";
30364
31162
  import { arch as arch2, platform as platform3 } from "os";
30365
31163
  import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
30366
31164
 
@@ -30614,13 +31412,13 @@ var SECRET_ENV_PATTERNS = [
30614
31412
  ];
30615
31413
 
30616
31414
  // ../secret-scanner/src/patterns.ts
30617
- function getSecretSuffix(match, n = 3) {
31415
+ function getSecretSuffix2(match, n = 3) {
30618
31416
  const cleaned = match.replace(/['";\s]+$/g, "");
30619
31417
  if (cleaned.length <= n) return cleaned;
30620
31418
  return cleaned.slice(-n);
30621
31419
  }
30622
31420
  function generatePlaceholder(prefix, match, suffixLength = 3, counter = 1) {
30623
- return `[${prefix}_${counter}_${getSecretSuffix(match, suffixLength)}]`;
31421
+ return `[${prefix}_${counter}_${getSecretSuffix2(match, suffixLength)}]`;
30624
31422
  }
30625
31423
  var SECRET_PATTERNS = [
30626
31424
  {
@@ -30789,11 +31587,11 @@ var SecretRegistry = class {
30789
31587
  return this.entries.get(value).token;
30790
31588
  }
30791
31589
  const prefix = this.getPlaceholderPrefix(name, type);
30792
- let token = `[${prefix}_${getSecretSuffix(value, this.suffixLength)}]`;
31590
+ let token = `[${prefix}_${getSecretSuffix2(value, this.suffixLength)}]`;
30793
31591
  const existing = this.usedPlaceholders.get(token);
30794
31592
  if (existing && existing !== value) {
30795
31593
  for (let len = this.suffixLength + 1; len <= 8; len++) {
30796
- const candidate = `[${prefix}_${getSecretSuffix(value, len)}]`;
31594
+ const candidate = `[${prefix}_${getSecretSuffix2(value, len)}]`;
30797
31595
  const candidateExisting = this.usedPlaceholders.get(candidate);
30798
31596
  if (!candidateExisting || candidateExisting === value) {
30799
31597
  token = candidate;
@@ -31114,6 +31912,7 @@ var SecretScanner = class {
31114
31912
  const allMappings = [];
31115
31913
  const coveredRanges = [];
31116
31914
  const urlExclusionZones = getUrlExclusionZones(text4);
31915
+ const identifierRanges = codingIdentifierRanges(text4);
31117
31916
  if (this.options.enableRegistryDetection && this.registry.size > 0) {
31118
31917
  const registryMappings = this.registry.findAll(text4);
31119
31918
  for (const mapping of registryMappings) {
@@ -31138,6 +31937,7 @@ var SecretScanner = class {
31138
31937
  const matchText = match[0];
31139
31938
  const startIndex = match.index;
31140
31939
  const endIndex = startIndex + matchText.length;
31940
+ if (pattern.type === "PHONE" && identifierRanges.some((range) => startIndex < range.end && endIndex > range.start)) continue;
31141
31941
  if (isInsideAnyRange(startIndex, endIndex, urlExclusionZones)) continue;
31142
31942
  const overlaps = coveredRanges.some(
31143
31943
  (range) => startIndex >= range.start && startIndex < range.end || endIndex > range.start && endIndex <= range.end || startIndex <= range.start && endIndex >= range.end
@@ -32857,12 +33657,12 @@ function resolveHostNotificationDestinations(value, deploymentMode, serverEnviro
32857
33657
 
32858
33658
  // src/server.ts
32859
33659
  import { execFileSync as execFileSync5, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
32860
- import { createHash as createHash18, randomBytes as randomBytes9, randomUUID as randomUUID15 } from "crypto";
33660
+ import { createHash as createHash19, randomBytes as randomBytes9, randomUUID as randomUUID15 } from "crypto";
32861
33661
  import { appendFileSync, chmodSync as chmodSync8, closeSync as closeSync4, copyFileSync as copyFileSync2, cpSync, existsSync as existsSync13, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, openSync as openSync4, readFileSync as readFileSync14, readSync, readdirSync as readdirSync6, rmSync as rmSync8, writeFileSync as writeFileSync8 } from "fs";
32862
33662
  import { createInterface as createInterface3 } from "readline";
32863
33663
  import { createInterface as createPromptInterface } from "readline/promises";
32864
33664
  import { homedir as homedir6, tmpdir as tmpdir2 } from "os";
32865
- import { dirname as dirname8, join as join15, resolve as resolve7 } from "path";
33665
+ import { dirname as dirname8, join as join16, resolve as resolve7 } from "path";
32866
33666
 
32867
33667
  // src/branding.ts
32868
33668
  var OPENMATES_WORDMARK = "OPENMATES";
@@ -32905,17 +33705,17 @@ import {
32905
33705
  symlinkSync
32906
33706
  } from "fs";
32907
33707
  import { createRequire as createRequire3 } from "module";
32908
- import { dirname as dirname4, join as join10, resolve as resolve5 } from "path";
32909
- var UI_PACKAGE_RELATIVE_PATH = join10("frontend", "packages", "ui");
32910
- var GENERATED_LOCALES_RELATIVE_PATH = join10(UI_PACKAGE_RELATIVE_PATH, "src", "i18n", "locales");
32911
- var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH = join10(UI_PACKAGE_RELATIVE_PATH, "scripts", "build-translations.js");
33708
+ import { dirname as dirname4, join as join11, resolve as resolve5 } from "path";
33709
+ var UI_PACKAGE_RELATIVE_PATH = join11("frontend", "packages", "ui");
33710
+ var GENERATED_LOCALES_RELATIVE_PATH = join11(UI_PACKAGE_RELATIVE_PATH, "src", "i18n", "locales");
33711
+ var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH = join11(UI_PACKAGE_RELATIVE_PATH, "scripts", "build-translations.js");
32912
33712
  var REQUIRED_LOCALE_JSON = "en.json";
32913
33713
  function sourceInstallLocalesPath(rootPath) {
32914
- return join10(rootPath, GENERATED_LOCALES_RELATIVE_PATH);
33714
+ return join11(rootPath, GENERATED_LOCALES_RELATIVE_PATH);
32915
33715
  }
32916
33716
  function ensureSourceInstallTranslations(installPath, sourcePath) {
32917
33717
  const localeDir = sourceInstallLocalesPath(installPath);
32918
- const requiredLocalePath = join10(localeDir, REQUIRED_LOCALE_JSON);
33718
+ const requiredLocalePath = join11(localeDir, REQUIRED_LOCALE_JSON);
32919
33719
  if (existsSync10(requiredLocalePath)) {
32920
33720
  return { status: "already_present", localeDir, copiedFiles: 0 };
32921
33721
  }
@@ -32935,27 +33735,27 @@ function copyGeneratedLocaleJson(sourcePath, installPath) {
32935
33735
  const targetRoot = resolve5(installPath);
32936
33736
  if (sourceRoot === targetRoot) return 0;
32937
33737
  const sourceLocaleDir = sourceInstallLocalesPath(sourceRoot);
32938
- if (!existsSync10(join10(sourceLocaleDir, REQUIRED_LOCALE_JSON))) return 0;
33738
+ if (!existsSync10(join11(sourceLocaleDir, REQUIRED_LOCALE_JSON))) return 0;
32939
33739
  const targetLocaleDir = sourceInstallLocalesPath(targetRoot);
32940
33740
  mkdirSync9(targetLocaleDir, { recursive: true });
32941
33741
  let copiedFiles = 0;
32942
33742
  for (const entry of readdirSync4(sourceLocaleDir, { withFileTypes: true })) {
32943
33743
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
32944
- copyFileSync(join10(sourceLocaleDir, entry.name), join10(targetLocaleDir, entry.name));
33744
+ copyFileSync(join11(sourceLocaleDir, entry.name), join11(targetLocaleDir, entry.name));
32945
33745
  copiedFiles += 1;
32946
33746
  }
32947
33747
  return copiedFiles;
32948
33748
  }
32949
33749
  function runBuildTranslationsScript(installPath) {
32950
- const uiPackageDir = join10(installPath, UI_PACKAGE_RELATIVE_PATH);
32951
- const buildScript = join10(installPath, BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH);
33750
+ const uiPackageDir = join11(installPath, UI_PACKAGE_RELATIVE_PATH);
33751
+ const buildScript = join11(installPath, BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH);
32952
33752
  if (!existsSync10(buildScript)) {
32953
33753
  throw new Error(`Translation build script not found at ${buildScript}.`);
32954
33754
  }
32955
33755
  const requireFromCli = createRequire3(import.meta.url);
32956
33756
  const yamlPackageDir = dirname4(requireFromCli.resolve("yaml/package.json"));
32957
- const nodeModulesDir = join10(uiPackageDir, "node_modules");
32958
- const yamlLink = join10(nodeModulesDir, "yaml");
33757
+ const nodeModulesDir = join11(uiPackageDir, "node_modules");
33758
+ const yamlLink = join11(nodeModulesDir, "yaml");
32959
33759
  const hadNodeModulesDir = existsSync10(nodeModulesDir);
32960
33760
  const hadYamlPackage = existsSync10(yamlLink);
32961
33761
  if (!hadYamlPackage) {
@@ -32976,7 +33776,7 @@ function runBuildTranslationsScript(installPath) {
32976
33776
  }
32977
33777
 
32978
33778
  // src/serverPlanning.ts
32979
- import { dirname as dirname5, join as join11, resolve as resolve6 } from "path";
33779
+ import { dirname as dirname5, join as join12, resolve as resolve6 } from "path";
32980
33780
  var CORE_WORKER_SERVICES = [
32981
33781
  "task-worker",
32982
33782
  "user-init-worker",
@@ -33186,10 +33986,10 @@ function parseServerRole(value) {
33186
33986
  throw new Error(`Unsupported server role '${value}'. Use core, upload, or preview.`);
33187
33987
  }
33188
33988
  function defaultOpenMatesCloudOverlayPath(openMatesPath) {
33189
- return resolve6(join11(dirname5(resolve6(openMatesPath)), OPENMATESCLOUD_OVERLAY_DIR));
33989
+ return resolve6(join12(dirname5(resolve6(openMatesPath)), OPENMATESCLOUD_OVERLAY_DIR));
33190
33990
  }
33191
33991
  function defaultOpenMatesCloudComposeFile(overlayPath) {
33192
- return resolve6(join11(overlayPath, OPENMATESCLOUD_COMPOSE_FILE));
33992
+ return resolve6(join12(overlayPath, OPENMATESCLOUD_COMPOSE_FILE));
33193
33993
  }
33194
33994
  function planOpenMatesCloudOverlay(input) {
33195
33995
  const deploymentMode = input.deploymentMode ?? "self_host";
@@ -33631,11 +34431,11 @@ function publishServerBackupArchive(sourceDir, archivePath, options = {}) {
33631
34431
 
33632
34432
  // src/serverCaddyUpdate.ts
33633
34433
  import { execFileSync as execFileSync4 } from "child_process";
33634
- import { createHash as createHash17 } from "crypto";
34434
+ import { createHash as createHash18 } from "crypto";
33635
34435
  import { accessSync, constants as constants2, existsSync as existsSync11, mkdirSync as mkdirSync10, mkdtempSync, readFileSync as readFileSync12, renameSync as renameSync5, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "fs";
33636
34436
  import { tmpdir } from "os";
33637
- import { dirname as dirname6, join as join12 } from "path";
33638
- var hash = (text4) => createHash17("sha256").update(text4).digest("hex");
34437
+ import { dirname as dirname6, join as join13 } from "path";
34438
+ var hash = (text4) => createHash18("sha256").update(text4).digest("hex");
33639
34439
  function resolveCaddyProfile(role, explicit, previous) {
33640
34440
  if (explicit === void 0) return previous ?? "self-host";
33641
34441
  if (role !== "upload" || explicit !== "official-upload") throw new Error("caddy_profile_unsupported");
@@ -33829,9 +34629,9 @@ function readCaddyUpdateState(path2, configPath, site) {
33829
34629
  function mergeCaddyRelease(current, previous, target) {
33830
34630
  if (previous === target) return current;
33831
34631
  if (current === previous) return target;
33832
- const directory = mkdtempSync(join12(tmpdir(), "openmates-caddy-merge-"));
34632
+ const directory = mkdtempSync(join13(tmpdir(), "openmates-caddy-merge-"));
33833
34633
  try {
33834
- const files = ["current", "previous", "target"].map((name) => join12(directory, name));
34634
+ const files = ["current", "previous", "target"].map((name) => join13(directory, name));
33835
34635
  [current, previous, target].forEach((content, i) => writeFileSync6(files[i], content, { mode: 384 }));
33836
34636
  try {
33837
34637
  return execFileSync4("diff3", ["-m", ...files], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 1e4 });
@@ -33925,7 +34725,7 @@ function caddyHostOperation(input) {
33925
34725
  async function applyCaddyPathUpdate(input) {
33926
34726
  if (!/^[a-f0-9]{40}$/i.test(input.revision)) throw new Error("caddy_release_revision_invalid");
33927
34727
  const current = caddyHostOperation({ action: "read", configPath: input.configPath }).content;
33928
- const statePath = join12(input.installPath, ".openmates", "caddy", `${input.role}.json`);
34728
+ const statePath = join13(input.installPath, ".openmates", "caddy", `${input.role}.json`);
33929
34729
  const previous = readCaddyUpdateState(statePath, input.configPath, input.site);
33930
34730
  const merged = previous ? { content: mergeCaddyRelease(current, previous.template, input.target), paths: Object.fromEntries([...matchers(input.target, input.site, input.profile === "official-upload")].map(([name, matcher]) => [name, matcher.paths])) } : mergeCaddyPaths({ current, target: input.target, site: input.site, scopeAware: input.profile === "official-upload" });
33931
34731
  if (input.profile === "official-upload") validateOfficialUploadCaddy(merged.content, officialUploadOrigins(input.target));
@@ -34642,9 +35442,9 @@ function signRuntimeWebhookPayload(payload, secret, timestamp, eventId) {
34642
35442
  // src/serverUpdateState.ts
34643
35443
  import { randomBytes as randomBytes8 } from "crypto";
34644
35444
  import { chmodSync as chmodSync6, closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync11, openSync as openSync3, readFileSync as readFileSync13, renameSync as renameSync6, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "fs";
34645
- import { dirname as dirname7, join as join13 } from "path";
35445
+ import { dirname as dirname7, join as join14 } from "path";
34646
35446
  function serverUpdateStatusFile(installPath, role) {
34647
- return join13(installPath, ".openmates", `${role}-update-status.json`);
35447
+ return join14(installPath, ".openmates", `${role}-update-status.json`);
34648
35448
  }
34649
35449
  function readServerUpdateStatus(installPath, role) {
34650
35450
  const filePath = serverUpdateStatusFile(installPath, role);
@@ -34669,8 +35469,8 @@ function writeServerUpdateStatus(installPath, role, status) {
34669
35469
  chmodSync6(filePath, 384);
34670
35470
  }
34671
35471
  function acquireServerUpdateLock(installPath) {
34672
- const stateDir2 = join13(installPath, ".openmates");
34673
- const lockPath = join13(stateDir2, "server-update.lock");
35472
+ const stateDir2 = join14(installPath, ".openmates");
35473
+ const lockPath = join14(stateDir2, "server-update.lock");
34674
35474
  mkdirSync11(stateDir2, { recursive: true, mode: 448 });
34675
35475
  let descriptor;
34676
35476
  try {
@@ -34911,11 +35711,11 @@ async function resolveStableImageTag(fetcher = fetch) {
34911
35711
 
34912
35712
  // src/serverRuntimePaths.ts
34913
35713
  import { chmodSync as chmodSync7, lstatSync as lstatSync3, mkdirSync as mkdirSync12 } from "fs";
34914
- import { join as join14 } from "path";
35714
+ import { join as join15 } from "path";
34915
35715
  function ensureRuntimeMetricsDirectory(installPath) {
34916
35716
  let path2 = installPath;
34917
35717
  for (const part of [".openmates", "runtime-health", "metrics"]) {
34918
- path2 = join14(path2, part);
35718
+ path2 = join15(path2, part);
34919
35719
  try {
34920
35720
  mkdirSync12(path2, { mode: 448 });
34921
35721
  } catch (error) {
@@ -34932,23 +35732,23 @@ function ensureRuntimeMetricsDirectory(installPath) {
34932
35732
  }
34933
35733
 
34934
35734
  // src/server.ts
34935
- var SOURCE_COMPOSE_FILE = join15("backend", "core", "docker-compose.yml");
35735
+ var SOURCE_COMPOSE_FILE = join16("backend", "core", "docker-compose.yml");
34936
35736
  var ROLE_IMAGE_COMPOSE_FILES = {
34937
- core: join15("backend", "core", "docker-compose.selfhost.yml"),
34938
- upload: join15("backend", "upload", "docker-compose.yml"),
34939
- preview: join15("backend", "preview", "docker-compose.preview.yml")
35737
+ core: join16("backend", "core", "docker-compose.selfhost.yml"),
35738
+ upload: join16("backend", "upload", "docker-compose.yml"),
35739
+ preview: join16("backend", "preview", "docker-compose.preview.yml")
34940
35740
  };
34941
35741
  var ROLE_TEMPLATE_FILES = {
34942
- core: join15("core", "docker-compose.selfhost.yml"),
34943
- upload: join15("upload", "docker-compose.yml"),
34944
- preview: join15("preview", "docker-compose.preview.yml")
35742
+ core: join16("core", "docker-compose.selfhost.yml"),
35743
+ upload: join16("upload", "docker-compose.yml"),
35744
+ preview: join16("preview", "docker-compose.preview.yml")
34945
35745
  };
34946
- var CORE_NO_WEBAPP_TEMPLATE_FILE = join15("core", "docker-compose.no-webapp.yml");
34947
- var CORE_PROMTAIL_CONFIG_FILE = join15("backend", "core", "monitoring", "promtail", "promtail-config.yaml");
34948
- var CORE_ALERTMANAGER_CONFIG_FILE = join15("backend", "core", "monitoring", "alertmanager", "alertmanager.yml");
34949
- var CORE_PROMETHEUS_CONFIG_DIR = join15("backend", "core", "monitoring", "prometheus");
34950
- var COMPOSE_OVERRIDE2 = join15("backend", "core", "docker-compose.override.yml");
34951
- var DEFAULT_INSTALL_PATH = join15(homedir6(), "openmates");
35746
+ var CORE_NO_WEBAPP_TEMPLATE_FILE = join16("core", "docker-compose.no-webapp.yml");
35747
+ var CORE_PROMTAIL_CONFIG_FILE = join16("backend", "core", "monitoring", "promtail", "promtail-config.yaml");
35748
+ var CORE_ALERTMANAGER_CONFIG_FILE = join16("backend", "core", "monitoring", "alertmanager", "alertmanager.yml");
35749
+ var CORE_PROMETHEUS_CONFIG_DIR = join16("backend", "core", "monitoring", "prometheus");
35750
+ var COMPOSE_OVERRIDE2 = join16("backend", "core", "docker-compose.override.yml");
35751
+ var DEFAULT_INSTALL_PATH = join16(homedir6(), "openmates");
34952
35752
  var REPO_URL = "https://github.com/glowingkitty/OpenMates.git";
34953
35753
  var ROLE_PROVENANCE_SERVICE = {
34954
35754
  core: "api",
@@ -35038,8 +35838,8 @@ scrape_configs:
35038
35838
  - labels:
35039
35839
  level: log_level
35040
35840
  `;
35041
- var BACKEND_CONFIG_FILE = join15("backend", "config", "backend_config.yml");
35042
- var IMAGE_RUNTIME_CONFIG_FILE = join15("config", "backend_config.yml");
35841
+ var BACKEND_CONFIG_FILE = join16("backend", "config", "backend_config.yml");
35842
+ var IMAGE_RUNTIME_CONFIG_FILE = join16("config", "backend_config.yml");
35043
35843
  var LOCAL_AI_MODELS_FILE = "local-ai-models.yml";
35044
35844
  var OFF_BY_DEFAULT_FEATURES = /* @__PURE__ */ new Map([
35045
35845
  ["embed:code:application", "Application previews are still unstable"],
@@ -35175,8 +35975,8 @@ function runInteractive(cmd, args, cwd) {
35175
35975
  });
35176
35976
  }
35177
35977
  function beginEngineeringRuntimeOperation(installPath, operationType, services) {
35178
- const manager = join15(installPath, "scripts", "engineering_control_plane.py");
35179
- const sharedConfig = join15(homedir6(), ".config", "openmates", "engineering-control-plane.env");
35978
+ const manager = join16(installPath, "scripts", "engineering_control_plane.py");
35979
+ const sharedConfig = join16(homedir6(), ".config", "openmates", "engineering-control-plane.env");
35180
35980
  if (!existsSync13(manager) || !existsSync13(sharedConfig)) return null;
35181
35981
  const requestedBy = process.env.CODEX_THREAD_ID || `openmates-cli:${process.pid}`;
35182
35982
  const args = [
@@ -35201,7 +36001,7 @@ function beginEngineeringRuntimeOperation(installPath, operationType, services)
35201
36001
  return parsed.operation_key;
35202
36002
  }
35203
36003
  function finishEngineeringRuntimeOperation(installPath, operationKey, status) {
35204
- const manager = join15(installPath, "scripts", "engineering_control_plane.py");
36004
+ const manager = join16(installPath, "scripts", "engineering_control_plane.py");
35205
36005
  execFileSync5(
35206
36006
  "python3",
35207
36007
  [manager, "operation", "update", "--operation-key", operationKey, "--status", status],
@@ -35230,8 +36030,8 @@ function loadConfigForInstallPath(installPath) {
35230
36030
  }
35231
36031
  function getInstallMode(installPath, config = loadConfigForInstallPath(installPath)) {
35232
36032
  if (config?.installMode) return config.installMode;
35233
- if (existsSync13(join15(installPath, SOURCE_COMPOSE_FILE))) return "source";
35234
- if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync13(join15(installPath, composeFile)))) return "image";
36033
+ if (existsSync13(join16(installPath, SOURCE_COMPOSE_FILE))) return "source";
36034
+ if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync13(join16(installPath, composeFile)))) return "image";
35235
36035
  return "source";
35236
36036
  }
35237
36037
  function getServerRole(flags, config) {
@@ -35458,7 +36258,7 @@ function composeArgs(installPath, withOverrides, installMode = getInstallMode(in
35458
36258
  withOverrides,
35459
36259
  installMode,
35460
36260
  role,
35461
- overrideExists: existsSync13(join15(installPath, COMPOSE_OVERRIDE2)),
36261
+ overrideExists: existsSync13(join16(installPath, COMPOSE_OVERRIDE2)),
35462
36262
  deploymentMode,
35463
36263
  overlayPath,
35464
36264
  overlayComposeFile,
@@ -35466,7 +36266,7 @@ function composeArgs(installPath, withOverrides, installMode = getInstallMode(in
35466
36266
  });
35467
36267
  }
35468
36268
  function ensureGitWorkDirEnv(installPath) {
35469
- const envPath = join15(installPath, ".env");
36269
+ const envPath = join16(installPath, ".env");
35470
36270
  if (!existsSync13(envPath)) return;
35471
36271
  const content = readFileSync14(envPath, "utf-8");
35472
36272
  const lineRegex = /^GIT_WORK_DIR=.*$/m;
@@ -35594,36 +36394,36 @@ async function fetchText(url) {
35594
36394
  return response.text();
35595
36395
  }
35596
36396
  function packagedTemplatePath(role) {
35597
- return join15(dirname8(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
36397
+ return join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
35598
36398
  }
35599
36399
  function packagedNoWebappTemplatePath() {
35600
- return join15(dirname8(new URL(import.meta.url).pathname), "..", "templates", CORE_NO_WEBAPP_TEMPLATE_FILE);
36400
+ return join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", CORE_NO_WEBAPP_TEMPLATE_FILE);
35601
36401
  }
35602
36402
  function packagedCaddyTemplatePath(role) {
35603
- return join15(dirname8(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
36403
+ return join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
35604
36404
  }
35605
36405
  function packagedCoreAlertmanagerTemplatePath() {
35606
- return join15(dirname8(new URL(import.meta.url).pathname), "..", "templates", "core", "monitoring", "alertmanager", "alertmanager.yml");
36406
+ return join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", "core", "monitoring", "alertmanager", "alertmanager.yml");
35607
36407
  }
35608
36408
  function ensureCoreAlertmanagerRuntimeFile(installPath) {
35609
36409
  const templatePath = packagedCoreAlertmanagerTemplatePath();
35610
36410
  if (!existsSync13(templatePath)) throw new Error(`Packaged Alertmanager config not found: ${templatePath}`);
35611
- const runtimePath = join15(installPath, CORE_ALERTMANAGER_CONFIG_FILE);
36411
+ const runtimePath = join16(installPath, CORE_ALERTMANAGER_CONFIG_FILE);
35612
36412
  if (fileHash(templatePath) === fileHash(runtimePath)) return;
35613
36413
  mkdirSync13(dirname8(runtimePath), { recursive: true });
35614
36414
  copyFileSync2(templatePath, runtimePath);
35615
36415
  }
35616
36416
  function ensureCorePrometheusRuntimeFiles(installPath) {
35617
- const packagedDir = join15(dirname8(new URL(import.meta.url).pathname), "..", "templates", "core", "monitoring", "prometheus");
35618
- const runtimeDir = join15(installPath, CORE_PROMETHEUS_CONFIG_DIR);
36417
+ const packagedDir = join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", "core", "monitoring", "prometheus");
36418
+ const runtimeDir = join16(installPath, CORE_PROMETHEUS_CONFIG_DIR);
35619
36419
  for (const fileName of ["prometheus.yml", "alert_rules.yml"]) {
35620
- const packagedPath = join15(packagedDir, fileName);
36420
+ const packagedPath = join16(packagedDir, fileName);
35621
36421
  if (!existsSync13(packagedPath)) throw new Error(`Packaged Prometheus file not found: ${packagedPath}`);
35622
36422
  }
35623
36423
  mkdirSync13(runtimeDir, { recursive: true });
35624
36424
  for (const fileName of ["prometheus.yml", "alert_rules.yml"]) {
35625
- const runtimePath = join15(runtimeDir, fileName);
35626
- if (!existsSync13(runtimePath)) copyFileSync2(join15(packagedDir, fileName), runtimePath);
36425
+ const runtimePath = join16(runtimeDir, fileName);
36426
+ if (!existsSync13(runtimePath)) copyFileSync2(join16(packagedDir, fileName), runtimePath);
35627
36427
  }
35628
36428
  }
35629
36429
  function readOfficialCloudNoWebappComposeTemplate() {
@@ -35643,19 +36443,19 @@ function readOfficialCloudNoWebappComposeTemplate() {
35643
36443
  }
35644
36444
  function ensureOfficialCloudNoWebappComposeFile(installPath, deploymentMode, role) {
35645
36445
  if (deploymentMode !== "official_cloud" || role !== "core") return;
35646
- const filePath = join15(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE);
36446
+ const filePath = join16(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE);
35647
36447
  if (existsSync13(filePath)) return;
35648
36448
  mkdirSync13(dirname8(filePath), { recursive: true });
35649
36449
  writeFileSync8(filePath, readOfficialCloudNoWebappComposeTemplate());
35650
36450
  }
35651
36451
  function fileHash(path2) {
35652
36452
  if (!existsSync13(path2)) return null;
35653
- return createHash18("sha256").update(readFileSync14(path2)).digest("hex");
36453
+ return createHash19("sha256").update(readFileSync14(path2)).digest("hex");
35654
36454
  }
35655
36455
  async function loadSelfHostComposeTemplate(templateRef, role, imageTag, packageVersion = getPackageVersion()) {
35656
36456
  const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
35657
36457
  if (templateDir) {
35658
- return readFileSync14(join15(resolve7(templateDir), ROLE_TEMPLATE_FILES[role]), "utf-8");
36458
+ return readFileSync14(join16(resolve7(templateDir), ROLE_TEMPLATE_FILES[role]), "utf-8");
35659
36459
  }
35660
36460
  const packaged = packagedTemplatePath(role);
35661
36461
  const source = resolveTemplateSource({
@@ -35671,24 +36471,24 @@ async function loadSelfHostComposeTemplate(templateRef, role, imageTag, packageV
35671
36471
  return fetchText(`https://raw.githubusercontent.com/glowingkitty/OpenMates/${source.ref}/${source.path}`);
35672
36472
  }
35673
36473
  async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
35674
- const roleDir = join15(installPath, "backend", role === "core" ? "core" : role);
35675
- const vaultConfigDir = join15(roleDir, "vault", "config");
36474
+ const roleDir = join16(installPath, "backend", role === "core" ? "core" : role);
36475
+ const vaultConfigDir = join16(roleDir, "vault", "config");
35676
36476
  mkdirSync13(vaultConfigDir, { recursive: true });
35677
- mkdirSync13(join15(installPath, "config", "providers"), { recursive: true });
35678
- writeFileSync8(join15(installPath, ROLE_IMAGE_COMPOSE_FILES[role]), await loadSelfHostComposeTemplate(templateRefForImageTag(imageTag, getPackageVersion()), role, imageTag));
36477
+ mkdirSync13(join16(installPath, "config", "providers"), { recursive: true });
36478
+ writeFileSync8(join16(installPath, ROLE_IMAGE_COMPOSE_FILES[role]), await loadSelfHostComposeTemplate(templateRefForImageTag(imageTag, getPackageVersion()), role, imageTag));
35679
36479
  if (role === "core") {
35680
- writeFileSync8(join15(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE), readOfficialCloudNoWebappComposeTemplate());
36480
+ writeFileSync8(join16(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE), readOfficialCloudNoWebappComposeTemplate());
35681
36481
  }
35682
36482
  if (role === "core") {
35683
36483
  ensureCorePrometheusRuntimeFiles(installPath);
35684
- const promtailConfigPath = join15(installPath, CORE_PROMTAIL_CONFIG_FILE);
36484
+ const promtailConfigPath = join16(installPath, CORE_PROMTAIL_CONFIG_FILE);
35685
36485
  mkdirSync13(dirname8(promtailConfigPath), { recursive: true });
35686
36486
  writeFileSync8(promtailConfigPath, SELFHOST_PROMTAIL_CONFIG_TEMPLATE);
35687
36487
  ensureCoreAlertmanagerRuntimeFile(installPath);
35688
36488
  }
35689
- writeFileSync8(join15(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
36489
+ writeFileSync8(join16(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
35690
36490
  ensureImageRuntimeConfig(installPath);
35691
- const envPath = join15(installPath, ".env");
36491
+ const envPath = join16(installPath, ".env");
35692
36492
  let envContent = existsSync13(envPath) ? readFileSync14(envPath, "utf-8") : MINIMAL_ENV_TEMPLATE;
35693
36493
  envContent = setEnvIfEmpty(envContent, "DATABASE_ADMIN_PASSWORD", randomHex(12));
35694
36494
  envContent = setEnvIfEmpty(envContent, "DATABASE_PASSWORD", randomHex(12));
@@ -35705,7 +36505,7 @@ async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
35705
36505
  `);
35706
36506
  }
35707
36507
  function getImageTagFromEnv(installPath, config) {
35708
- const envPath = join15(installPath, ".env");
36508
+ const envPath = join16(installPath, ".env");
35709
36509
  if (existsSync13(envPath)) {
35710
36510
  const envTag = getEnvVar(readFileSync14(envPath, "utf-8"), "OPENMATES_IMAGE_TAG");
35711
36511
  if (envTag) return envTag;
@@ -35740,7 +36540,7 @@ async function waitForServerHealth(installPath, role = "core", options = {}) {
35740
36540
  }
35741
36541
  throw new Error(`Updated ${role} server did not pass health checks in time. Tried ${healthUrl}.`);
35742
36542
  }
35743
- const envPath = join15(installPath, ".env");
36543
+ const envPath = join16(installPath, ".env");
35744
36544
  const envContent = existsSync13(envPath) ? readFileSync14(envPath, "utf-8") : "";
35745
36545
  const urls = deriveSelfHostCliUrls(envContent);
35746
36546
  const apiHealthUrl = `${trailingSlashTrimmed(urls.apiUrl)}/health`;
@@ -35781,7 +36581,7 @@ function hasLlmCredentials(envPath) {
35781
36581
  return false;
35782
36582
  }
35783
36583
  function warnIfMissingLlmCredentials(installPath) {
35784
- const envPath = join15(installPath, ".env");
36584
+ const envPath = join16(installPath, ".env");
35785
36585
  if (!existsSync13(envPath)) {
35786
36586
  console.error(
35787
36587
  "No .env file found. Run 'openmates server install' first, or create .env from .env.example."
@@ -35807,11 +36607,11 @@ function printJson(data) {
35807
36607
  console.log(JSON.stringify(data, null, 2));
35808
36608
  }
35809
36609
  function localModelsOverlayPath(installPath, installMode = getInstallMode(installPath)) {
35810
- if (installMode === "source") return join15(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
35811
- return join15(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
36610
+ if (installMode === "source") return join16(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
36611
+ return join16(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
35812
36612
  }
35813
36613
  function imageBackendConfigPath(installPath) {
35814
- return join15(installPath, IMAGE_RUNTIME_CONFIG_FILE);
36614
+ return join16(installPath, IMAGE_RUNTIME_CONFIG_FILE);
35815
36615
  }
35816
36616
  function ensureImageRuntimeConfig(installPath) {
35817
36617
  const configPath = imageBackendConfigPath(installPath);
@@ -35836,8 +36636,8 @@ function writeLocalModelOverlay(path2, overlay) {
35836
36636
  `);
35837
36637
  }
35838
36638
  function hasLocalAiModels(installPath) {
35839
- const imagePath = join15(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
35840
- const sourcePath = join15(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
36639
+ const imagePath = join16(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
36640
+ const sourcePath = join16(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
35841
36641
  return [imagePath, sourcePath].some((path2) => {
35842
36642
  if (!existsSync13(path2)) return false;
35843
36643
  try {
@@ -35880,10 +36680,10 @@ function nowStamp() {
35880
36680
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
35881
36681
  }
35882
36682
  function backupRoot(installPath) {
35883
- return join15(installPath, "backups");
36683
+ return join16(installPath, "backups");
35884
36684
  }
35885
36685
  function roleBackupDir(installPath, role) {
35886
- return join15(backupRoot(installPath), role);
36686
+ return join16(backupRoot(installPath), role);
35887
36687
  }
35888
36688
  function persistQuickServerTestOutcome(installPath, role, outcome) {
35889
36689
  const current = readServerUpdateStatus(installPath, role);
@@ -36044,7 +36844,7 @@ function copyIfExists(source, destination) {
36044
36844
  cpSync(source, destination, { recursive: true, force: true });
36045
36845
  }
36046
36846
  function readEnvMap(installPath) {
36047
- const envPath = join15(installPath, ".env");
36847
+ const envPath = join16(installPath, ".env");
36048
36848
  if (!existsSync13(envPath)) return {};
36049
36849
  const values = {};
36050
36850
  for (const line of readFileSync14(envPath, "utf-8").split("\n")) {
@@ -36057,7 +36857,7 @@ function readEnvMap(installPath) {
36057
36857
  return values;
36058
36858
  }
36059
36859
  function envPathForInstall(installPath) {
36060
- return join15(installPath, ".env");
36860
+ return join16(installPath, ".env");
36061
36861
  }
36062
36862
  function readEnvContent(installPath) {
36063
36863
  const envPath = envPathForInstall(installPath);
@@ -36066,13 +36866,13 @@ function readEnvContent(installPath) {
36066
36866
  function pruneEnvBackups(installPath) {
36067
36867
  const backups = readdirSync6(installPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith(ENV_BACKUP_PREFIX)).map((entry) => entry.name).sort();
36068
36868
  for (const backup of backups.slice(0, Math.max(0, backups.length - ENV_BACKUP_RETENTION_COUNT))) {
36069
- rmSync8(join15(installPath, backup), { force: true });
36869
+ rmSync8(join16(installPath, backup), { force: true });
36070
36870
  }
36071
36871
  }
36072
36872
  function backupEnvFile(installPath) {
36073
36873
  const envPath = envPathForInstall(installPath);
36074
36874
  if (!existsSync13(envPath)) return null;
36075
- const backupPath = join15(installPath, `${ENV_BACKUP_PREFIX}${nowStamp()}`);
36875
+ const backupPath = join16(installPath, `${ENV_BACKUP_PREFIX}${nowStamp()}`);
36076
36876
  copyFileSync2(envPath, backupPath);
36077
36877
  chmodSync8(backupPath, 384);
36078
36878
  pruneEnvBackups(installPath);
@@ -36293,7 +37093,7 @@ function formatSecretPreflight(preflight) {
36293
37093
  return parts.join("; ");
36294
37094
  }
36295
37095
  function hashFile(path2) {
36296
- const hash2 = createHash18("sha256");
37096
+ const hash2 = createHash19("sha256");
36297
37097
  const fd = openSync4(path2, "r");
36298
37098
  const buffer = Buffer.allocUnsafe(CHECKSUM_BUFFER_BYTES);
36299
37099
  try {
@@ -36311,7 +37111,7 @@ function writeChecksums(rootDir) {
36311
37111
  const lines = [];
36312
37112
  const walk = (dir) => {
36313
37113
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
36314
- const path2 = join15(dir, entry.name);
37114
+ const path2 = join16(dir, entry.name);
36315
37115
  if (entry.isDirectory()) {
36316
37116
  walk(path2);
36317
37117
  continue;
@@ -36322,11 +37122,11 @@ function writeChecksums(rootDir) {
36322
37122
  }
36323
37123
  };
36324
37124
  walk(rootDir);
36325
- writeFileSync8(join15(rootDir, "checksums.sha256"), `${lines.sort().join("\n")}
37125
+ writeFileSync8(join16(rootDir, "checksums.sha256"), `${lines.sort().join("\n")}
36326
37126
  `);
36327
37127
  }
36328
37128
  function verifyChecksums(rootDir) {
36329
- const checksumsPath = join15(rootDir, "checksums.sha256");
37129
+ const checksumsPath = join16(rootDir, "checksums.sha256");
36330
37130
  if (!existsSync13(checksumsPath)) throw new Error("Backup archive is missing checksums.sha256.");
36331
37131
  for (const line of readFileSync14(checksumsPath, "utf-8").split("\n")) {
36332
37132
  const trimmed = line.trim();
@@ -36337,7 +37137,7 @@ function verifyChecksums(rootDir) {
36337
37137
  if (relative12.startsWith("/") || relative12.split(/[\\/]/).includes("..")) {
36338
37138
  throw new Error(`Unsafe checksum path in backup archive: ${relative12}`);
36339
37139
  }
36340
- const filePath = join15(rootDir, relative12);
37140
+ const filePath = join16(rootDir, relative12);
36341
37141
  if (!existsSync13(filePath)) throw new Error(`Backup archive is missing checksummed file: ${relative12}`);
36342
37142
  const actual = hashFile(filePath);
36343
37143
  if (actual !== match[1]) throw new Error(`Backup checksum mismatch for ${relative12}.`);
@@ -36347,27 +37147,27 @@ function createServerBackup(installPath, role, options = {}) {
36347
37147
  const plan = planBackup({ role, includeObservability: options.includeObservability });
36348
37148
  const backupDir = roleBackupDir(installPath, role);
36349
37149
  mkdirSync13(backupDir, { recursive: true, mode: 448 });
36350
- const archivePath = options.output ? resolve7(options.output) : join15(backupDir, options.preUpdate ? `latest-pre-update-${role}.tar.gz` : `openmates-${role}-${nowStamp()}.tar.gz`);
37150
+ const archivePath = options.output ? resolve7(options.output) : join16(backupDir, options.preUpdate ? `latest-pre-update-${role}.tar.gz` : `openmates-${role}-${nowStamp()}.tar.gz`);
36351
37151
  mkdirSync13(dirname8(archivePath), { recursive: true, mode: 448 });
36352
- const tempDir = mkdtempSync2(join15(backupDir, ".tmp-"));
37152
+ const tempDir = mkdtempSync2(join16(backupDir, ".tmp-"));
36353
37153
  const env = readEnvMap(installPath);
36354
37154
  const manifest = {
36355
37155
  role,
36356
37156
  backup_format_version: BACKUP_FORMAT_VERSION,
36357
37157
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
36358
37158
  cli_version: getPackageVersion(),
36359
- image_tag: getEnvVar(existsSync13(join15(installPath, ".env")) ? readFileSync14(join15(installPath, ".env"), "utf-8") : "", "OPENMATES_IMAGE_TAG"),
37159
+ image_tag: getEnvVar(existsSync13(join16(installPath, ".env")) ? readFileSync14(join16(installPath, ".env"), "utf-8") : "", "OPENMATES_IMAGE_TAG"),
36360
37160
  recovery_scope: role === "core" ? "database-and-runtime-only" : "runtime-only",
36361
37161
  include_observability: false,
36362
- contents: plan.contents.filter((content) => content !== "runtime-env" || existsSync13(join15(installPath, ".env"))).filter((content) => content !== "runtime-config" || existsSync13(join15(installPath, "config")))
37162
+ contents: plan.contents.filter((content) => content !== "runtime-env" || existsSync13(join16(installPath, ".env"))).filter((content) => content !== "runtime-config" || existsSync13(join16(installPath, "config")))
36363
37163
  };
36364
37164
  try {
36365
- copyIfExists(join15(installPath, ".env"), join15(tempDir, "runtime", ".env"));
36366
- copyIfExists(join15(installPath, "config"), join15(tempDir, "runtime", "config"));
37165
+ copyIfExists(join16(installPath, ".env"), join16(tempDir, "runtime", ".env"));
37166
+ copyIfExists(join16(installPath, "config"), join16(tempDir, "runtime", "config"));
36367
37167
  if (role === "core") {
36368
37168
  const databaseUser = env.DATABASE_USERNAME || "directus";
36369
37169
  const databaseName = env.DATABASE_NAME || "directus";
36370
- const dumpFile = openSync4(join15(tempDir, "postgres.sql"), "w");
37170
+ const dumpFile = openSync4(join16(tempDir, "postgres.sql"), "w");
36371
37171
  try {
36372
37172
  const result = spawnSync(
36373
37173
  "docker",
@@ -36382,7 +37182,7 @@ function createServerBackup(installPath, role, options = {}) {
36382
37182
  closeSync4(dumpFile);
36383
37183
  }
36384
37184
  }
36385
- writeFileSync8(join15(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
37185
+ writeFileSync8(join16(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
36386
37186
  `, { mode: 384 });
36387
37187
  writeChecksums(tempDir);
36388
37188
  publishServerBackupArchive(tempDir, archivePath);
@@ -36393,7 +37193,7 @@ function createServerBackup(installPath, role, options = {}) {
36393
37193
  }
36394
37194
  function validateExtractedServerBackup(tempDir, role) {
36395
37195
  verifyChecksums(tempDir);
36396
- const manifestPath = join15(tempDir, "manifest.json");
37196
+ const manifestPath = join16(tempDir, "manifest.json");
36397
37197
  if (!existsSync13(manifestPath)) throw new Error("Backup archive is missing manifest.json.");
36398
37198
  const manifest = JSON.parse(readFileSync14(manifestPath, "utf-8"));
36399
37199
  if (manifest.role !== role) throw new Error(`Backup role '${manifest.role}' does not match requested role '${role}'.`);
@@ -36405,7 +37205,7 @@ function withValidatedServerBackup(installPath, role, file, useBackup, tempRoot
36405
37205
  const archivePath = resolve7(file);
36406
37206
  if (!existsSync13(archivePath)) throw new Error(`Backup file not found: ${archivePath}`);
36407
37207
  mkdirSync13(tempRoot, { recursive: true, mode: 448 });
36408
- const tempDir = mkdtempSync2(join15(tempRoot, ".restore-"));
37208
+ const tempDir = mkdtempSync2(join16(tempRoot, ".restore-"));
36409
37209
  try {
36410
37210
  execSync(`tar -xzf ${shellQuote(archivePath)} -C ${shellQuote(tempDir)}`, { stdio: "pipe" });
36411
37211
  validateExtractedServerBackup(tempDir, role);
@@ -36417,9 +37217,9 @@ function withValidatedServerBackup(installPath, role, file, useBackup, tempRoot
36417
37217
  function restoreServerBackup(installPath, role, file) {
36418
37218
  withValidatedServerBackup(installPath, role, file, (tempDir) => {
36419
37219
  const env = readEnvMap(installPath);
36420
- copyIfExists(join15(tempDir, "runtime", ".env"), join15(installPath, ".env"));
36421
- copyIfExists(join15(tempDir, "runtime", "config"), join15(installPath, "config"));
36422
- const postgresDump = join15(tempDir, "postgres.sql");
37220
+ copyIfExists(join16(tempDir, "runtime", ".env"), join16(installPath, ".env"));
37221
+ copyIfExists(join16(tempDir, "runtime", "config"), join16(installPath, "config"));
37222
+ const postgresDump = join16(tempDir, "postgres.sql");
36423
37223
  if (role === "core" && existsSync13(postgresDump)) {
36424
37224
  const databaseUser = env.DATABASE_USERNAME || "directus";
36425
37225
  const databaseName = env.DATABASE_NAME || "directus";
@@ -36706,9 +37506,9 @@ async function serverRegister(flags) {
36706
37506
  const installPath = resolveServerPath(
36707
37507
  typeof flags.path === "string" ? flags : { ...flags, path: process.cwd() }
36708
37508
  );
36709
- const envPath = join15(installPath, ".env");
36710
- if (!existsSync13(join15(installPath, SOURCE_COMPOSE_FILE))) {
36711
- throw new Error(`Source Compose file not found: ${join15(installPath, SOURCE_COMPOSE_FILE)}`);
37509
+ const envPath = join16(installPath, ".env");
37510
+ if (!existsSync13(join16(installPath, SOURCE_COMPOSE_FILE))) {
37511
+ throw new Error(`Source Compose file not found: ${join16(installPath, SOURCE_COMPOSE_FILE)}`);
36712
37512
  }
36713
37513
  if (!existsSync13(envPath)) {
36714
37514
  throw new Error(`Runtime env file not found: ${envPath}`);
@@ -36784,7 +37584,7 @@ async function serverInstall(flags) {
36784
37584
  withAlerts: flags["with-alerts"] === true,
36785
37585
  includeWebapp: deploymentMode !== "official_cloud"
36786
37586
  });
36787
- if (existsSync13(join15(installPath, SOURCE_COMPOSE_FILE)) || Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync13(join15(installPath, composeFile)))) {
37587
+ if (existsSync13(join16(installPath, SOURCE_COMPOSE_FILE)) || Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync13(join16(installPath, composeFile)))) {
36788
37588
  console.error(`OpenMates already exists at ${installPath}.`);
36789
37589
  console.error("Use 'openmates server update' to update, or choose a different --path.");
36790
37590
  process.exit(1);
@@ -36798,7 +37598,7 @@ async function serverInstall(flags) {
36798
37598
  if (!existsSync13(envSource)) {
36799
37599
  throw new Error(`Env file not found: ${envSource}`);
36800
37600
  }
36801
- copyFileSync2(envSource, join15(installPath, ".env"));
37601
+ copyFileSync2(envSource, join16(installPath, ".env"));
36802
37602
  console.error(`Copied ${envSource} to ${installPath}/.env`);
36803
37603
  }
36804
37604
  const requestedImageTag = typeof flags["image-tag"] === "string" ? flags["image-tag"] : getDefaultImageTag();
@@ -36806,7 +37606,7 @@ async function serverInstall(flags) {
36806
37606
  console.error(`Preparing OpenMates image-mode install at ${installPath}...`);
36807
37607
  await writeImageModeRuntimeFiles(installPath, imageTag, role);
36808
37608
  const overlay2 = writeDeploymentModeEnv(installPath, deploymentMode, requestedOverlayPath);
36809
- const cliUrls2 = deriveSelfHostCliUrls(readFileSync14(join15(installPath, ".env"), "utf-8"));
37609
+ const cliUrls2 = deriveSelfHostCliUrls(readFileSync14(join16(installPath, ".env"), "utf-8"));
36810
37610
  try {
36811
37611
  exec("docker network create openmates", installPath);
36812
37612
  } catch {
@@ -36834,7 +37634,7 @@ async function serverInstall(flags) {
36834
37634
  if (flags.json === true) {
36835
37635
  printJson({ command: "install", status: "success", path: installPath, mode: "image", imageTag });
36836
37636
  } else {
36837
- const firstInvite = getEnvVar(readFileSync14(join15(installPath, ".env"), "utf-8"), "SELF_HOST_FIRST_INVITE_CODE");
37637
+ const firstInvite = getEnvVar(readFileSync14(join16(installPath, ".env"), "utf-8"), "SELF_HOST_FIRST_INVITE_CODE");
36838
37638
  console.log(`
36839
37639
  OpenMates installed at ${installPath}`);
36840
37640
  console.log(`Mode: image (${DEFAULT_IMAGE_REGISTRY}, tag ${imageTag})`);
@@ -36876,7 +37676,7 @@ CLI default API: ${cliUrls2.apiUrl}`);
36876
37676
  if (!existsSync13(envSource)) {
36877
37677
  throw new Error(`Env file not found: ${envSource}`);
36878
37678
  }
36879
- copyFileSync2(envSource, join15(installPath, ".env"));
37679
+ copyFileSync2(envSource, join16(installPath, ".env"));
36880
37680
  console.error(`Copied ${envSource} to ${installPath}/.env`);
36881
37681
  }
36882
37682
  const translationResult = ensureSourceInstallTranslations(installPath, sourcePath);
@@ -36895,7 +37695,7 @@ CLI default API: ${cliUrls2.apiUrl}`);
36895
37695
  console.error("Setup script failed. Check the output above for details.");
36896
37696
  process.exit(setupCode);
36897
37697
  }
36898
- const envPath = join15(installPath, ".env");
37698
+ const envPath = join16(installPath, ".env");
36899
37699
  const overlay = writeDeploymentModeEnv(installPath, deploymentMode, requestedOverlayPath);
36900
37700
  const cliUrls = deriveSelfHostCliUrls(existsSync13(envPath) ? readFileSync14(envPath, "utf-8") : "");
36901
37701
  saveServerConfig({
@@ -36938,8 +37738,8 @@ async function installContinuousUpdateService(flags) {
36938
37738
  const channel = typeof flags.channel === "string" ? flags.channel : config?.imageChannel ?? "stable";
36939
37739
  const window = typeof flags.window === "string" ? flags.window : "02:00-04:00 UTC";
36940
37740
  const plan = planContinuousUpdateService({ role, channel, window });
36941
- const servicePath = join15("/etc", "systemd", "system", plan.serviceName);
36942
- const timerPath = join15("/etc", "systemd", "system", plan.timerName);
37741
+ const servicePath = join16("/etc", "systemd", "system", plan.serviceName);
37742
+ const timerPath = join16("/etc", "systemd", "system", plan.timerName);
36943
37743
  if (flags["dry-run"] === true || flags.json === true) {
36944
37744
  printJson({ command: "update install-service", status: "planned", role, servicePath, timerPath, unit: plan.unit, timer: plan.timer });
36945
37745
  return;
@@ -36979,12 +37779,12 @@ function inspectCmsCacheConsistency(installPath, withOverrides, installMode) {
36979
37779
  });
36980
37780
  }
36981
37781
  function runRuntimeVerification(installPath, role, config) {
36982
- const envText = existsSync13(join15(installPath, ".env")) ? readFileSync14(join15(installPath, ".env"), "utf-8") : "";
37782
+ const envText = existsSync13(join16(installPath, ".env")) ? readFileSync14(join16(installPath, ".env"), "utf-8") : "";
36983
37783
  const mode = resolveRuntimeDeploymentMode({
36984
37784
  envText,
36985
37785
  overlayExists: existsSync13(config?.openMatesCloudOverlayPath ?? defaultOpenMatesCloudOverlayPath(installPath))
36986
37786
  });
36987
- const backupPath = join15(roleBackupDir(installPath, role), `latest-pre-update-${role}.tar.gz`);
37787
+ const backupPath = join16(roleBackupDir(installPath, role), `latest-pre-update-${role}.tar.gz`);
36988
37788
  const plan = planRuntimeVerification({ role, deploymentMode: mode.effectiveMode, hasVerifiedBackup: existsSync13(backupPath) });
36989
37789
  const verifierService = { core: "api", upload: "app-uploads", preview: "preview" }[role];
36990
37790
  const installMode = getInstallMode(installPath, config);
@@ -37178,10 +37978,10 @@ async function installRuntimeMonitoringServices(installPath, role) {
37178
37978
  [operationalPlan.watchdogTimerName, operationalPlan.watchdogTimer]
37179
37979
  ] : []
37180
37980
  ].filter((entry) => Boolean(entry[0] && entry[1]));
37181
- const reportStatePath = join15(installPath, ".openmates", "runtime-health", `${role}-operational-report.json`);
37182
- const reportMetricsDir = join15(installPath, ".openmates", "runtime-health", "metrics");
37183
- const startedMetricPath = join15(reportMetricsDir, "operational-report-started.prom");
37184
- const successMetricPath = join15(reportMetricsDir, "operational-report.prom");
37981
+ const reportStatePath = join16(installPath, ".openmates", "runtime-health", `${role}-operational-report.json`);
37982
+ const reportMetricsDir = join16(installPath, ".openmates", "runtime-health", "metrics");
37983
+ const startedMetricPath = join16(reportMetricsDir, "operational-report-started.prom");
37984
+ const successMetricPath = join16(reportMetricsDir, "operational-report.prom");
37185
37985
  const operationalUnitNames = [
37186
37986
  operationalPlan.digestServiceName,
37187
37987
  operationalPlan.digestTimerName,
@@ -37208,17 +38008,17 @@ async function installRuntimeMonitoringServices(installPath, role) {
37208
38008
  );
37209
38009
  }
37210
38010
  } else {
37211
- const installedOperationalUnits = operationalUnitNames.filter((name) => existsSync13(join15("/etc", "systemd", "system", name)));
38011
+ const installedOperationalUnits = operationalUnitNames.filter((name) => existsSync13(join16("/etc", "systemd", "system", name)));
37212
38012
  const installedOperationalTimers = installedOperationalUnits.filter((name) => name.endsWith(".timer"));
37213
38013
  if (installedOperationalTimers.length) {
37214
38014
  execFileSync5("systemctl", ["disable", "--now", ...installedOperationalTimers], { stdio: "pipe" });
37215
38015
  }
37216
- for (const name of installedOperationalUnits) rmSync8(join15("/etc", "systemd", "system", name));
38016
+ for (const name of installedOperationalUnits) rmSync8(join16("/etc", "systemd", "system", name));
37217
38017
  for (const path2 of [reportStatePath, startedMetricPath, successMetricPath]) {
37218
38018
  if (existsSync13(path2)) rmSync8(path2);
37219
38019
  }
37220
38020
  }
37221
- for (const [name, content] of files) writeFileSync8(join15("/etc", "systemd", "system", name), content, { mode: 420 });
38021
+ for (const [name, content] of files) writeFileSync8(join16("/etc", "systemd", "system", name), content, { mode: 420 });
37222
38022
  execSync("systemctl daemon-reload", { stdio: "pipe" });
37223
38023
  const timers = files.filter(([name]) => name.endsWith(".timer")).map(([name]) => name);
37224
38024
  execSync(`systemctl enable --now ${timers.map(shellQuote).join(" ")}`, { stdio: "pipe" });
@@ -37237,7 +38037,7 @@ function caddyUpdatePlan(installPath, role, config, flags) {
37237
38037
  const configPath = resolve7(typeof flags["caddy-config"] === "string" ? flags["caddy-config"] : "/etc/caddy/Caddyfile");
37238
38038
  const required = role === "core" && getInstallDeploymentMode(installPath, config) === "official_cloud";
37239
38039
  if (!existsSync13(configPath) && required) throw new Error("Managed cloud Caddyfile is missing; update cannot proceed.");
37240
- const prior = role === "upload" ? readCaddyUpdateState(join15(installPath, ".openmates", "caddy", `${role}.json`), configPath, null) : void 0;
38040
+ const prior = role === "upload" ? readCaddyUpdateState(join16(installPath, ".openmates", "caddy", `${role}.json`), configPath, null) : void 0;
37241
38041
  const profile = resolveCaddyProfile(role, flags["caddy-profile"], prior ? prior.profile ?? "self-host" : void 0);
37242
38042
  if (profile === "official-upload" && !existsSync13(configPath)) throw new Error("caddy_official_upload_host_missing");
37243
38043
  if (role === "upload" && existsSync13(configPath)) {
@@ -37268,7 +38068,7 @@ async function updateServerCaddy(input) {
37268
38068
  if (!revision || !/^[a-f0-9]{40}$/i.test(revision)) throw new Error("caddy_release_revision_invalid");
37269
38069
  let target;
37270
38070
  if (input.mode === "source") {
37271
- target = readFileSync14(join15(input.installPath, templatePath), "utf8");
38071
+ target = readFileSync14(join16(input.installPath, templatePath), "utf8");
37272
38072
  } else {
37273
38073
  const response = await fetch(`https://raw.githubusercontent.com/glowingkitty/OpenMates/${revision}/${templatePath}`, { signal: AbortSignal.timeout(15e3), redirect: "error" });
37274
38074
  if (!response.ok) throw new Error("caddy_release_template_unavailable");
@@ -37599,7 +38399,7 @@ async function serverUpdate(client2, rest, flags) {
37599
38399
  try {
37600
38400
  await autoInstallRuntimeMonitoringServices(installPath, role);
37601
38401
  } catch (error) {
37602
- const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${join15(roleBackupDir(installPath, role), safetyPlan.backupName)}` : null;
38402
+ const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${join16(roleBackupDir(installPath, role), safetyPlan.backupName)}` : null;
37603
38403
  writeServerUpdateStatus(installPath, role, {
37604
38404
  status: "degraded",
37605
38405
  targetImageTag: target.tag,
@@ -37962,11 +38762,11 @@ async function serverMonitoring(rest, flags) {
37962
38762
  const state2 = await readOperationalReportState(installPath, role);
37963
38763
  const nextState2 = accepted ? { ...state2, incidentOpen: false, incidentOpenedAt: void 0, lastAcceptedReportAt: (/* @__PURE__ */ new Date()).toISOString() } : state2;
37964
38764
  await writeOperationalReportState(installPath, role, nextState2);
37965
- const metricsDir = join15(installPath, ".openmates", "runtime-health", "metrics");
38765
+ const metricsDir = join16(installPath, ".openmates", "runtime-health", "metrics");
37966
38766
  mkdirSync13(metricsDir, { recursive: true, mode: 448 });
37967
38767
  if (accepted) {
37968
38768
  writeFileSync8(
37969
- join15(metricsDir, "operational-report.prom"),
38769
+ join16(metricsDir, "operational-report.prom"),
37970
38770
  `operational_report_last_success_timestamp_seconds{environment="${output2.receipts[0]?.environment ?? environment}"} ${Math.floor(Date.now() / 1e3)}
37971
38771
  `,
37972
38772
  { mode: 384 }
@@ -37984,11 +38784,11 @@ async function serverMonitoring(rest, flags) {
37984
38784
  destinationSource: receipt.destination_source,
37985
38785
  fallbackUsed: receipt.fallback_used
37986
38786
  }));
37987
- const receiptDir = join15(installPath, ".openmates", "runtime-health", "receipts");
38787
+ const receiptDir = join16(installPath, ".openmates", "runtime-health", "receipts");
37988
38788
  mkdirSync13(receiptDir, { recursive: true, mode: 448 });
37989
38789
  const receiptMonth = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
37990
38790
  for (const receipt of redactedReceipts) {
37991
- appendFileSync(join15(receiptDir, `${receiptMonth}.jsonl`), `${JSON.stringify(receipt)}
38791
+ appendFileSync(join16(receiptDir, `${receiptMonth}.jsonl`), `${JSON.stringify(receipt)}
37992
38792
  `, { mode: 384 });
37993
38793
  }
37994
38794
  const displayed = { command: "monitoring digest", reportId: output2.report_id, reportSha256: output2.report_sha256, deliveryState: output2.delivery_state, receipts: redactedReceipts };
@@ -38193,7 +38993,7 @@ async function serverFeatures(rest, flags) {
38193
38993
  const installPath = resolveServerPath(flags);
38194
38994
  const installMode = getInstallMode(installPath);
38195
38995
  if (installMode === "image") ensureImageRuntimeConfig(installPath);
38196
- const configPath = installMode === "image" ? imageBackendConfigPath(installPath) : join15(installPath, BACKEND_CONFIG_FILE);
38996
+ const configPath = installMode === "image" ? imageBackendConfigPath(installPath) : join16(installPath, BACKEND_CONFIG_FILE);
38197
38997
  if (!existsSync13(configPath)) {
38198
38998
  throw new Error(`Backend config not found at ${configPath}. Run 'openmates server install' first or pass --path <dir>.`);
38199
38999
  }
@@ -38443,7 +39243,7 @@ async function serverBackup(rest, flags) {
38443
39243
  console.log(" none");
38444
39244
  return;
38445
39245
  }
38446
- for (const file of files) console.log(` ${join15(dir, file)}`);
39246
+ for (const file of files) console.log(` ${join16(dir, file)}`);
38447
39247
  return;
38448
39248
  }
38449
39249
  requireDocker();
@@ -38990,7 +39790,7 @@ async function handleServer(client2, subcommand, rest, flags) {
38990
39790
  }
38991
39791
 
38992
39792
  // src/codeRunInput.ts
38993
- import { readdir, readFile, stat as stat2 } from "fs/promises";
39793
+ import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
38994
39794
  import { basename as basename3, relative as relative2, resolve as resolve8 } from "path";
38995
39795
  var CODE_RUN_MAX_FILES = 50;
38996
39796
  var CODE_RUN_MAX_FILE_BYTES = 1e6;
@@ -39036,7 +39836,7 @@ async function buildCodeRunRequestsFromFlags(flags) {
39036
39836
  }
39037
39837
  for (const filePath of toList(flags.file)) {
39038
39838
  const absolute = resolve8(filePath);
39039
- const buffer = await readFile(absolute);
39839
+ const buffer = await readFile2(absolute);
39040
39840
  const sandboxPath = normalizeCodeRunPath(basename3(filePath));
39041
39841
  addFile({
39042
39842
  path: sandboxPath,
@@ -39051,7 +39851,7 @@ async function buildCodeRunRequestsFromFlags(flags) {
39051
39851
  const dirFiles = await collectDirectoryFiles(root, flags.include, flags.exclude);
39052
39852
  for (const absolute of dirFiles) {
39053
39853
  const sandboxPath = normalizeCodeRunPath(relative2(root, absolute).replace(/\\/g, "/"));
39054
- const buffer = await readFile(absolute);
39854
+ const buffer = await readFile2(absolute);
39055
39855
  addFile({
39056
39856
  path: sandboxPath,
39057
39857
  content_base64: buffer.toString("base64"),
@@ -60141,6 +60941,41 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
60141
60941
  }
60142
60942
  },
60143
60943
  chat: {
60944
+ draft_embed_labels: {
60945
+ image: {
60946
+ text: "Image"
60947
+ },
60948
+ audio: {
60949
+ text: "Audio"
60950
+ },
60951
+ recording: {
60952
+ text: "Recording"
60953
+ },
60954
+ website: {
60955
+ text: "Website"
60956
+ },
60957
+ video: {
60958
+ text: "Video"
60959
+ },
60960
+ location: {
60961
+ text: "Location"
60962
+ },
60963
+ pdf: {
60964
+ text: "PDF"
60965
+ },
60966
+ file: {
60967
+ text: "File"
60968
+ },
60969
+ book: {
60970
+ text: "Book"
60971
+ },
60972
+ code: {
60973
+ text: "Code"
60974
+ },
60975
+ embed: {
60976
+ text: "Embed"
60977
+ }
60978
+ },
60144
60979
  generated_by_cost: {
60145
60980
  text: "Cost: {credits} credits"
60146
60981
  },
@@ -74986,6 +75821,155 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
74986
75821
  }
74987
75822
  }
74988
75823
  },
75824
+ local_models: {
75825
+ title: {
75826
+ text: "Local models lab"
75827
+ },
75828
+ description: {
75829
+ text: "Optional on-device experiments. Download models once, then test without a network connection. No credits are charged."
75830
+ },
75831
+ toggle: {
75832
+ text: "Use local models in this lab"
75833
+ },
75834
+ scope: {
75835
+ text: "This switch enables only the tests below. Chat speech recognition, speech playback and personal-data protection keep their current providers and settings."
75836
+ },
75837
+ download: {
75838
+ text: "Download"
75839
+ },
75840
+ not_downloaded: {
75841
+ text: "Not downloaded"
75842
+ },
75843
+ ready: {
75844
+ text: "Ready for offline tests"
75845
+ },
75846
+ downloading: {
75847
+ text: "Downloading {percent}%"
75848
+ },
75849
+ download_failed: {
75850
+ text: "Download failed. Retry when your connection and free storage are available."
75851
+ },
75852
+ run: {
75853
+ text: "Run local test"
75854
+ },
75855
+ import_audio: {
75856
+ text: "Import audio file"
75857
+ },
75858
+ record: {
75859
+ text: "Record microphone"
75860
+ },
75861
+ stop_recording: {
75862
+ text: "Stop recording"
75863
+ },
75864
+ audio_ready: {
75865
+ text: "Audio ready: {seconds} seconds"
75866
+ },
75867
+ speech_input: {
75868
+ text: "English text to speak"
75869
+ },
75870
+ privacy_input: {
75871
+ text: "Text to inspect for personal data"
75872
+ },
75873
+ input_placeholder: {
75874
+ text: "Enter disposable test text"
75875
+ },
75876
+ play: {
75877
+ text: "Play generated speech"
75878
+ },
75879
+ stop_playback: {
75880
+ text: "Stop playback"
75881
+ },
75882
+ running: {
75883
+ text: "Running locally\u2026"
75884
+ },
75885
+ cancelling: {
75886
+ text: "Cancelling and unloading\u2026"
75887
+ },
75888
+ elapsed: {
75889
+ text: "Elapsed time"
75890
+ },
75891
+ rtf: {
75892
+ text: "Real-time factor"
75893
+ },
75894
+ environment: {
75895
+ text: "{cores} CPU cores \xB7 {memory} device memory \xB7 thermal state: {thermal}"
75896
+ },
75897
+ thermal_nominal: {
75898
+ text: "normal"
75899
+ },
75900
+ thermal_fair: {
75901
+ text: "warm"
75902
+ },
75903
+ thermal_serious: {
75904
+ text: "hot"
75905
+ },
75906
+ thermal_critical: {
75907
+ text: "critical"
75908
+ },
75909
+ unavailable: {
75910
+ text: "Unavailable"
75911
+ },
75912
+ result: {
75913
+ text: "Local test result"
75914
+ },
75915
+ no_entities: {
75916
+ text: "No entities detected. This experimental result does not prove the text is safe."
75917
+ },
75918
+ audio_error: {
75919
+ text: "The audio file could not be read, recorded or played. Choose a supported audio file and try again."
75920
+ },
75921
+ microphone_error: {
75922
+ text: "Microphone access is required to record a test. You can also import an audio file."
75923
+ },
75924
+ run_error: {
75925
+ text: "The local test failed. Check that the model download is complete and this device supports its runtime. No cloud fallback was used."
75926
+ },
75927
+ privacy_note: {
75928
+ text: "Inputs, recordings and generated output are temporary and cleared when you leave this page. Installed models remain until removed. Run one model at a time; cancellation waits for the active native operation to finish."
75929
+ },
75930
+ whisper: {
75931
+ text: "WhisperKit large-v3 turbo \xB7 speech to text"
75932
+ },
75933
+ kokoro: {
75934
+ text: "Kokoro \xB7 English text to speech"
75935
+ },
75936
+ privacy_filter: {
75937
+ text: "OpenAI Privacy Filter \xB7 personal data"
75938
+ },
75939
+ entity: {
75940
+ text: "{label} \xB7 {start}\u2013{end} \xB7 confidence {score}"
75941
+ },
75942
+ installed_size: {
75943
+ text: "Download size"
75944
+ },
75945
+ revision: {
75946
+ text: "Revision"
75947
+ },
75948
+ waveform: {
75949
+ text: "Generated audio waveform"
75950
+ },
75951
+ cancelled: {
75952
+ text: "Download cancelled"
75953
+ },
75954
+ not_enough_space: {
75955
+ text: "Not enough free storage for this model"
75956
+ },
75957
+ kokoro_os_error: {
75958
+ text: "Kokoro is currently unavailable on iOS and macOS 27 or later because its upstream runtime can crash. Try another model or an earlier supported OS."
75959
+ },
75960
+ peak_memory: {
75961
+ text: "App peak resident memory"
75962
+ },
75963
+ verifying: {
75964
+ text: "Verifying installed files locally\u2026"
75965
+ },
75966
+ kokoro_input_limits: {
75967
+ text: "Use short, simple English text. Spell out decimals, currency amounts and times in words. Unsupported formats and foreign characters are rejected. This lab does not include the full upstream text normalization pipeline."
75968
+ },
75969
+ architecture_unavailable: {
75970
+ text: "These local models require an iPhone, iPad or Apple silicon Mac. Intel Macs and Apple Watch are unsupported; downloads and tests are disabled."
75971
+ }
75972
+ },
74989
75973
  open_settings_menu: {
74990
75974
  text: "Open settings menu"
74991
75975
  },
@@ -81400,10 +82384,10 @@ function buildAssistantFeedbackDecision(rating) {
81400
82384
  }
81401
82385
 
81402
82386
  // src/benchmark.ts
81403
- import { createHash as createHash19, randomUUID as randomUUID16 } from "crypto";
82387
+ import { createHash as createHash20, randomUUID as randomUUID16 } from "crypto";
81404
82388
  import { existsSync as existsSync14, mkdtempSync as mkdtempSync3, readFileSync as readFileSync15, readdirSync as readdirSync7, writeFileSync as writeFileSync9 } from "fs";
81405
82389
  import { tmpdir as tmpdir3 } from "os";
81406
- import { dirname as dirname9, join as join16, resolve as resolve9 } from "path";
82390
+ import { dirname as dirname9, join as join17, resolve as resolve9 } from "path";
81407
82391
  import { fileURLToPath } from "url";
81408
82392
  var DEFAULT_JUDGE_MODEL = "google/gemini-3-flash-preview";
81409
82393
  var DEFAULT_PROMPT_BUDGET_MODEL = "google/gemini-3.5-flash";
@@ -82571,7 +83555,7 @@ function loadProviderPricing() {
82571
83555
  if (!providersDir) return pricing;
82572
83556
  for (const fileName of readdirSync7(providersDir)) {
82573
83557
  if (!fileName.endsWith(".yml")) continue;
82574
- const filePath = join16(providersDir, fileName);
83558
+ const filePath = join17(providersDir, fileName);
82575
83559
  const text4 = readFileSync15(filePath, "utf-8");
82576
83560
  const provider = parseProviderId(text4) ?? fileName.replace(/\.yml$/, "");
82577
83561
  for (const modelPricing of parseModelPricing(text4, provider)) {
@@ -82626,9 +83610,9 @@ function findProvidersDir() {
82626
83610
  const currentFile = fileURLToPath(import.meta.url);
82627
83611
  let current = dirname9(currentFile);
82628
83612
  for (let index = 0; index < 8; index += 1) {
82629
- const candidate = join16(current, "backend", "providers");
83613
+ const candidate = join17(current, "backend", "providers");
82630
83614
  if (existsSync14(candidate)) return candidate;
82631
- const parentCandidate = join16(current, "..", "..", "backend", "providers");
83615
+ const parentCandidate = join17(current, "..", "..", "backend", "providers");
82632
83616
  if (existsSync14(parentCandidate)) return resolve9(parentCandidate);
82633
83617
  const next = dirname9(current);
82634
83618
  if (next === current) break;
@@ -82834,14 +83818,14 @@ function hashPromptBudgetCorpus(cases) {
82834
83818
  return stableHash(cases.map((benchmarkCase) => ({ id: benchmarkCase.id, turns: benchmarkCase.turns })));
82835
83819
  }
82836
83820
  function stableHash(value) {
82837
- return createHash19("sha256").update(JSON.stringify(value)).digest("hex");
83821
+ return createHash20("sha256").update(JSON.stringify(value)).digest("hex");
82838
83822
  }
82839
83823
  function defaultImageFixturePath() {
82840
- const fixtureDir = join16(dirname9(fileURLToPath(import.meta.url)), "..", "fixtures");
82841
- const fixturePath = join16(fixtureDir, "brandenburger-tor.png");
83824
+ const fixtureDir = join17(dirname9(fileURLToPath(import.meta.url)), "..", "fixtures");
83825
+ const fixturePath = join17(fixtureDir, "brandenburger-tor.png");
82842
83826
  if (existsSync14(fixturePath)) return fixturePath;
82843
- const tempDir = mkdtempSync3(join16(tmpdir3(), "openmates-benchmark-"));
82844
- const tempPath = join16(tempDir, "brandenburger-tor.svg");
83827
+ const tempDir = mkdtempSync3(join17(tmpdir3(), "openmates-benchmark-"));
83828
+ const tempPath = join17(tempDir, "brandenburger-tor.svg");
82845
83829
  writeFileSync9(tempPath, FIXTURE_IMAGE_SVG, "utf-8");
82846
83830
  return tempPath;
82847
83831
  }
@@ -83521,7 +84505,7 @@ var APP_GRADIENTS = {
83521
84505
  var PRIMARY_GRADIENT = { "start": "#4867cd", "end": "#5a85eb" };
83522
84506
 
83523
84507
  // src/tuiProjectsWorkspace.ts
83524
- import { createHash as createHash20, randomBytes as randomBytes10, randomUUID as randomUUID17 } from "crypto";
84508
+ import { createHash as createHash21, randomBytes as randomBytes10, randomUUID as randomUUID17 } from "crypto";
83525
84509
 
83526
84510
  // src/tuiForms.ts
83527
84511
  function formValue(form, name) {
@@ -83697,7 +84681,7 @@ async function loadTuiProject(client2, id) {
83697
84681
  client2.listProjectSources(id, context)
83698
84682
  ]);
83699
84683
  const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
83700
- const folderByHash = new Map(folderIds.map((folderId) => [createHash20("sha256").update(folderId).digest("hex"), folderId]));
84684
+ const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
83701
84685
  project.folders = await Promise.all(itemsResponse.folders.filter((folder) => typeof folder.folder_id === "string" && folder.folder_id).map(async (folder) => {
83702
84686
  const parentHash = typeof folder.hashed_parent_folder_id === "string" ? folder.hashed_parent_folder_id : typeof folder.parent_folder_id === "string" && folderByHash.has(folder.parent_folder_id) ? folder.parent_folder_id : void 0;
83703
84687
  return {
@@ -83957,7 +84941,7 @@ async function submitProjectForm(client2, form) {
83957
84941
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
83958
84942
  key_wrappers: wrapping.teamId ? [{
83959
84943
  key_type: "team",
83960
- hashed_team_id: createHash20("sha256").update(wrapping.teamId).digest("hex"),
84944
+ hashed_team_id: createHash21("sha256").update(wrapping.teamId).digest("hex"),
83961
84945
  team_key_epoch: 1,
83962
84946
  encrypted_project_key: await encryptBytesWithAesGcm(key, wrapping.key),
83963
84947
  wrapper_version: 1,
@@ -88020,13 +89004,13 @@ function clamp(value, min, max) {
88020
89004
  }
88021
89005
 
88022
89006
  // src/codexTaskBridge.ts
88023
- import { createHash as createHash21 } from "crypto";
89007
+ import { createHash as createHash22 } from "crypto";
88024
89008
  import { spawn as spawn3 } from "child_process";
88025
89009
  import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
88026
- import { isAbsolute as isAbsolute4, join as join17, resolve as resolve10 } from "path";
89010
+ import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
88027
89011
  import lockfile5 from "proper-lockfile";
88028
89012
  function startCodexTaskBridges(roots, snapshots, stateRoot, notice) {
88029
- const configPath = join17(stateRoot, "codex-adapter.json");
89013
+ const configPath = join18(stateRoot, "codex-adapter.json");
88030
89014
  const children = [];
88031
89015
  if (existsSync15(configPath)) {
88032
89016
  const config = JSON.parse(readFileSync16(configPath, "utf8"));
@@ -88063,13 +89047,13 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
88063
89047
  const session = client2.getSession();
88064
89048
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
88065
89049
  const account = JSON.stringify(scope2);
88066
- const directory = join17(stateRoot, "codex-chat-deletions", createHash21("sha256").update(account).digest("hex"));
89050
+ const directory = join18(stateRoot, "codex-chat-deletions", createHash22("sha256").update(account).digest("hex"));
88067
89051
  if (!existsSync15(directory)) return;
88068
89052
  const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
88069
89053
  try {
88070
89054
  let attempts = 0;
88071
89055
  for (const name of readdirSync8(directory).filter((name2) => /^[a-f0-9]{64}\.json$/.test(name2)).sort()) {
88072
- const path2 = join17(directory, name);
89056
+ const path2 = join18(directory, name);
88073
89057
  const record7 = JSON.parse(readFileSync16(path2, "utf8"));
88074
89058
  if (record7.state !== "pending") continue;
88075
89059
  if (attempts++ >= 3) break;
@@ -88097,18 +89081,18 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
88097
89081
  }
88098
89082
 
88099
89083
  // src/taskCommandDelivery.ts
88100
- import { createHash as createHash22 } from "crypto";
89084
+ import { createHash as createHash23 } from "crypto";
88101
89085
  import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
88102
- import { join as join18 } from "path";
89086
+ import { join as join19 } from "path";
88103
89087
  import lockfile6 from "proper-lockfile";
88104
89088
  async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
88105
89089
  const session = client2.getSession();
88106
89090
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
88107
- const directory = join18(root, "task-command-delivery", createHash22("sha256").update(JSON.stringify(scope2)).digest("hex"));
89091
+ const directory = join19(root, "task-command-delivery", createHash23("sha256").update(JSON.stringify(scope2)).digest("hex"));
88108
89092
  if (!existsSync16(directory)) return;
88109
89093
  const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
88110
89094
  try {
88111
- const records = readdirSync9(directory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name)).map((name) => ({ path: join18(directory, name), command: JSON.parse(readFileSync17(join18(directory, name), "utf8")) })).sort((a, b) => a.command.created_at - b.command.created_at);
89095
+ const records = readdirSync9(directory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name)).map((name) => ({ path: join19(directory, name), command: JSON.parse(readFileSync17(join19(directory, name), "utf8")) })).sort((a, b) => a.command.created_at - b.command.created_at);
88112
89096
  let attempted = 0;
88113
89097
  for (const { path: path2, command } of records) {
88114
89098
  if (command.state !== "pending") continue;
@@ -88214,8 +89198,8 @@ import {
88214
89198
  writeFileSync as writeFileSync13
88215
89199
  } from "fs";
88216
89200
  import { spawn as spawn7 } from "child_process";
88217
- import { createHash as createHash28 } from "crypto";
88218
- import { extname as extname4, join as join25, resolve as resolve16, relative as relative11 } from "path";
89201
+ import { createHash as createHash29 } from "crypto";
89202
+ import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
88219
89203
 
88220
89204
  // src/projectSourceRootPolicy.ts
88221
89205
  import { existsSync as existsSync17, realpathSync as realpathSync2, statSync as statSync2 } from "fs";
@@ -88249,7 +89233,7 @@ function isEqualOrInside(parent, candidate) {
88249
89233
  }
88250
89234
 
88251
89235
  // src/projectPathPolicy.ts
88252
- import { createHash as createHash23, randomBytes as randomBytes12 } from "crypto";
89236
+ import { createHash as createHash24, randomBytes as randomBytes12 } from "crypto";
88253
89237
  import {
88254
89238
  chmodSync as chmodSync9,
88255
89239
  closeSync as closeSync5,
@@ -88269,7 +89253,7 @@ import {
88269
89253
  unlinkSync,
88270
89254
  writeSync
88271
89255
  } from "fs";
88272
- import { dirname as dirname11, isAbsolute as isAbsolute5, join as join19, relative as relative4, resolve as resolve12 } from "path";
89256
+ import { dirname as dirname11, isAbsolute as isAbsolute5, join as join20, relative as relative4, resolve as resolve12 } from "path";
88273
89257
  var DEFAULT_MAX_IGNORE_FILES = 128;
88274
89258
  var DEFAULT_MAX_IGNORE_BYTES = 1024 * 1024;
88275
89259
  var MAX_POLICY_ENTRIES = 5e4;
@@ -88362,7 +89346,7 @@ function loadProjectPathPolicy(sourceRoot, options = {}) {
88362
89346
  function findGitPolicyRoot(sourceRoot) {
88363
89347
  let current = sourceRoot;
88364
89348
  while (true) {
88365
- if (existsSync18(join19(current, ".git"))) return current;
89349
+ if (existsSync18(join20(current, ".git"))) return current;
88366
89350
  const parent = dirname11(current);
88367
89351
  if (parent === current) return sourceRoot;
88368
89352
  current = parent;
@@ -88392,7 +89376,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88392
89376
  if (parent === current || !isInside2(policyRoot, parent)) throw new Error("Source root is outside its Git policy root");
88393
89377
  current = parent;
88394
89378
  }
88395
- for (const directory of chain.reverse()) add(join19(directory, ".gitignore"));
89379
+ for (const directory of chain.reverse()) add(join20(directory, ".gitignore"));
88396
89380
  for (const target of limits.targetPaths) {
88397
89381
  const normalized = target === "." ? "" : normalizeSourceRelativePath(target);
88398
89382
  const absolute = normalized ? resolve12(sourceRoot, normalized) : sourceRoot;
@@ -88406,7 +89390,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88406
89390
  directory = dirname11(directory);
88407
89391
  }
88408
89392
  for (const ancestor of ancestors.reverse()) {
88409
- const ignorePath = join19(ancestor, ".gitignore");
89393
+ const ignorePath = join20(ancestor, ".gitignore");
88410
89394
  if (!files.some((file) => file.path === normalizeRelative(policyRoot, ignorePath))) add(ignorePath);
88411
89395
  }
88412
89396
  }
@@ -88419,7 +89403,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88419
89403
  for (const entry of readdirSync10(directory, { withFileTypes: true })) {
88420
89404
  if (++entries > limits.maxEntries) throw new Error("Project path policy scan exceeds its entry limit");
88421
89405
  if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
88422
- const child = join19(directory, entry.name);
89406
+ const child = join20(directory, entry.name);
88423
89407
  const canonical = realpathSync3(child);
88424
89408
  if (!isInside2(sourceRoot, canonical) || seen.has(canonical)) continue;
88425
89409
  const sourceRelative = normalizeRelative(sourceRoot, child);
@@ -88427,7 +89411,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88427
89411
  if (limits.privatePolicy.isPrivate(sourceRelative, true) || currentPolicy.isIgnored(policyRelative, true)) continue;
88428
89412
  seen.add(canonical);
88429
89413
  const before = files.length;
88430
- add(join19(child, ".gitignore"));
89414
+ add(join20(child, ".gitignore"));
88431
89415
  if (files.length !== before) currentPolicy = createProjectPathPolicy({ ignoreFiles: files, privatePaths: [] });
88432
89416
  directories.push(child);
88433
89417
  }
@@ -88440,10 +89424,10 @@ function updateStickyPrivatePaths(sourceRoot, additions, stateDirectory) {
88440
89424
  throw new Error("Project path policy state must stay outside the source root");
88441
89425
  }
88442
89426
  const key = sha2563(realpathSync3(sourceRoot));
88443
- const path2 = join19(stateRoot, `${key}.json`);
89427
+ const path2 = join20(stateRoot, `${key}.json`);
88444
89428
  if (additions.length === 0 && !existsSync18(path2)) return [];
88445
89429
  preparePrivateDirectory(stateRoot);
88446
- const lock = join19(stateRoot, `${key}.lock`);
89430
+ const lock = join20(stateRoot, `${key}.lock`);
88447
89431
  try {
88448
89432
  mkdirSync14(lock, { mode: 448 });
88449
89433
  } catch (error) {
@@ -88530,7 +89514,7 @@ function isInside2(root, candidate) {
88530
89514
  return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
88531
89515
  }
88532
89516
  function sha2563(value) {
88533
- return createHash23("sha256").update(value).digest("hex");
89517
+ return createHash24("sha256").update(value).digest("hex");
88534
89518
  }
88535
89519
  function stableJson2(value) {
88536
89520
  return JSON.stringify(value, (_key, item) => {
@@ -88540,7 +89524,7 @@ function stableJson2(value) {
88540
89524
  }
88541
89525
 
88542
89526
  // src/remoteFileWrites.ts
88543
- import { createHash as createHash24, randomBytes as randomBytes13 } from "crypto";
89527
+ import { createHash as createHash25, randomBytes as randomBytes13 } from "crypto";
88544
89528
  import {
88545
89529
  chmodSync as chmodSync10,
88546
89530
  closeSync as closeSync7,
@@ -88560,7 +89544,7 @@ import {
88560
89544
  writeFileSync as writeFileSync10
88561
89545
  } from "fs";
88562
89546
  import { tmpdir as tmpdir5 } from "os";
88563
- import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute6, join as join21, relative as relative5, resolve as resolve13 } from "path";
89547
+ import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute6, join as join22, relative as relative5, resolve as resolve13 } from "path";
88564
89548
  import lockfile7 from "proper-lockfile";
88565
89549
 
88566
89550
  // src/projectFileRisk.ts
@@ -88632,7 +89616,7 @@ function matchesAdditionalPrivatePattern(path2, patterns) {
88632
89616
  // src/remoteCommandSourceLock.ts
88633
89617
  import { closeSync as closeSync6, constants as constants4, lstatSync as lstatSync5, mkdirSync as mkdirSync15, openSync as openSync6, realpathSync as realpathSync4, statSync as statSync4 } from "fs";
88634
89618
  import { tmpdir as tmpdir4 } from "os";
88635
- import { join as join20 } from "path";
89619
+ import { join as join21 } from "path";
88636
89620
  import { spawn as spawn4, spawnSync as spawnSync2 } from "child_process";
88637
89621
  var RemoteProjectSourceLockError = class extends Error {
88638
89622
  constructor(message) {
@@ -88683,15 +89667,15 @@ async function acquireRemoteProjectFileEditLock(sourceRoot, options = {}) {
88683
89667
  let abortListener = null;
88684
89668
  await new Promise((resolvePromise, reject) => {
88685
89669
  let stderr2 = "";
88686
- const onError = (error) => fail3(`Failed to start Project lock holder: ${error.message}`);
88687
- const onExit = (code, signal) => fail3(`Project lock holder exited before acquisition (${signal ?? code}): ${stderr2.trim()}`);
89670
+ const onError = (error) => fail4(`Failed to start Project lock holder: ${error.message}`);
89671
+ const onExit = (code, signal) => fail4(`Project lock holder exited before acquisition (${signal ?? code}): ${stderr2.trim()}`);
88688
89672
  const cleanup = () => {
88689
89673
  if (timer) clearTimeout(timer);
88690
89674
  if (abortListener && options.signal) options.signal.removeEventListener("abort", abortListener);
88691
89675
  child.removeListener("error", onError);
88692
89676
  child.removeListener("exit", onExit);
88693
89677
  };
88694
- const fail3 = (message) => {
89678
+ const fail4 = (message) => {
88695
89679
  cleanup();
88696
89680
  child.kill("SIGKILL");
88697
89681
  reject(new RemoteProjectSourceLockError(message));
@@ -88702,13 +89686,13 @@ async function acquireRemoteProjectFileEditLock(sourceRoot, options = {}) {
88702
89686
  child.once("error", onError);
88703
89687
  child.once("exit", onExit);
88704
89688
  child.stdout.once("data", (chunk) => {
88705
- if (chunk[0] !== LOCK_READY_BYTE) return fail3("Project lock holder returned an invalid readiness signal");
89689
+ if (chunk[0] !== LOCK_READY_BYTE) return fail4("Project lock holder returned an invalid readiness signal");
88706
89690
  cleanup();
88707
89691
  resolvePromise();
88708
89692
  });
88709
- timer = setTimeout(() => fail3(`Timed out waiting ${timeoutMs}ms for the Project file-edit lock`), timeoutMs);
89693
+ timer = setTimeout(() => fail4(`Timed out waiting ${timeoutMs}ms for the Project file-edit lock`), timeoutMs);
88710
89694
  if (options.signal) {
88711
- abortListener = () => fail3("Project lock acquisition was canceled");
89695
+ abortListener = () => fail4("Project lock acquisition was canceled");
88712
89696
  options.signal.addEventListener("abort", abortListener, { once: true });
88713
89697
  }
88714
89698
  });
@@ -88769,9 +89753,9 @@ function remoteProjectSourceLockPath(sourceRoot) {
88769
89753
  if (!stat3.isDirectory()) throw new RemoteProjectSourceLockError("Project source root must be a directory");
88770
89754
  const uid = typeof process.getuid === "function" ? process.getuid() : null;
88771
89755
  if (uid === null) throw new RemoteProjectSourceLockError("Project source locks require a numeric user identity");
88772
- const directory = join20(tmpdir4(), `openmates-project-source-locks-${uid}`);
89756
+ const directory = join21(tmpdir4(), `openmates-project-source-locks-${uid}`);
88773
89757
  preparePrivateDirectory2(directory, uid);
88774
- const lockPath = join20(directory, "source-writes.lock");
89758
+ const lockPath = join21(directory, "source-writes.lock");
88775
89759
  const descriptor = openSync6(lockPath, constants4.O_CREAT | constants4.O_RDWR | constants4.O_NOFOLLOW, 384);
88776
89760
  closeSync6(descriptor);
88777
89761
  const lockStat = lstatSync5(lockPath);
@@ -88802,10 +89786,10 @@ var RemoteFileMutationError = class extends Error {
88802
89786
  }
88803
89787
  };
88804
89788
  var MAX_FILE_BYTES2 = 200 * 1024;
88805
- var LOCK_ROOT = join21(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
88806
- var DEFAULT_JOURNAL_ROOT = join21(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89789
+ var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89790
+ var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
88807
89791
  function sha256ProjectFile(content) {
88808
- return createHash24("sha256").update(content).digest("hex");
89792
+ return createHash25("sha256").update(content).digest("hex");
88809
89793
  }
88810
89794
  async function executeRemoteFileMutation(options) {
88811
89795
  const mutation = validateMutation(options.mutation);
@@ -88820,7 +89804,7 @@ async function executeRemoteFileMutation(options) {
88820
89804
  }
88821
89805
  const scopeDigest = sha256ProjectFile(options.operationScope);
88822
89806
  const payloadDigest = sha256ProjectFile(stableJson3(mutation));
88823
- const journalPath = join21(journalRoot, scopeDigest, `${sha256ProjectFile(`${options.operationScope}\0${mutation.operation_id}`)}.json`);
89807
+ const journalPath = join22(journalRoot, scopeDigest, `${sha256ProjectFile(`${options.operationScope}\0${mutation.operation_id}`)}.json`);
88824
89808
  preparePrivateDirectory3(dirname12(journalPath));
88825
89809
  await options.authorize();
88826
89810
  const existingJournal = readJournal(journalPath);
@@ -88985,7 +89969,7 @@ function openPinnedParent(root, path2) {
88985
89969
  function readPinnedFile(parent) {
88986
89970
  let descriptor;
88987
89971
  try {
88988
- descriptor = openSync7(join21(parent.procPath, parent.targetName), constants5.O_RDONLY | constants5.O_NOFOLLOW);
89972
+ descriptor = openSync7(join22(parent.procPath, parent.targetName), constants5.O_RDONLY | constants5.O_NOFOLLOW);
88989
89973
  } catch (error) {
88990
89974
  if (error.code === "ENOENT") return null;
88991
89975
  if (error.code === "ELOOP") {
@@ -89058,8 +90042,8 @@ function assertPreviewStillCurrent(mutation, before, current) {
89058
90042
  }
89059
90043
  function createPinnedFile(parent, content) {
89060
90044
  const temp = temporaryName();
89061
- const tempPath = join21(parent.procPath, temp);
89062
- const targetPath = join21(parent.procPath, parent.targetName);
90045
+ const tempPath = join22(parent.procPath, temp);
90046
+ const targetPath = join22(parent.procPath, parent.targetName);
89063
90047
  let descriptor;
89064
90048
  try {
89065
90049
  descriptor = openSync7(tempPath, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 438);
@@ -89083,7 +90067,7 @@ function createPinnedFile(parent, content) {
89083
90067
  }
89084
90068
  }
89085
90069
  function replacePinnedFile(parent, expected, content) {
89086
- const tempPath = join21(parent.procPath, temporaryName());
90070
+ const tempPath = join22(parent.procPath, temporaryName());
89087
90071
  let descriptor;
89088
90072
  try {
89089
90073
  descriptor = openSync7(tempPath, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, expected.mode);
@@ -89096,7 +90080,7 @@ function replacePinnedFile(parent, expected, content) {
89096
90080
  if (!nearWrite || nearWrite.hash !== expected.hash || nearWrite.dev !== expected.dev || nearWrite.ino !== expected.ino) {
89097
90081
  throw new RemoteFileMutationError("file_changed", "Project file changed at the final write boundary");
89098
90082
  }
89099
- renameSync8(tempPath, join21(parent.procPath, parent.targetName));
90083
+ renameSync8(tempPath, join22(parent.procPath, parent.targetName));
89100
90084
  fsyncSync3(parent.descriptor);
89101
90085
  } catch (error) {
89102
90086
  if (descriptor !== void 0) closeSync7(descriptor);
@@ -89123,7 +90107,7 @@ ${body}${hasFinalNewline ? "" : "\\n"}`;
89123
90107
  }
89124
90108
  async function acquirePathLock(parentIdentity, path2) {
89125
90109
  preparePrivateDirectory3(LOCK_ROOT);
89126
- const target = join21(LOCK_ROOT, sha256ProjectFile(`${parentIdentity}\0${basename5(path2)}`));
90110
+ const target = join22(LOCK_ROOT, sha256ProjectFile(`${parentIdentity}\0${basename5(path2)}`));
89127
90111
  preparePrivateDirectory3(target);
89128
90112
  try {
89129
90113
  return await lockfile7.lock(target, {
@@ -89493,13 +90477,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
89493
90477
  import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
89494
90478
 
89495
90479
  // src/remoteCommandRuntime.ts
89496
- import { createHash as createHash26, randomUUID as randomUUID21 } from "crypto";
90480
+ import { createHash as createHash27, randomUUID as randomUUID21 } from "crypto";
89497
90481
  import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
89498
90482
  import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
89499
- import { isAbsolute as isAbsolute8, join as join22, relative as relative7, resolve as resolve14, sep } from "path";
90483
+ import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
89500
90484
 
89501
90485
  // src/remoteCommandAppArmor.ts
89502
- import { createHash as createHash25 } from "crypto";
90486
+ import { createHash as createHash26 } from "crypto";
89503
90487
  import { spawn as spawn5 } from "child_process";
89504
90488
  import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
89505
90489
  import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
@@ -89716,7 +90700,7 @@ function hasControl2(value) {
89716
90700
  });
89717
90701
  }
89718
90702
  function sha2564(value) {
89719
- return createHash25("sha256").update(value).digest("hex");
90703
+ return createHash26("sha256").update(value).digest("hex");
89720
90704
  }
89721
90705
 
89722
90706
  // src/remoteCommandRuntime.ts
@@ -90142,7 +91126,7 @@ function createRemoteCommandPreflight(request) {
90142
91126
  error instanceof Error ? error.message : "Project source root boundary cannot be established"
90143
91127
  );
90144
91128
  }
90145
- const cwd = request.policy.cwd === "." ? root : realpath(join22(root, request.policy.cwd));
91129
+ const cwd = request.policy.cwd === "." ? root : realpath(join23(root, request.policy.cwd));
90146
91130
  if (!isInside5(root, cwd) || !statSync6(cwd).isDirectory()) throw new RemoteCommandError("invalid_request", "Command cwd escapes the Project root");
90147
91131
  const canonicalPolicy = { ...request.policy, argv: [...request.policy.argv], cwd: relative7(root, cwd).split(sep).join("/") || "." };
90148
91132
  const toolchains = request.toolchain_paths.map(canonicalDirectory);
@@ -90260,7 +91244,7 @@ function prepareSandboxLaunch(preflight, authority, capability, network, appArmo
90260
91244
  args.push(preflight.policy.source_access === "read_write" ? "--bind" : "--ro-bind", preflight.source_root, "/project");
90261
91245
  if (preflight.policy.source_access === "read_write") {
90262
91246
  for (const protectedPath of preflight.protected_project_paths) {
90263
- args.push("--ro-bind", join22(preflight.source_root, protectedPath.path), `/project/${protectedPath.path}`);
91247
+ args.push("--ro-bind", join23(preflight.source_root, protectedPath.path), `/project/${protectedPath.path}`);
90264
91248
  }
90265
91249
  }
90266
91250
  for (const masked of preflight.masked_project_paths) {
@@ -90394,13 +91378,13 @@ function discoverProjectPathProtection(root, policy) {
90394
91378
  try {
90395
91379
  while (directories.length) {
90396
91380
  const parent = directories.pop();
90397
- for (const entry of readdirSync12(join22(root, parent), { withFileTypes: true })) {
91381
+ for (const entry of readdirSync12(join23(root, parent), { withFileTypes: true })) {
90398
91382
  visited += 1;
90399
91383
  if (visited > MAX_PROJECT_MASK_SCAN_ENTRIES) {
90400
91384
  throw new RemoteCommandError("confinement_unavailable", `Project credential scan exceeds ${MAX_PROJECT_MASK_SCAN_ENTRIES} entries`);
90401
91385
  }
90402
91386
  const path2 = parent ? `${parent}/${entry.name}` : entry.name;
90403
- const fullPath = join22(root, path2);
91387
+ const fullPath = join23(root, path2);
90404
91388
  const stat3 = lstatSync9(fullPath);
90405
91389
  const kind = entry.isDirectory() ? "directory" : "file";
90406
91390
  let masked = policy ? policy.isPrivate(path2, kind === "directory") || path2.toLowerCase() === ".git/config" : classifyProjectFileReadRisk(path2).isHighRisk || path2.toLowerCase() === ".git/config";
@@ -90557,7 +91541,7 @@ function stableJson4(value) {
90557
91541
  return JSON.stringify(value);
90558
91542
  }
90559
91543
  function sha2565(value) {
90560
- return createHash26("sha256").update(value).digest("hex");
91544
+ return createHash27("sha256").update(value).digest("hex");
90561
91545
  }
90562
91546
  function deepFreeze(value) {
90563
91547
  if (value && typeof value === "object") {
@@ -90572,7 +91556,7 @@ import { lookup as dnsLookup } from "dns/promises";
90572
91556
  import { chmodSync as chmodSync11, mkdtempSync as mkdtempSync4, realpathSync as realpathSync8, rmSync as rmSync10, statSync as statSync7, writeFileSync as writeFileSync11 } from "fs";
90573
91557
  import { isIP as isIP2, createConnection, createServer } from "net";
90574
91558
  import { tmpdir as tmpdir6 } from "os";
90575
- import { isAbsolute as isAbsolute9, join as join23, relative as relative8 } from "path";
91559
+ import { isAbsolute as isAbsolute9, join as join24, relative as relative8 } from "path";
90576
91560
  var DEFAULT_BRIDGE_PORT = 43117;
90577
91561
  var MAX_CONNECT_HEADER_BYTES = 8 * 1024;
90578
91562
  var CONNECT_HEADER_TIMEOUT_MS = 1e4;
@@ -90648,8 +91632,8 @@ async function prepareRemoteHttpsConnectNetworkConfinement(claim, preflight, opt
90648
91632
  throw capabilityError(`HTTPS network profiles require the trusted Node runtime in an approved toolchain (${nodeExecutable})`);
90649
91633
  }
90650
91634
  const controlDirectory = createPrivateControlDirectory(preflight, options.controlRoots);
90651
- const socketPath = join23(controlDirectory, "proxy.sock");
90652
- const bridgePath = join23(controlDirectory, "bridge.mjs");
91635
+ const socketPath = join24(controlDirectory, "proxy.sock");
91636
+ const bridgePath = join24(controlDirectory, "bridge.mjs");
90653
91637
  writeFileSync11(bridgePath, BRIDGE_SOURCE, { encoding: "utf8", mode: 320, flag: "wx" });
90654
91638
  const activeSockets = /* @__PURE__ */ new Set();
90655
91639
  const server2 = createServer((client2) => {
@@ -90778,7 +91762,7 @@ function createPrivateControlDirectory(preflight, configuredRoots) {
90778
91762
  try {
90779
91763
  if (!isAbsolute9(candidate) || !statSync7(candidate).isDirectory()) continue;
90780
91764
  const root = realpathSync8(candidate);
90781
- const directory = mkdtempSync4(join23(root, "openmates-command-network-"));
91765
+ const directory = mkdtempSync4(join24(root, "openmates-command-network-"));
90782
91766
  chmodSync11(directory, 448);
90783
91767
  const exposed = preflight.source_root === directory || isInside6(preflight.source_root, directory) || preflight.toolchain_paths.some((path2) => isInside6(path2, directory)) || preflight.writable_targets.some((target) => isInside6(target.host_path, directory));
90784
91768
  if (!exposed) return directory;
@@ -90835,9 +91819,9 @@ function capabilityError(message) {
90835
91819
  }
90836
91820
 
90837
91821
  // src/remoteCommandResourceGrants.ts
90838
- import { createHash as createHash27 } from "crypto";
91822
+ import { createHash as createHash28 } from "crypto";
90839
91823
  import { chmodSync as chmodSync12, existsSync as existsSync22, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, realpathSync as realpathSync9, renameSync as renameSync9, statSync as statSync8, writeFileSync as writeFileSync12 } from "fs";
90840
- import { isAbsolute as isAbsolute10, join as join24, relative as relative9 } from "path";
91824
+ import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
90841
91825
  var STORE_FILE = "remote-command-resource-grants.json";
90842
91826
  var ID = /^[a-z][a-z0-9-]{0,63}$/;
90843
91827
  var ENV = /^[A-Z_][A-Z0-9_]{0,127}$/;
@@ -90930,7 +91914,7 @@ function resolveRemoteCommandResourceGrants(context) {
90930
91914
  return { writable_targets, network_profiles, credential_profiles };
90931
91915
  }
90932
91916
  function definitionDigest(kind, definition) {
90933
- return createHash27("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
91917
+ return createHash28("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
90934
91918
  }
90935
91919
  function upsert(grant) {
90936
91920
  validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
@@ -90938,7 +91922,7 @@ function upsert(grant) {
90938
91922
  writeStore([...others, grant]);
90939
91923
  }
90940
91924
  function readStore() {
90941
- const path2 = join24(resolveStateDir(), STORE_FILE);
91925
+ const path2 = join25(resolveStateDir(), STORE_FILE);
90942
91926
  if (!existsSync22(path2)) return [];
90943
91927
  const stat3 = lstatSync10(path2);
90944
91928
  if (!stat3.isFile() || stat3.isSymbolicLink() || (stat3.mode & 63) !== 0) throw new Error("Remote command resource grant store is not private");
@@ -90950,7 +91934,7 @@ function writeStore(grants) {
90950
91934
  const directory = resolveStateDir();
90951
91935
  mkdirSync18(directory, { recursive: true, mode: 448 });
90952
91936
  chmodSync12(directory, 448);
90953
- const path2 = join24(directory, STORE_FILE);
91937
+ const path2 = join25(directory, STORE_FILE);
90954
91938
  if (existsSync22(path2) && lstatSync10(path2).isSymbolicLink()) throw new Error("Remote command resource grant store cannot be a symbolic link");
90955
91939
  const temporary = `${path2}.${process.pid}.tmp`;
90956
91940
  writeFileSync12(temporary, `${JSON.stringify({ version: 1, grants }, null, 2)}
@@ -91933,7 +92917,7 @@ function discoverRemoteAccessRepositories(roots) {
91933
92917
  }
91934
92918
  for (const entry of entries) {
91935
92919
  if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
91936
- visit(join25(directory, entry.name), approvedRoot);
92920
+ visit(join26(directory, entry.name), approvedRoot);
91937
92921
  }
91938
92922
  };
91939
92923
  for (const root of roots) visit(root, root);
@@ -91951,7 +92935,7 @@ function remoteAccessHostingCandidates(roots, explicitPath, discover = discoverR
91951
92935
  };
91952
92936
  }
91953
92937
  function remoteAccessSourceType(rootPath) {
91954
- const gitMarker = join25(rootPath, ".git");
92938
+ const gitMarker = join26(rootPath, ".git");
91955
92939
  return existsSync24(gitMarker) ? "local_git_repository" : "local_folder";
91956
92940
  }
91957
92941
  function listRemoteAccessDirectory(options) {
@@ -92008,7 +92992,7 @@ function listRemoteAccessDirectory(options) {
92008
92992
  remainingChildSummaryEntries -= 1;
92009
92993
  const childPath = `${folderPath}/${child.name}`;
92010
92994
  if (child.name.startsWith(".") || child.isSymbolicLink() || !child.isFile() && !child.isDirectory() || childPolicy.isIgnored(childPath, child.isDirectory()) || childPolicy.isPrivate(childPath, child.isDirectory())) continue;
92011
- const fileStat = child.isFile() ? lstatSync11(join25(absoluteFolder, child.name)) : null;
92995
+ const fileStat = child.isFile() ? lstatSync11(join26(absoluteFolder, child.name)) : null;
92012
92996
  if (fileStat && fileStat.nlink > 1) continue;
92013
92997
  if (child.isDirectory()) childFolderCount += 1;
92014
92998
  else {
@@ -92029,7 +93013,7 @@ function listRemoteAccessDirectory(options) {
92029
93013
  const compareNames = (a, b) => a.localeCompare(b) || (a < b ? -1 : a > b ? 1 : 0);
92030
93014
  for (const entry of readdirSync13(directory, { withFileTypes: true }).sort((a, b) => compareNames(a.name, b.name))) {
92031
93015
  if (cursor !== void 0 && compareNames(entry.name, cursor) <= 0) continue;
92032
- const entryPath = relative11(root, join25(directory, entry.name)).replace(/\\/g, "/");
93016
+ const entryPath = relative11(root, join26(directory, entry.name)).replace(/\\/g, "/");
92033
93017
  if (entry.name.startsWith(".") || entry.isSymbolicLink() || policy.isIgnored(entryPath, entry.isDirectory()) || policy.isPrivate(entryPath, entry.isDirectory())) {
92034
93018
  excluded += 1;
92035
93019
  continue;
@@ -92038,7 +93022,7 @@ function listRemoteAccessDirectory(options) {
92038
93022
  excluded += 1;
92039
93023
  continue;
92040
93024
  }
92041
- const fileStat = entry.isFile() ? lstatSync11(join25(directory, entry.name)) : void 0;
93025
+ const fileStat = entry.isFile() ? lstatSync11(join26(directory, entry.name)) : void 0;
92042
93026
  if (fileStat && fileStat.nlink > 1) {
92043
93027
  excluded += 1;
92044
93028
  continue;
@@ -92100,7 +93084,7 @@ function readRemoteAccessTextFile(options) {
92100
93084
  truncated,
92101
93085
  sizeBytes: stats.size,
92102
93086
  lineCount: countTextLines(content),
92103
- expected_base: truncated ? null : createHash28("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
93087
+ expected_base: truncated ? null : createHash29("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
92104
93088
  };
92105
93089
  } catch (error) {
92106
93090
  const code = error.code;
@@ -92150,7 +93134,7 @@ function readRemoteAccessImageChunk(options) {
92150
93134
  mime_type: mimeType,
92151
93135
  size_bytes: stats.size,
92152
93136
  offset: options.offset,
92153
- content_hash: createHash28("sha256").update(bytes2).digest("hex")
93137
+ content_hash: createHash29("sha256").update(bytes2).digest("hex")
92154
93138
  };
92155
93139
  } catch (error) {
92156
93140
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -92198,8 +93182,8 @@ function readRemoteAccessFileChunk(options) {
92198
93182
  content_base64: bytes2.toString("base64"),
92199
93183
  size_bytes: stats.size,
92200
93184
  offset: options.offset,
92201
- file_identity: createHash28("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
92202
- chunk_hash: createHash28("sha256").update(bytes2).digest("hex")
93185
+ file_identity: createHash29("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
93186
+ chunk_hash: createHash29("sha256").update(bytes2).digest("hex")
92203
93187
  };
92204
93188
  } catch (error) {
92205
93189
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -92307,13 +93291,13 @@ async function runRemoteAccessBridge(options) {
92307
93291
  for (const binding of options.bindings) {
92308
93292
  const projectId = binding.source.projectId;
92309
93293
  if (caches.has(projectId)) continue;
92310
- const cache = new ProjectTaskCache(options.taskCacheRoot ?? join25(resolveStateDir(), "project-task-cache"), accountScope, projectId);
93294
+ const cache = new ProjectTaskCache(options.taskCacheRoot ?? join26(resolveStateDir(), "project-task-cache"), accountScope, projectId);
92311
93295
  releases.push(await lockfile8.lock(cache.directory, { retries: 0, stale: 12e4 }));
92312
93296
  caches.set(projectId, cache);
92313
93297
  }
92314
93298
  codexBridges = startCodexTaskBridges(
92315
93299
  options.bindings.map((item) => item.source.rootPath),
92316
- [...caches.values()].map((cache) => join25(cache.directory, "snapshot.json")),
93300
+ [...caches.values()].map((cache) => join26(cache.directory, "snapshot.json")),
92317
93301
  resolveStateDir(),
92318
93302
  (status) => options.onTaskSync?.({ projectId: "", directory: "", status })
92319
93303
  );
@@ -92672,7 +93656,7 @@ function waitForAbort2(signal, timeoutMs) {
92672
93656
  }
92673
93657
  function resolveRemoteCachePath(sourceId, homeDirectory) {
92674
93658
  assertSafeSourceId(sourceId);
92675
- return join25(remoteAccessStateDirectory(homeDirectory), "remote-cache", sourceId);
93659
+ return join26(remoteAccessStateDirectory(homeDirectory), "remote-cache", sourceId);
92676
93660
  }
92677
93661
  function startRemoteAccessSource(input) {
92678
93662
  assertSafeSourceId(input.sourceId);
@@ -92822,7 +93806,7 @@ function searchRemoteSourceWithoutRg(request, sourceRoot, targetPath, policy) {
92822
93806
  continue;
92823
93807
  }
92824
93808
  for (const entry of entries) {
92825
- const absolutePath = join25(directory, entry.name);
93809
+ const absolutePath = join26(directory, entry.name);
92826
93810
  const relativePath = relative11(root, absolutePath).replace(/\\/g, "/");
92827
93811
  if (entry.name === ".git" || entry.isSymbolicLink() || policy.isIgnored(relativePath, entry.isDirectory()) || policy.isPrivate(relativePath, entry.isDirectory())) {
92828
93812
  excluded += 1;
@@ -92952,15 +93936,15 @@ function searchRemoteFileNamesWithoutRg(request, sourceRoot, targetPath, policy,
92952
93936
  enumerationTruncated = true;
92953
93937
  break;
92954
93938
  }
92955
- const path2 = relative11(sourceRoot, join25(absolute, entry.name)).replace(/\\/g, "/");
93939
+ const path2 = relative11(sourceRoot, join26(absolute, entry.name)).replace(/\\/g, "/");
92956
93940
  const isDirectory = entry.isDirectory();
92957
- if (skipPriority && priorityAbsolute === join25(absolute, entry.name)) continue;
93941
+ if (skipPriority && priorityAbsolute === join26(absolute, entry.name)) continue;
92958
93942
  if (entry.name.startsWith(".") || entry.isSymbolicLink() || !entry.isFile() && !isDirectory || policy.isIgnored(path2, isDirectory) || policy.isPrivate(path2, isDirectory)) {
92959
93943
  excluded += 1;
92960
93944
  continue;
92961
93945
  }
92962
- if (isDirectory) pending.push(join25(absolute, entry.name));
92963
- else if (lstatSync11(join25(absolute, entry.name)).nlink > 1) {
93946
+ if (isDirectory) pending.push(join26(absolute, entry.name));
93947
+ else if (lstatSync11(join26(absolute, entry.name)).nlink > 1) {
92964
93948
  excluded += 1;
92965
93949
  continue;
92966
93950
  }
@@ -93165,7 +94149,7 @@ function collectSearchPathMetadata(sourceRoot, targetPath, policy, collectDirect
93165
94149
  throw new Error("Remote source search alias scan exceeded its bounded file limit");
93166
94150
  }
93167
94151
  if (entry.name === ".git" || entry.isSymbolicLink()) continue;
93168
- const absolute = join25(directory, entry.name);
94152
+ const absolute = join26(directory, entry.name);
93169
94153
  const path2 = relative11(sourceRoot, absolute).replace(/\\/g, "/");
93170
94154
  if (entry.isDirectory()) {
93171
94155
  if (!policy.isIgnored(path2, true) && !policy.isPrivate(path2, true) && (!collectDirectories || !entry.name.startsWith("."))) {
@@ -93242,7 +94226,7 @@ function resolveApprovedPath(sourceRoot, relativePath) {
93242
94226
  const relation = relative11(sourceRoot, lexical);
93243
94227
  let current = sourceRoot;
93244
94228
  for (const part of relation.split(/[\\/]/).filter(Boolean)) {
93245
- current = join25(current, part);
94229
+ current = join26(current, part);
93246
94230
  if (lstatSync11(current).isSymbolicLink()) throw new Error("Remote source path is a symbolic link");
93247
94231
  }
93248
94232
  const canonical = realpathSync11(lexical);
@@ -93306,7 +94290,7 @@ function remoteAccessStateDirectory(homeDirectory) {
93306
94290
  return homeDirectory === void 0 ? resolveStateDir() : resolveStateDir({ homeDir: homeDirectory, stateDir: "", profile: "" });
93307
94291
  }
93308
94292
  function remoteAccessStorePath(homeDirectory) {
93309
- return join25(remoteAccessStateDirectory(homeDirectory), "remote-sources.json");
94293
+ return join26(remoteAccessStateDirectory(homeDirectory), "remote-sources.json");
93310
94294
  }
93311
94295
  function saveRemoteAccessSources(sources, homeDirectory) {
93312
94296
  const filePath = remoteAccessStorePath(homeDirectory);
@@ -93390,11 +94374,11 @@ function inheritedInstaller(executable, args) {
93390
94374
  import { spawnSync as spawnSync5 } from "child_process";
93391
94375
  import { readFileSync as readFileSync22, mkdirSync as mkdirSync20, writeFileSync as writeFileSync14, renameSync as renameSync10 } from "fs";
93392
94376
  import { homedir as homedir7 } from "os";
93393
- import { dirname as dirname14, basename as basename8, join as join26 } from "path";
94377
+ import { dirname as dirname14, basename as basename8, join as join27 } from "path";
93394
94378
  import { fileURLToPath as fileURLToPath3 } from "url";
93395
94379
  var PACKAGE_NAME = "openmates";
93396
94380
  function channelFile() {
93397
- return join26(homedir7(), ".openmates", "updates.json");
94381
+ return join27(homedir7(), ".openmates", "updates.json");
93398
94382
  }
93399
94383
  function parseChannel(value) {
93400
94384
  if (value === "dev") return "dev";
@@ -93764,7 +94748,7 @@ import { execFileSync as execFileSync6 } from "child_process";
93764
94748
  import { createSign, randomUUID as randomUUID22 } from "crypto";
93765
94749
  import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
93766
94750
  import { homedir as homedir8 } from "os";
93767
- import { join as join27, resolve as resolve17 } from "path";
94751
+ import { join as join28, resolve as resolve17 } from "path";
93768
94752
  var REVOLUT_BUSINESS_CERTIFICATE_DOCS_URL = "https://developer.revolut.com/docs/guides/manage-accounts/get-started/make-your-first-api-request#generate-a-private-and-a-public-certificate";
93769
94753
  var REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI = "https://app.dev.openmates.org/oauth/revolut-business/callback";
93770
94754
  var REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI = "https://openmates.org/oauth/revolut-business/callback";
@@ -93774,7 +94758,7 @@ var REVOLUT_BUSINESS_AUDIENCE = "https://revolut.com";
93774
94758
  var CERTIFICATE_DAYS = "1825";
93775
94759
  var RSA_BITS = "2048";
93776
94760
  function defaultRevolutBusinessCertificateDir(environment) {
93777
- return join27(homedir8(), ".openmates", "revolut-business", environment);
94761
+ return join28(homedir8(), ".openmates", "revolut-business", environment);
93778
94762
  }
93779
94763
  function defaultRevolutBusinessRedirectUri(environment) {
93780
94764
  return environment === "sandbox" ? REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI : REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI;
@@ -93797,7 +94781,7 @@ async function exchangeRevolutBusinessAuthorizationCode(options) {
93797
94781
  if (!clientId) throw new Error("Missing Revolut Business client ID.");
93798
94782
  const code = extractRevolutBusinessAuthorizationCode(options.codeOrRedirectUrl);
93799
94783
  if (!code) throw new Error("Missing Revolut Business authorization code.");
93800
- const privateKeyPem = options.privateKeyPem ?? readFileSync23(options.privateKeyPath ?? join27(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
94784
+ const privateKeyPem = options.privateKeyPem ?? readFileSync23(options.privateKeyPath ?? join28(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
93801
94785
  const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
93802
94786
  const clientAssertion = generateRevolutBusinessClientAssertion({ clientId, privateKeyPem, redirectUri });
93803
94787
  const response = await fetch(environment === "sandbox" ? "https://sandbox-b2b.revolut.com/api/1.0/auth/token" : "https://b2b.revolut.com/api/1.0/auth/token", {
@@ -93852,8 +94836,8 @@ function generateRevolutBusinessCertificate(options = {}) {
93852
94836
  if (!redirectUri.startsWith("https://")) throw new Error("Revolut OAuth redirect URI must start with https://.");
93853
94837
  mkdirSync22(outputDir, { recursive: true, mode: 448 });
93854
94838
  chmodSync14(outputDir, 448);
93855
- const privateKeyPath = join27(outputDir, "privatecert.pem");
93856
- const publicCertificatePath = join27(outputDir, "publiccert.cer");
94839
+ const privateKeyPath = join28(outputDir, "privatecert.pem");
94840
+ const publicCertificatePath = join28(outputDir, "publiccert.cer");
93857
94841
  if (!options.overwrite && (existsSync25(privateKeyPath) || existsSync25(publicCertificatePath))) {
93858
94842
  throw new Error(`Revolut certificate files already exist in ${outputDir}. Re-run with --overwrite to replace them.`);
93859
94843
  }
@@ -95548,7 +96532,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
95548
96532
  if (action === "diff") throw new Error("Revision diffs are reviewed in the authenticated web review page; use submit-for-review to print its URL.");
95549
96533
  if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
95550
96534
  const snapshot2 = JSON.stringify(planToJson(plan));
95551
- const revision = await client2.submitPlanRevision(plan.planId, { fingerprint: createHash29("sha256").update(snapshot2).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot2), created_at: nowSeconds3() });
96535
+ const revision = await client2.submitPlanRevision(plan.planId, { fingerprint: createHash30("sha256").update(snapshot2).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot2), created_at: nowSeconds3() });
95552
96536
  const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
95553
96537
  if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
95554
96538
  else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
@@ -96468,7 +97452,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
96468
97452
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
96469
97453
  key_wrappers: wrapping.teamId ? [{
96470
97454
  key_type: "team",
96471
- hashed_team_id: createHash29("sha256").update(wrapping.teamId).digest("hex"),
97455
+ hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
96472
97456
  team_key_epoch: 1,
96473
97457
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
96474
97458
  wrapper_version: 1,
@@ -97844,7 +98828,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
97844
98828
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
97845
98829
  key_wrappers: wrapping.teamId ? [{
97846
98830
  key_type: "team",
97847
- hashed_team_id: createHash29("sha256").update(wrapping.teamId).digest("hex"),
98831
+ hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
97848
98832
  team_key_epoch: 1,
97849
98833
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
97850
98834
  wrapper_version: 1,
@@ -97918,7 +98902,7 @@ function readRootProjectInstructions(rootPath) {
97918
98902
  return left.localeCompare(right);
97919
98903
  });
97920
98904
  const source = matchingNames[0];
97921
- if (source) return { instructions: readFileSync24(join28(rootPath, source), "utf8"), source };
98905
+ if (source) return { instructions: readFileSync24(join29(rootPath, source), "utf8"), source };
97922
98906
  }
97923
98907
  return null;
97924
98908
  }
@@ -98817,31 +99801,31 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
98817
99801
  } catch {
98818
99802
  }
98819
99803
  }
98820
- const { mkdir: mkdir3, writeFile: writeFile2 } = await import("fs/promises");
98821
- const { join: join29 } = await import("path");
99804
+ const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
99805
+ const { join: join30 } = await import("path");
98822
99806
  if (useZip) {
98823
- const tmpDir = join29(outputDir, `.${filenameBase}_tmp`);
98824
- await mkdir3(tmpDir, { recursive: true });
98825
- await writeFile2(join29(tmpDir, `${filenameBase}.yml`), yamlContent);
98826
- await writeFile2(join29(tmpDir, `${filenameBase}.md`), mdContent);
99807
+ const tmpDir = join30(outputDir, `.${filenameBase}_tmp`);
99808
+ await mkdir4(tmpDir, { recursive: true });
99809
+ await writeFile3(join30(tmpDir, `${filenameBase}.yml`), yamlContent);
99810
+ await writeFile3(join30(tmpDir, `${filenameBase}.md`), mdContent);
98827
99811
  if (codeEmbeds.length > 0) {
98828
99812
  for (const ce of codeEmbeds) {
98829
99813
  const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
98830
- const fullPath = join29(tmpDir, "code", fpath);
98831
- await mkdir3(fullPath.substring(0, fullPath.lastIndexOf("/")), {
99814
+ const fullPath = join30(tmpDir, "code", fpath);
99815
+ await mkdir4(fullPath.substring(0, fullPath.lastIndexOf("/")), {
98832
99816
  recursive: true
98833
99817
  });
98834
- await writeFile2(fullPath, ce.content);
99818
+ await writeFile3(fullPath, ce.content);
98835
99819
  }
98836
99820
  }
98837
99821
  if (transcriptEmbeds.length > 0) {
98838
- const tDir = join29(tmpDir, "transcripts");
98839
- await mkdir3(tDir, { recursive: true });
99822
+ const tDir = join30(tmpDir, "transcripts");
99823
+ await mkdir4(tDir, { recursive: true });
98840
99824
  for (const te of transcriptEmbeds) {
98841
- await writeFile2(join29(tDir, te.filename), te.content);
99825
+ await writeFile3(join30(tDir, te.filename), te.content);
98842
99826
  }
98843
99827
  }
98844
- const zipPath = join29(outputDir, `${filenameBase}.zip`);
99828
+ const zipPath = join30(outputDir, `${filenameBase}.zip`);
98845
99829
  const { execSync: execSync2 } = await import("child_process");
98846
99830
  try {
98847
99831
  execSync2(`cd "${tmpDir}" && zip -r "${zipPath}" .`, { stdio: "pipe" });
@@ -98856,29 +99840,29 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
98856
99840
  );
98857
99841
  }
98858
99842
  } else {
98859
- const chatDir = join29(outputDir, filenameBase);
98860
- await mkdir3(chatDir, { recursive: true });
99843
+ const chatDir = join30(outputDir, filenameBase);
99844
+ await mkdir4(chatDir, { recursive: true });
98861
99845
  const written = [];
98862
- await writeFile2(join29(chatDir, `${filenameBase}.yml`), yamlContent);
99846
+ await writeFile3(join30(chatDir, `${filenameBase}.yml`), yamlContent);
98863
99847
  written.push(`${filenameBase}.yml`);
98864
- await writeFile2(join29(chatDir, `${filenameBase}.md`), mdContent);
99848
+ await writeFile3(join30(chatDir, `${filenameBase}.md`), mdContent);
98865
99849
  written.push(`${filenameBase}.md`);
98866
99850
  if (codeEmbeds.length > 0) {
98867
99851
  for (const ce of codeEmbeds) {
98868
99852
  const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
98869
- const fullPath = join29(chatDir, "code", fpath);
98870
- await mkdir3(fullPath.substring(0, fullPath.lastIndexOf("/")), {
99853
+ const fullPath = join30(chatDir, "code", fpath);
99854
+ await mkdir4(fullPath.substring(0, fullPath.lastIndexOf("/")), {
98871
99855
  recursive: true
98872
99856
  });
98873
- await writeFile2(fullPath, ce.content);
99857
+ await writeFile3(fullPath, ce.content);
98874
99858
  written.push(`code/${fpath}`);
98875
99859
  }
98876
99860
  }
98877
99861
  if (transcriptEmbeds.length > 0) {
98878
- const tDir = join29(chatDir, "transcripts");
98879
- await mkdir3(tDir, { recursive: true });
99862
+ const tDir = join30(chatDir, "transcripts");
99863
+ await mkdir4(tDir, { recursive: true });
98880
99864
  for (const te of transcriptEmbeds) {
98881
- await writeFile2(join29(tDir, te.filename), te.content);
99865
+ await writeFile3(join30(tDir, te.filename), te.content);
98882
99866
  written.push(`transcripts/${te.filename}`);
98883
99867
  }
98884
99868
  }
@@ -98914,7 +99898,7 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
98914
99898
  printJson2({
98915
99899
  chat_id: chat.id,
98916
99900
  title: chat.title,
98917
- output_dir: useZip ? join29(outputDir, `${filenameBase}.zip`) : join29(outputDir, filenameBase),
99901
+ output_dir: useZip ? join30(outputDir, `${filenameBase}.zip`) : join30(outputDir, filenameBase),
98918
99902
  files,
98919
99903
  code_embeds: codeEmbeds.length,
98920
99904
  transcript_embeds: transcriptEmbeds.length
@@ -99235,7 +100219,7 @@ function normalizeApiKeyLoadedChat(payload) {
99235
100219
  const messages = rawMessages.map((message) => {
99236
100220
  const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
99237
100221
  const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
99238
- const hashedMessageId = messageId ? createHash29("sha256").update(messageId).digest("hex") : null;
100222
+ const hashedMessageId = messageId ? createHash30("sha256").update(messageId).digest("hex") : null;
99239
100223
  const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
99240
100224
  return {
99241
100225
  id: messageId,
@@ -101381,8 +102365,8 @@ function printAccountExportBundle(bundle, flags) {
101381
102365
  }
101382
102366
  var ACCOUNT_IMPORT_SOURCES = /* @__PURE__ */ new Set(["openmates", "chatgpt", "claude", "gemini", "opencode", "other"]);
101383
102367
  async function parseAccountImportFile(parserFormat, file, flags = {}) {
101384
- const { readFile: readFile2 } = await import("fs/promises");
101385
- const payload = await readFile2(file);
102368
+ const { readFile: readFile3 } = await import("fs/promises");
102369
+ const payload = await readFile3(file);
101386
102370
  const requestedSource = typeof flags.source === "string" ? flags.source : parserFormat;
101387
102371
  if (!ACCOUNT_IMPORT_SOURCES.has(requestedSource)) throw new Error(`Unsupported account import source: ${requestedSource}`);
101388
102372
  const selectedSource = requestedSource;
@@ -101762,13 +102746,13 @@ function parseYamlScalar(value) {
101762
102746
  return trimmed;
101763
102747
  }
101764
102748
  async function saveDownloadedDocument(document, output) {
101765
- const { mkdir: mkdir3, writeFile: writeFile2 } = await import("fs/promises");
101766
- const { join: join29, basename: basename10, dirname: dirname17 } = await import("path");
102749
+ const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
102750
+ const { join: join30, basename: basename10, dirname: dirname17 } = await import("path");
101767
102751
  const target = typeof output === "string" ? output : ".";
101768
102752
  const filename = basename10(document.filename || "document.pdf");
101769
- const filePath = target.endsWith(".pdf") ? target : join29(target, filename);
101770
- await mkdir3(dirname17(filePath), { recursive: true });
101771
- await writeFile2(filePath, document.data);
102753
+ const filePath = target.endsWith(".pdf") ? target : join30(target, filename);
102754
+ await mkdir4(dirname17(filePath), { recursive: true });
102755
+ await writeFile3(filePath, document.data);
101772
102756
  return filePath;
101773
102757
  }
101774
102758
  function printMates(json) {
@@ -102233,7 +103217,7 @@ async function promptPlainText(question) {
102233
103217
  }
102234
103218
  }
102235
103219
  async function writeSecretFile(filePath, content, force = false) {
102236
- const { mkdir: mkdir3, writeFile: writeFile2, stat: stat3 } = await import("fs/promises");
103220
+ const { mkdir: mkdir4, writeFile: writeFile3, stat: stat3 } = await import("fs/promises");
102237
103221
  const { dirname: dirname17 } = await import("path");
102238
103222
  try {
102239
103223
  await stat3(filePath);
@@ -102244,8 +103228,8 @@ async function writeSecretFile(filePath, content, force = false) {
102244
103228
  }
102245
103229
  if (error instanceof Error && !("code" in error)) throw error;
102246
103230
  }
102247
- await mkdir3(dirname17(filePath), { recursive: true });
102248
- await writeFile2(filePath, content, { mode: 384 });
103231
+ await mkdir4(dirname17(filePath), { recursive: true });
103232
+ await writeFile3(filePath, content, { mode: 384 });
102249
103233
  return filePath;
102250
103234
  }
102251
103235
  async function generateProvisioningPassword() {
@@ -102611,8 +103595,8 @@ async function handleSettings(client2, subcommand, rest, flags) {
102611
103595
  if (matches(tokens, ["account", "import-chat"])) {
102612
103596
  const file = rest[1];
102613
103597
  if (!file) throw new Error("Missing import file. Example: openmates settings account import-chat ./chat.yml");
102614
- const { readFile: readFile2 } = await import("fs/promises");
102615
- const content = await readFile2(file, "utf-8");
103598
+ const { readFile: readFile3 } = await import("fs/promises");
103599
+ const content = await readFile3(file, "utf-8");
102616
103600
  await printSettingsMutationResult(
102617
103601
  client2.settingsPost("import-chat", parseChatImportPayload(content)),
102618
103602
  flags
@@ -106341,19 +107325,19 @@ async function handleDocs(client2, subcommand, rest, flags) {
106341
107325
  return;
106342
107326
  }
106343
107327
  if (subcommand === "download") {
106344
- const { writeFile: writeFile2, mkdir: mkdir3 } = await import("fs/promises");
106345
- const { join: join29, dirname: dirname17 } = await import("path");
107328
+ const { writeFile: writeFile3, mkdir: mkdir4 } = await import("fs/promises");
107329
+ const { join: join30, dirname: dirname17 } = await import("path");
106346
107330
  if (flags.all === true) {
106347
107331
  const outputDir = typeof flags.output === "string" ? flags.output : "./openmates-docs";
106348
107332
  const tree = await client2.listDocs();
106349
107333
  const slugs = collectSlugs(tree);
106350
- await mkdir3(outputDir, { recursive: true });
107334
+ await mkdir4(outputDir, { recursive: true });
106351
107335
  let count = 0;
106352
107336
  for (const slug2 of slugs) {
106353
107337
  const content2 = await client2.getDoc(slug2);
106354
- const filePath = join29(outputDir, `${slug2}.md`);
106355
- await mkdir3(dirname17(filePath), { recursive: true });
106356
- await writeFile2(filePath, content2, "utf-8");
107338
+ const filePath = join30(outputDir, `${slug2}.md`);
107339
+ await mkdir4(dirname17(filePath), { recursive: true });
107340
+ await writeFile3(filePath, content2, "utf-8");
106357
107341
  count++;
106358
107342
  process.stderr.write(`\r Downloaded ${count}/${slugs.length}`);
106359
107343
  }
@@ -106372,7 +107356,7 @@ async function handleDocs(client2, subcommand, rest, flags) {
106372
107356
  }
106373
107357
  const content = await client2.getDoc(slug);
106374
107358
  const filename = typeof flags.output === "string" ? flags.output : `${slug.split("/").pop()}.md`;
106375
- await writeFile2(filename, content, "utf-8");
107359
+ await writeFile3(filename, content, "utf-8");
106376
107360
  console.log(`Saved to ${filename}`);
106377
107361
  return;
106378
107362
  }