openmates 0.27.0-alpha.3 → 0.27.0-alpha.4

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"),
@@ -81400,10 +82200,10 @@ function buildAssistantFeedbackDecision(rating) {
81400
82200
  }
81401
82201
 
81402
82202
  // src/benchmark.ts
81403
- import { createHash as createHash19, randomUUID as randomUUID16 } from "crypto";
82203
+ import { createHash as createHash20, randomUUID as randomUUID16 } from "crypto";
81404
82204
  import { existsSync as existsSync14, mkdtempSync as mkdtempSync3, readFileSync as readFileSync15, readdirSync as readdirSync7, writeFileSync as writeFileSync9 } from "fs";
81405
82205
  import { tmpdir as tmpdir3 } from "os";
81406
- import { dirname as dirname9, join as join16, resolve as resolve9 } from "path";
82206
+ import { dirname as dirname9, join as join17, resolve as resolve9 } from "path";
81407
82207
  import { fileURLToPath } from "url";
81408
82208
  var DEFAULT_JUDGE_MODEL = "google/gemini-3-flash-preview";
81409
82209
  var DEFAULT_PROMPT_BUDGET_MODEL = "google/gemini-3.5-flash";
@@ -82571,7 +83371,7 @@ function loadProviderPricing() {
82571
83371
  if (!providersDir) return pricing;
82572
83372
  for (const fileName of readdirSync7(providersDir)) {
82573
83373
  if (!fileName.endsWith(".yml")) continue;
82574
- const filePath = join16(providersDir, fileName);
83374
+ const filePath = join17(providersDir, fileName);
82575
83375
  const text4 = readFileSync15(filePath, "utf-8");
82576
83376
  const provider = parseProviderId(text4) ?? fileName.replace(/\.yml$/, "");
82577
83377
  for (const modelPricing of parseModelPricing(text4, provider)) {
@@ -82626,9 +83426,9 @@ function findProvidersDir() {
82626
83426
  const currentFile = fileURLToPath(import.meta.url);
82627
83427
  let current = dirname9(currentFile);
82628
83428
  for (let index = 0; index < 8; index += 1) {
82629
- const candidate = join16(current, "backend", "providers");
83429
+ const candidate = join17(current, "backend", "providers");
82630
83430
  if (existsSync14(candidate)) return candidate;
82631
- const parentCandidate = join16(current, "..", "..", "backend", "providers");
83431
+ const parentCandidate = join17(current, "..", "..", "backend", "providers");
82632
83432
  if (existsSync14(parentCandidate)) return resolve9(parentCandidate);
82633
83433
  const next = dirname9(current);
82634
83434
  if (next === current) break;
@@ -82834,14 +83634,14 @@ function hashPromptBudgetCorpus(cases) {
82834
83634
  return stableHash(cases.map((benchmarkCase) => ({ id: benchmarkCase.id, turns: benchmarkCase.turns })));
82835
83635
  }
82836
83636
  function stableHash(value) {
82837
- return createHash19("sha256").update(JSON.stringify(value)).digest("hex");
83637
+ return createHash20("sha256").update(JSON.stringify(value)).digest("hex");
82838
83638
  }
82839
83639
  function defaultImageFixturePath() {
82840
- const fixtureDir = join16(dirname9(fileURLToPath(import.meta.url)), "..", "fixtures");
82841
- const fixturePath = join16(fixtureDir, "brandenburger-tor.png");
83640
+ const fixtureDir = join17(dirname9(fileURLToPath(import.meta.url)), "..", "fixtures");
83641
+ const fixturePath = join17(fixtureDir, "brandenburger-tor.png");
82842
83642
  if (existsSync14(fixturePath)) return fixturePath;
82843
- const tempDir = mkdtempSync3(join16(tmpdir3(), "openmates-benchmark-"));
82844
- const tempPath = join16(tempDir, "brandenburger-tor.svg");
83643
+ const tempDir = mkdtempSync3(join17(tmpdir3(), "openmates-benchmark-"));
83644
+ const tempPath = join17(tempDir, "brandenburger-tor.svg");
82845
83645
  writeFileSync9(tempPath, FIXTURE_IMAGE_SVG, "utf-8");
82846
83646
  return tempPath;
82847
83647
  }
@@ -83521,7 +84321,7 @@ var APP_GRADIENTS = {
83521
84321
  var PRIMARY_GRADIENT = { "start": "#4867cd", "end": "#5a85eb" };
83522
84322
 
83523
84323
  // src/tuiProjectsWorkspace.ts
83524
- import { createHash as createHash20, randomBytes as randomBytes10, randomUUID as randomUUID17 } from "crypto";
84324
+ import { createHash as createHash21, randomBytes as randomBytes10, randomUUID as randomUUID17 } from "crypto";
83525
84325
 
83526
84326
  // src/tuiForms.ts
83527
84327
  function formValue(form, name) {
@@ -83697,7 +84497,7 @@ async function loadTuiProject(client2, id) {
83697
84497
  client2.listProjectSources(id, context)
83698
84498
  ]);
83699
84499
  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]));
84500
+ const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
83701
84501
  project.folders = await Promise.all(itemsResponse.folders.filter((folder) => typeof folder.folder_id === "string" && folder.folder_id).map(async (folder) => {
83702
84502
  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
84503
  return {
@@ -83957,7 +84757,7 @@ async function submitProjectForm(client2, form) {
83957
84757
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
83958
84758
  key_wrappers: wrapping.teamId ? [{
83959
84759
  key_type: "team",
83960
- hashed_team_id: createHash20("sha256").update(wrapping.teamId).digest("hex"),
84760
+ hashed_team_id: createHash21("sha256").update(wrapping.teamId).digest("hex"),
83961
84761
  team_key_epoch: 1,
83962
84762
  encrypted_project_key: await encryptBytesWithAesGcm(key, wrapping.key),
83963
84763
  wrapper_version: 1,
@@ -88020,13 +88820,13 @@ function clamp(value, min, max) {
88020
88820
  }
88021
88821
 
88022
88822
  // src/codexTaskBridge.ts
88023
- import { createHash as createHash21 } from "crypto";
88823
+ import { createHash as createHash22 } from "crypto";
88024
88824
  import { spawn as spawn3 } from "child_process";
88025
88825
  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";
88826
+ import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
88027
88827
  import lockfile5 from "proper-lockfile";
88028
88828
  function startCodexTaskBridges(roots, snapshots, stateRoot, notice) {
88029
- const configPath = join17(stateRoot, "codex-adapter.json");
88829
+ const configPath = join18(stateRoot, "codex-adapter.json");
88030
88830
  const children = [];
88031
88831
  if (existsSync15(configPath)) {
88032
88832
  const config = JSON.parse(readFileSync16(configPath, "utf8"));
@@ -88063,13 +88863,13 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
88063
88863
  const session = client2.getSession();
88064
88864
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
88065
88865
  const account = JSON.stringify(scope2);
88066
- const directory = join17(stateRoot, "codex-chat-deletions", createHash21("sha256").update(account).digest("hex"));
88866
+ const directory = join18(stateRoot, "codex-chat-deletions", createHash22("sha256").update(account).digest("hex"));
88067
88867
  if (!existsSync15(directory)) return;
88068
88868
  const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
88069
88869
  try {
88070
88870
  let attempts = 0;
88071
88871
  for (const name of readdirSync8(directory).filter((name2) => /^[a-f0-9]{64}\.json$/.test(name2)).sort()) {
88072
- const path2 = join17(directory, name);
88872
+ const path2 = join18(directory, name);
88073
88873
  const record7 = JSON.parse(readFileSync16(path2, "utf8"));
88074
88874
  if (record7.state !== "pending") continue;
88075
88875
  if (attempts++ >= 3) break;
@@ -88097,18 +88897,18 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
88097
88897
  }
88098
88898
 
88099
88899
  // src/taskCommandDelivery.ts
88100
- import { createHash as createHash22 } from "crypto";
88900
+ import { createHash as createHash23 } from "crypto";
88101
88901
  import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
88102
- import { join as join18 } from "path";
88902
+ import { join as join19 } from "path";
88103
88903
  import lockfile6 from "proper-lockfile";
88104
88904
  async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
88105
88905
  const session = client2.getSession();
88106
88906
  const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
88107
- const directory = join18(root, "task-command-delivery", createHash22("sha256").update(JSON.stringify(scope2)).digest("hex"));
88907
+ const directory = join19(root, "task-command-delivery", createHash23("sha256").update(JSON.stringify(scope2)).digest("hex"));
88108
88908
  if (!existsSync16(directory)) return;
88109
88909
  const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
88110
88910
  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);
88911
+ 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
88912
  let attempted = 0;
88113
88913
  for (const { path: path2, command } of records) {
88114
88914
  if (command.state !== "pending") continue;
@@ -88214,8 +89014,8 @@ import {
88214
89014
  writeFileSync as writeFileSync13
88215
89015
  } from "fs";
88216
89016
  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";
89017
+ import { createHash as createHash29 } from "crypto";
89018
+ import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
88219
89019
 
88220
89020
  // src/projectSourceRootPolicy.ts
88221
89021
  import { existsSync as existsSync17, realpathSync as realpathSync2, statSync as statSync2 } from "fs";
@@ -88249,7 +89049,7 @@ function isEqualOrInside(parent, candidate) {
88249
89049
  }
88250
89050
 
88251
89051
  // src/projectPathPolicy.ts
88252
- import { createHash as createHash23, randomBytes as randomBytes12 } from "crypto";
89052
+ import { createHash as createHash24, randomBytes as randomBytes12 } from "crypto";
88253
89053
  import {
88254
89054
  chmodSync as chmodSync9,
88255
89055
  closeSync as closeSync5,
@@ -88269,7 +89069,7 @@ import {
88269
89069
  unlinkSync,
88270
89070
  writeSync
88271
89071
  } from "fs";
88272
- import { dirname as dirname11, isAbsolute as isAbsolute5, join as join19, relative as relative4, resolve as resolve12 } from "path";
89072
+ import { dirname as dirname11, isAbsolute as isAbsolute5, join as join20, relative as relative4, resolve as resolve12 } from "path";
88273
89073
  var DEFAULT_MAX_IGNORE_FILES = 128;
88274
89074
  var DEFAULT_MAX_IGNORE_BYTES = 1024 * 1024;
88275
89075
  var MAX_POLICY_ENTRIES = 5e4;
@@ -88362,7 +89162,7 @@ function loadProjectPathPolicy(sourceRoot, options = {}) {
88362
89162
  function findGitPolicyRoot(sourceRoot) {
88363
89163
  let current = sourceRoot;
88364
89164
  while (true) {
88365
- if (existsSync18(join19(current, ".git"))) return current;
89165
+ if (existsSync18(join20(current, ".git"))) return current;
88366
89166
  const parent = dirname11(current);
88367
89167
  if (parent === current) return sourceRoot;
88368
89168
  current = parent;
@@ -88392,7 +89192,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88392
89192
  if (parent === current || !isInside2(policyRoot, parent)) throw new Error("Source root is outside its Git policy root");
88393
89193
  current = parent;
88394
89194
  }
88395
- for (const directory of chain.reverse()) add(join19(directory, ".gitignore"));
89195
+ for (const directory of chain.reverse()) add(join20(directory, ".gitignore"));
88396
89196
  for (const target of limits.targetPaths) {
88397
89197
  const normalized = target === "." ? "" : normalizeSourceRelativePath(target);
88398
89198
  const absolute = normalized ? resolve12(sourceRoot, normalized) : sourceRoot;
@@ -88406,7 +89206,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88406
89206
  directory = dirname11(directory);
88407
89207
  }
88408
89208
  for (const ancestor of ancestors.reverse()) {
88409
- const ignorePath = join19(ancestor, ".gitignore");
89209
+ const ignorePath = join20(ancestor, ".gitignore");
88410
89210
  if (!files.some((file) => file.path === normalizeRelative(policyRoot, ignorePath))) add(ignorePath);
88411
89211
  }
88412
89212
  }
@@ -88419,7 +89219,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88419
89219
  for (const entry of readdirSync10(directory, { withFileTypes: true })) {
88420
89220
  if (++entries > limits.maxEntries) throw new Error("Project path policy scan exceeds its entry limit");
88421
89221
  if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
88422
- const child = join19(directory, entry.name);
89222
+ const child = join20(directory, entry.name);
88423
89223
  const canonical = realpathSync3(child);
88424
89224
  if (!isInside2(sourceRoot, canonical) || seen.has(canonical)) continue;
88425
89225
  const sourceRelative = normalizeRelative(sourceRoot, child);
@@ -88427,7 +89227,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
88427
89227
  if (limits.privatePolicy.isPrivate(sourceRelative, true) || currentPolicy.isIgnored(policyRelative, true)) continue;
88428
89228
  seen.add(canonical);
88429
89229
  const before = files.length;
88430
- add(join19(child, ".gitignore"));
89230
+ add(join20(child, ".gitignore"));
88431
89231
  if (files.length !== before) currentPolicy = createProjectPathPolicy({ ignoreFiles: files, privatePaths: [] });
88432
89232
  directories.push(child);
88433
89233
  }
@@ -88440,10 +89240,10 @@ function updateStickyPrivatePaths(sourceRoot, additions, stateDirectory) {
88440
89240
  throw new Error("Project path policy state must stay outside the source root");
88441
89241
  }
88442
89242
  const key = sha2563(realpathSync3(sourceRoot));
88443
- const path2 = join19(stateRoot, `${key}.json`);
89243
+ const path2 = join20(stateRoot, `${key}.json`);
88444
89244
  if (additions.length === 0 && !existsSync18(path2)) return [];
88445
89245
  preparePrivateDirectory(stateRoot);
88446
- const lock = join19(stateRoot, `${key}.lock`);
89246
+ const lock = join20(stateRoot, `${key}.lock`);
88447
89247
  try {
88448
89248
  mkdirSync14(lock, { mode: 448 });
88449
89249
  } catch (error) {
@@ -88530,7 +89330,7 @@ function isInside2(root, candidate) {
88530
89330
  return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
88531
89331
  }
88532
89332
  function sha2563(value) {
88533
- return createHash23("sha256").update(value).digest("hex");
89333
+ return createHash24("sha256").update(value).digest("hex");
88534
89334
  }
88535
89335
  function stableJson2(value) {
88536
89336
  return JSON.stringify(value, (_key, item) => {
@@ -88540,7 +89340,7 @@ function stableJson2(value) {
88540
89340
  }
88541
89341
 
88542
89342
  // src/remoteFileWrites.ts
88543
- import { createHash as createHash24, randomBytes as randomBytes13 } from "crypto";
89343
+ import { createHash as createHash25, randomBytes as randomBytes13 } from "crypto";
88544
89344
  import {
88545
89345
  chmodSync as chmodSync10,
88546
89346
  closeSync as closeSync7,
@@ -88560,7 +89360,7 @@ import {
88560
89360
  writeFileSync as writeFileSync10
88561
89361
  } from "fs";
88562
89362
  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";
89363
+ import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute6, join as join22, relative as relative5, resolve as resolve13 } from "path";
88564
89364
  import lockfile7 from "proper-lockfile";
88565
89365
 
88566
89366
  // src/projectFileRisk.ts
@@ -88632,7 +89432,7 @@ function matchesAdditionalPrivatePattern(path2, patterns) {
88632
89432
  // src/remoteCommandSourceLock.ts
88633
89433
  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
89434
  import { tmpdir as tmpdir4 } from "os";
88635
- import { join as join20 } from "path";
89435
+ import { join as join21 } from "path";
88636
89436
  import { spawn as spawn4, spawnSync as spawnSync2 } from "child_process";
88637
89437
  var RemoteProjectSourceLockError = class extends Error {
88638
89438
  constructor(message) {
@@ -88683,15 +89483,15 @@ async function acquireRemoteProjectFileEditLock(sourceRoot, options = {}) {
88683
89483
  let abortListener = null;
88684
89484
  await new Promise((resolvePromise, reject) => {
88685
89485
  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()}`);
89486
+ const onError = (error) => fail4(`Failed to start Project lock holder: ${error.message}`);
89487
+ const onExit = (code, signal) => fail4(`Project lock holder exited before acquisition (${signal ?? code}): ${stderr2.trim()}`);
88688
89488
  const cleanup = () => {
88689
89489
  if (timer) clearTimeout(timer);
88690
89490
  if (abortListener && options.signal) options.signal.removeEventListener("abort", abortListener);
88691
89491
  child.removeListener("error", onError);
88692
89492
  child.removeListener("exit", onExit);
88693
89493
  };
88694
- const fail3 = (message) => {
89494
+ const fail4 = (message) => {
88695
89495
  cleanup();
88696
89496
  child.kill("SIGKILL");
88697
89497
  reject(new RemoteProjectSourceLockError(message));
@@ -88702,13 +89502,13 @@ async function acquireRemoteProjectFileEditLock(sourceRoot, options = {}) {
88702
89502
  child.once("error", onError);
88703
89503
  child.once("exit", onExit);
88704
89504
  child.stdout.once("data", (chunk) => {
88705
- if (chunk[0] !== LOCK_READY_BYTE) return fail3("Project lock holder returned an invalid readiness signal");
89505
+ if (chunk[0] !== LOCK_READY_BYTE) return fail4("Project lock holder returned an invalid readiness signal");
88706
89506
  cleanup();
88707
89507
  resolvePromise();
88708
89508
  });
88709
- timer = setTimeout(() => fail3(`Timed out waiting ${timeoutMs}ms for the Project file-edit lock`), timeoutMs);
89509
+ timer = setTimeout(() => fail4(`Timed out waiting ${timeoutMs}ms for the Project file-edit lock`), timeoutMs);
88710
89510
  if (options.signal) {
88711
- abortListener = () => fail3("Project lock acquisition was canceled");
89511
+ abortListener = () => fail4("Project lock acquisition was canceled");
88712
89512
  options.signal.addEventListener("abort", abortListener, { once: true });
88713
89513
  }
88714
89514
  });
@@ -88769,9 +89569,9 @@ function remoteProjectSourceLockPath(sourceRoot) {
88769
89569
  if (!stat3.isDirectory()) throw new RemoteProjectSourceLockError("Project source root must be a directory");
88770
89570
  const uid = typeof process.getuid === "function" ? process.getuid() : null;
88771
89571
  if (uid === null) throw new RemoteProjectSourceLockError("Project source locks require a numeric user identity");
88772
- const directory = join20(tmpdir4(), `openmates-project-source-locks-${uid}`);
89572
+ const directory = join21(tmpdir4(), `openmates-project-source-locks-${uid}`);
88773
89573
  preparePrivateDirectory2(directory, uid);
88774
- const lockPath = join20(directory, "source-writes.lock");
89574
+ const lockPath = join21(directory, "source-writes.lock");
88775
89575
  const descriptor = openSync6(lockPath, constants4.O_CREAT | constants4.O_RDWR | constants4.O_NOFOLLOW, 384);
88776
89576
  closeSync6(descriptor);
88777
89577
  const lockStat = lstatSync5(lockPath);
@@ -88802,10 +89602,10 @@ var RemoteFileMutationError = class extends Error {
88802
89602
  }
88803
89603
  };
88804
89604
  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"}`);
89605
+ var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
89606
+ var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
88807
89607
  function sha256ProjectFile(content) {
88808
- return createHash24("sha256").update(content).digest("hex");
89608
+ return createHash25("sha256").update(content).digest("hex");
88809
89609
  }
88810
89610
  async function executeRemoteFileMutation(options) {
88811
89611
  const mutation = validateMutation(options.mutation);
@@ -88820,7 +89620,7 @@ async function executeRemoteFileMutation(options) {
88820
89620
  }
88821
89621
  const scopeDigest = sha256ProjectFile(options.operationScope);
88822
89622
  const payloadDigest = sha256ProjectFile(stableJson3(mutation));
88823
- const journalPath = join21(journalRoot, scopeDigest, `${sha256ProjectFile(`${options.operationScope}\0${mutation.operation_id}`)}.json`);
89623
+ const journalPath = join22(journalRoot, scopeDigest, `${sha256ProjectFile(`${options.operationScope}\0${mutation.operation_id}`)}.json`);
88824
89624
  preparePrivateDirectory3(dirname12(journalPath));
88825
89625
  await options.authorize();
88826
89626
  const existingJournal = readJournal(journalPath);
@@ -88985,7 +89785,7 @@ function openPinnedParent(root, path2) {
88985
89785
  function readPinnedFile(parent) {
88986
89786
  let descriptor;
88987
89787
  try {
88988
- descriptor = openSync7(join21(parent.procPath, parent.targetName), constants5.O_RDONLY | constants5.O_NOFOLLOW);
89788
+ descriptor = openSync7(join22(parent.procPath, parent.targetName), constants5.O_RDONLY | constants5.O_NOFOLLOW);
88989
89789
  } catch (error) {
88990
89790
  if (error.code === "ENOENT") return null;
88991
89791
  if (error.code === "ELOOP") {
@@ -89058,8 +89858,8 @@ function assertPreviewStillCurrent(mutation, before, current) {
89058
89858
  }
89059
89859
  function createPinnedFile(parent, content) {
89060
89860
  const temp = temporaryName();
89061
- const tempPath = join21(parent.procPath, temp);
89062
- const targetPath = join21(parent.procPath, parent.targetName);
89861
+ const tempPath = join22(parent.procPath, temp);
89862
+ const targetPath = join22(parent.procPath, parent.targetName);
89063
89863
  let descriptor;
89064
89864
  try {
89065
89865
  descriptor = openSync7(tempPath, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 438);
@@ -89083,7 +89883,7 @@ function createPinnedFile(parent, content) {
89083
89883
  }
89084
89884
  }
89085
89885
  function replacePinnedFile(parent, expected, content) {
89086
- const tempPath = join21(parent.procPath, temporaryName());
89886
+ const tempPath = join22(parent.procPath, temporaryName());
89087
89887
  let descriptor;
89088
89888
  try {
89089
89889
  descriptor = openSync7(tempPath, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, expected.mode);
@@ -89096,7 +89896,7 @@ function replacePinnedFile(parent, expected, content) {
89096
89896
  if (!nearWrite || nearWrite.hash !== expected.hash || nearWrite.dev !== expected.dev || nearWrite.ino !== expected.ino) {
89097
89897
  throw new RemoteFileMutationError("file_changed", "Project file changed at the final write boundary");
89098
89898
  }
89099
- renameSync8(tempPath, join21(parent.procPath, parent.targetName));
89899
+ renameSync8(tempPath, join22(parent.procPath, parent.targetName));
89100
89900
  fsyncSync3(parent.descriptor);
89101
89901
  } catch (error) {
89102
89902
  if (descriptor !== void 0) closeSync7(descriptor);
@@ -89123,7 +89923,7 @@ ${body}${hasFinalNewline ? "" : "\\n"}`;
89123
89923
  }
89124
89924
  async function acquirePathLock(parentIdentity, path2) {
89125
89925
  preparePrivateDirectory3(LOCK_ROOT);
89126
- const target = join21(LOCK_ROOT, sha256ProjectFile(`${parentIdentity}\0${basename5(path2)}`));
89926
+ const target = join22(LOCK_ROOT, sha256ProjectFile(`${parentIdentity}\0${basename5(path2)}`));
89127
89927
  preparePrivateDirectory3(target);
89128
89928
  try {
89129
89929
  return await lockfile7.lock(target, {
@@ -89493,13 +90293,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
89493
90293
  import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
89494
90294
 
89495
90295
  // src/remoteCommandRuntime.ts
89496
- import { createHash as createHash26, randomUUID as randomUUID21 } from "crypto";
90296
+ import { createHash as createHash27, randomUUID as randomUUID21 } from "crypto";
89497
90297
  import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
89498
90298
  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";
90299
+ import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
89500
90300
 
89501
90301
  // src/remoteCommandAppArmor.ts
89502
- import { createHash as createHash25 } from "crypto";
90302
+ import { createHash as createHash26 } from "crypto";
89503
90303
  import { spawn as spawn5 } from "child_process";
89504
90304
  import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
89505
90305
  import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
@@ -89716,7 +90516,7 @@ function hasControl2(value) {
89716
90516
  });
89717
90517
  }
89718
90518
  function sha2564(value) {
89719
- return createHash25("sha256").update(value).digest("hex");
90519
+ return createHash26("sha256").update(value).digest("hex");
89720
90520
  }
89721
90521
 
89722
90522
  // src/remoteCommandRuntime.ts
@@ -90142,7 +90942,7 @@ function createRemoteCommandPreflight(request) {
90142
90942
  error instanceof Error ? error.message : "Project source root boundary cannot be established"
90143
90943
  );
90144
90944
  }
90145
- const cwd = request.policy.cwd === "." ? root : realpath(join22(root, request.policy.cwd));
90945
+ const cwd = request.policy.cwd === "." ? root : realpath(join23(root, request.policy.cwd));
90146
90946
  if (!isInside5(root, cwd) || !statSync6(cwd).isDirectory()) throw new RemoteCommandError("invalid_request", "Command cwd escapes the Project root");
90147
90947
  const canonicalPolicy = { ...request.policy, argv: [...request.policy.argv], cwd: relative7(root, cwd).split(sep).join("/") || "." };
90148
90948
  const toolchains = request.toolchain_paths.map(canonicalDirectory);
@@ -90260,7 +91060,7 @@ function prepareSandboxLaunch(preflight, authority, capability, network, appArmo
90260
91060
  args.push(preflight.policy.source_access === "read_write" ? "--bind" : "--ro-bind", preflight.source_root, "/project");
90261
91061
  if (preflight.policy.source_access === "read_write") {
90262
91062
  for (const protectedPath of preflight.protected_project_paths) {
90263
- args.push("--ro-bind", join22(preflight.source_root, protectedPath.path), `/project/${protectedPath.path}`);
91063
+ args.push("--ro-bind", join23(preflight.source_root, protectedPath.path), `/project/${protectedPath.path}`);
90264
91064
  }
90265
91065
  }
90266
91066
  for (const masked of preflight.masked_project_paths) {
@@ -90394,13 +91194,13 @@ function discoverProjectPathProtection(root, policy) {
90394
91194
  try {
90395
91195
  while (directories.length) {
90396
91196
  const parent = directories.pop();
90397
- for (const entry of readdirSync12(join22(root, parent), { withFileTypes: true })) {
91197
+ for (const entry of readdirSync12(join23(root, parent), { withFileTypes: true })) {
90398
91198
  visited += 1;
90399
91199
  if (visited > MAX_PROJECT_MASK_SCAN_ENTRIES) {
90400
91200
  throw new RemoteCommandError("confinement_unavailable", `Project credential scan exceeds ${MAX_PROJECT_MASK_SCAN_ENTRIES} entries`);
90401
91201
  }
90402
91202
  const path2 = parent ? `${parent}/${entry.name}` : entry.name;
90403
- const fullPath = join22(root, path2);
91203
+ const fullPath = join23(root, path2);
90404
91204
  const stat3 = lstatSync9(fullPath);
90405
91205
  const kind = entry.isDirectory() ? "directory" : "file";
90406
91206
  let masked = policy ? policy.isPrivate(path2, kind === "directory") || path2.toLowerCase() === ".git/config" : classifyProjectFileReadRisk(path2).isHighRisk || path2.toLowerCase() === ".git/config";
@@ -90557,7 +91357,7 @@ function stableJson4(value) {
90557
91357
  return JSON.stringify(value);
90558
91358
  }
90559
91359
  function sha2565(value) {
90560
- return createHash26("sha256").update(value).digest("hex");
91360
+ return createHash27("sha256").update(value).digest("hex");
90561
91361
  }
90562
91362
  function deepFreeze(value) {
90563
91363
  if (value && typeof value === "object") {
@@ -90572,7 +91372,7 @@ import { lookup as dnsLookup } from "dns/promises";
90572
91372
  import { chmodSync as chmodSync11, mkdtempSync as mkdtempSync4, realpathSync as realpathSync8, rmSync as rmSync10, statSync as statSync7, writeFileSync as writeFileSync11 } from "fs";
90573
91373
  import { isIP as isIP2, createConnection, createServer } from "net";
90574
91374
  import { tmpdir as tmpdir6 } from "os";
90575
- import { isAbsolute as isAbsolute9, join as join23, relative as relative8 } from "path";
91375
+ import { isAbsolute as isAbsolute9, join as join24, relative as relative8 } from "path";
90576
91376
  var DEFAULT_BRIDGE_PORT = 43117;
90577
91377
  var MAX_CONNECT_HEADER_BYTES = 8 * 1024;
90578
91378
  var CONNECT_HEADER_TIMEOUT_MS = 1e4;
@@ -90648,8 +91448,8 @@ async function prepareRemoteHttpsConnectNetworkConfinement(claim, preflight, opt
90648
91448
  throw capabilityError(`HTTPS network profiles require the trusted Node runtime in an approved toolchain (${nodeExecutable})`);
90649
91449
  }
90650
91450
  const controlDirectory = createPrivateControlDirectory(preflight, options.controlRoots);
90651
- const socketPath = join23(controlDirectory, "proxy.sock");
90652
- const bridgePath = join23(controlDirectory, "bridge.mjs");
91451
+ const socketPath = join24(controlDirectory, "proxy.sock");
91452
+ const bridgePath = join24(controlDirectory, "bridge.mjs");
90653
91453
  writeFileSync11(bridgePath, BRIDGE_SOURCE, { encoding: "utf8", mode: 320, flag: "wx" });
90654
91454
  const activeSockets = /* @__PURE__ */ new Set();
90655
91455
  const server2 = createServer((client2) => {
@@ -90778,7 +91578,7 @@ function createPrivateControlDirectory(preflight, configuredRoots) {
90778
91578
  try {
90779
91579
  if (!isAbsolute9(candidate) || !statSync7(candidate).isDirectory()) continue;
90780
91580
  const root = realpathSync8(candidate);
90781
- const directory = mkdtempSync4(join23(root, "openmates-command-network-"));
91581
+ const directory = mkdtempSync4(join24(root, "openmates-command-network-"));
90782
91582
  chmodSync11(directory, 448);
90783
91583
  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
91584
  if (!exposed) return directory;
@@ -90835,9 +91635,9 @@ function capabilityError(message) {
90835
91635
  }
90836
91636
 
90837
91637
  // src/remoteCommandResourceGrants.ts
90838
- import { createHash as createHash27 } from "crypto";
91638
+ import { createHash as createHash28 } from "crypto";
90839
91639
  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";
91640
+ import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
90841
91641
  var STORE_FILE = "remote-command-resource-grants.json";
90842
91642
  var ID = /^[a-z][a-z0-9-]{0,63}$/;
90843
91643
  var ENV = /^[A-Z_][A-Z0-9_]{0,127}$/;
@@ -90930,7 +91730,7 @@ function resolveRemoteCommandResourceGrants(context) {
90930
91730
  return { writable_targets, network_profiles, credential_profiles };
90931
91731
  }
90932
91732
  function definitionDigest(kind, definition) {
90933
- return createHash27("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
91733
+ return createHash28("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
90934
91734
  }
90935
91735
  function upsert(grant) {
90936
91736
  validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
@@ -90938,7 +91738,7 @@ function upsert(grant) {
90938
91738
  writeStore([...others, grant]);
90939
91739
  }
90940
91740
  function readStore() {
90941
- const path2 = join24(resolveStateDir(), STORE_FILE);
91741
+ const path2 = join25(resolveStateDir(), STORE_FILE);
90942
91742
  if (!existsSync22(path2)) return [];
90943
91743
  const stat3 = lstatSync10(path2);
90944
91744
  if (!stat3.isFile() || stat3.isSymbolicLink() || (stat3.mode & 63) !== 0) throw new Error("Remote command resource grant store is not private");
@@ -90950,7 +91750,7 @@ function writeStore(grants) {
90950
91750
  const directory = resolveStateDir();
90951
91751
  mkdirSync18(directory, { recursive: true, mode: 448 });
90952
91752
  chmodSync12(directory, 448);
90953
- const path2 = join24(directory, STORE_FILE);
91753
+ const path2 = join25(directory, STORE_FILE);
90954
91754
  if (existsSync22(path2) && lstatSync10(path2).isSymbolicLink()) throw new Error("Remote command resource grant store cannot be a symbolic link");
90955
91755
  const temporary = `${path2}.${process.pid}.tmp`;
90956
91756
  writeFileSync12(temporary, `${JSON.stringify({ version: 1, grants }, null, 2)}
@@ -91933,7 +92733,7 @@ function discoverRemoteAccessRepositories(roots) {
91933
92733
  }
91934
92734
  for (const entry of entries) {
91935
92735
  if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
91936
- visit(join25(directory, entry.name), approvedRoot);
92736
+ visit(join26(directory, entry.name), approvedRoot);
91937
92737
  }
91938
92738
  };
91939
92739
  for (const root of roots) visit(root, root);
@@ -91951,7 +92751,7 @@ function remoteAccessHostingCandidates(roots, explicitPath, discover = discoverR
91951
92751
  };
91952
92752
  }
91953
92753
  function remoteAccessSourceType(rootPath) {
91954
- const gitMarker = join25(rootPath, ".git");
92754
+ const gitMarker = join26(rootPath, ".git");
91955
92755
  return existsSync24(gitMarker) ? "local_git_repository" : "local_folder";
91956
92756
  }
91957
92757
  function listRemoteAccessDirectory(options) {
@@ -92008,7 +92808,7 @@ function listRemoteAccessDirectory(options) {
92008
92808
  remainingChildSummaryEntries -= 1;
92009
92809
  const childPath = `${folderPath}/${child.name}`;
92010
92810
  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;
92811
+ const fileStat = child.isFile() ? lstatSync11(join26(absoluteFolder, child.name)) : null;
92012
92812
  if (fileStat && fileStat.nlink > 1) continue;
92013
92813
  if (child.isDirectory()) childFolderCount += 1;
92014
92814
  else {
@@ -92029,7 +92829,7 @@ function listRemoteAccessDirectory(options) {
92029
92829
  const compareNames = (a, b) => a.localeCompare(b) || (a < b ? -1 : a > b ? 1 : 0);
92030
92830
  for (const entry of readdirSync13(directory, { withFileTypes: true }).sort((a, b) => compareNames(a.name, b.name))) {
92031
92831
  if (cursor !== void 0 && compareNames(entry.name, cursor) <= 0) continue;
92032
- const entryPath = relative11(root, join25(directory, entry.name)).replace(/\\/g, "/");
92832
+ const entryPath = relative11(root, join26(directory, entry.name)).replace(/\\/g, "/");
92033
92833
  if (entry.name.startsWith(".") || entry.isSymbolicLink() || policy.isIgnored(entryPath, entry.isDirectory()) || policy.isPrivate(entryPath, entry.isDirectory())) {
92034
92834
  excluded += 1;
92035
92835
  continue;
@@ -92038,7 +92838,7 @@ function listRemoteAccessDirectory(options) {
92038
92838
  excluded += 1;
92039
92839
  continue;
92040
92840
  }
92041
- const fileStat = entry.isFile() ? lstatSync11(join25(directory, entry.name)) : void 0;
92841
+ const fileStat = entry.isFile() ? lstatSync11(join26(directory, entry.name)) : void 0;
92042
92842
  if (fileStat && fileStat.nlink > 1) {
92043
92843
  excluded += 1;
92044
92844
  continue;
@@ -92100,7 +92900,7 @@ function readRemoteAccessTextFile(options) {
92100
92900
  truncated,
92101
92901
  sizeBytes: stats.size,
92102
92902
  lineCount: countTextLines(content),
92103
- expected_base: truncated ? null : createHash28("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
92903
+ expected_base: truncated ? null : createHash29("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
92104
92904
  };
92105
92905
  } catch (error) {
92106
92906
  const code = error.code;
@@ -92150,7 +92950,7 @@ function readRemoteAccessImageChunk(options) {
92150
92950
  mime_type: mimeType,
92151
92951
  size_bytes: stats.size,
92152
92952
  offset: options.offset,
92153
- content_hash: createHash28("sha256").update(bytes2).digest("hex")
92953
+ content_hash: createHash29("sha256").update(bytes2).digest("hex")
92154
92954
  };
92155
92955
  } catch (error) {
92156
92956
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -92198,8 +92998,8 @@ function readRemoteAccessFileChunk(options) {
92198
92998
  content_base64: bytes2.toString("base64"),
92199
92999
  size_bytes: stats.size,
92200
93000
  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")
93001
+ file_identity: createHash29("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
93002
+ chunk_hash: createHash29("sha256").update(bytes2).digest("hex")
92203
93003
  };
92204
93004
  } catch (error) {
92205
93005
  if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
@@ -92307,13 +93107,13 @@ async function runRemoteAccessBridge(options) {
92307
93107
  for (const binding of options.bindings) {
92308
93108
  const projectId = binding.source.projectId;
92309
93109
  if (caches.has(projectId)) continue;
92310
- const cache = new ProjectTaskCache(options.taskCacheRoot ?? join25(resolveStateDir(), "project-task-cache"), accountScope, projectId);
93110
+ const cache = new ProjectTaskCache(options.taskCacheRoot ?? join26(resolveStateDir(), "project-task-cache"), accountScope, projectId);
92311
93111
  releases.push(await lockfile8.lock(cache.directory, { retries: 0, stale: 12e4 }));
92312
93112
  caches.set(projectId, cache);
92313
93113
  }
92314
93114
  codexBridges = startCodexTaskBridges(
92315
93115
  options.bindings.map((item) => item.source.rootPath),
92316
- [...caches.values()].map((cache) => join25(cache.directory, "snapshot.json")),
93116
+ [...caches.values()].map((cache) => join26(cache.directory, "snapshot.json")),
92317
93117
  resolveStateDir(),
92318
93118
  (status) => options.onTaskSync?.({ projectId: "", directory: "", status })
92319
93119
  );
@@ -92672,7 +93472,7 @@ function waitForAbort2(signal, timeoutMs) {
92672
93472
  }
92673
93473
  function resolveRemoteCachePath(sourceId, homeDirectory) {
92674
93474
  assertSafeSourceId(sourceId);
92675
- return join25(remoteAccessStateDirectory(homeDirectory), "remote-cache", sourceId);
93475
+ return join26(remoteAccessStateDirectory(homeDirectory), "remote-cache", sourceId);
92676
93476
  }
92677
93477
  function startRemoteAccessSource(input) {
92678
93478
  assertSafeSourceId(input.sourceId);
@@ -92822,7 +93622,7 @@ function searchRemoteSourceWithoutRg(request, sourceRoot, targetPath, policy) {
92822
93622
  continue;
92823
93623
  }
92824
93624
  for (const entry of entries) {
92825
- const absolutePath = join25(directory, entry.name);
93625
+ const absolutePath = join26(directory, entry.name);
92826
93626
  const relativePath = relative11(root, absolutePath).replace(/\\/g, "/");
92827
93627
  if (entry.name === ".git" || entry.isSymbolicLink() || policy.isIgnored(relativePath, entry.isDirectory()) || policy.isPrivate(relativePath, entry.isDirectory())) {
92828
93628
  excluded += 1;
@@ -92952,15 +93752,15 @@ function searchRemoteFileNamesWithoutRg(request, sourceRoot, targetPath, policy,
92952
93752
  enumerationTruncated = true;
92953
93753
  break;
92954
93754
  }
92955
- const path2 = relative11(sourceRoot, join25(absolute, entry.name)).replace(/\\/g, "/");
93755
+ const path2 = relative11(sourceRoot, join26(absolute, entry.name)).replace(/\\/g, "/");
92956
93756
  const isDirectory = entry.isDirectory();
92957
- if (skipPriority && priorityAbsolute === join25(absolute, entry.name)) continue;
93757
+ if (skipPriority && priorityAbsolute === join26(absolute, entry.name)) continue;
92958
93758
  if (entry.name.startsWith(".") || entry.isSymbolicLink() || !entry.isFile() && !isDirectory || policy.isIgnored(path2, isDirectory) || policy.isPrivate(path2, isDirectory)) {
92959
93759
  excluded += 1;
92960
93760
  continue;
92961
93761
  }
92962
- if (isDirectory) pending.push(join25(absolute, entry.name));
92963
- else if (lstatSync11(join25(absolute, entry.name)).nlink > 1) {
93762
+ if (isDirectory) pending.push(join26(absolute, entry.name));
93763
+ else if (lstatSync11(join26(absolute, entry.name)).nlink > 1) {
92964
93764
  excluded += 1;
92965
93765
  continue;
92966
93766
  }
@@ -93165,7 +93965,7 @@ function collectSearchPathMetadata(sourceRoot, targetPath, policy, collectDirect
93165
93965
  throw new Error("Remote source search alias scan exceeded its bounded file limit");
93166
93966
  }
93167
93967
  if (entry.name === ".git" || entry.isSymbolicLink()) continue;
93168
- const absolute = join25(directory, entry.name);
93968
+ const absolute = join26(directory, entry.name);
93169
93969
  const path2 = relative11(sourceRoot, absolute).replace(/\\/g, "/");
93170
93970
  if (entry.isDirectory()) {
93171
93971
  if (!policy.isIgnored(path2, true) && !policy.isPrivate(path2, true) && (!collectDirectories || !entry.name.startsWith("."))) {
@@ -93242,7 +94042,7 @@ function resolveApprovedPath(sourceRoot, relativePath) {
93242
94042
  const relation = relative11(sourceRoot, lexical);
93243
94043
  let current = sourceRoot;
93244
94044
  for (const part of relation.split(/[\\/]/).filter(Boolean)) {
93245
- current = join25(current, part);
94045
+ current = join26(current, part);
93246
94046
  if (lstatSync11(current).isSymbolicLink()) throw new Error("Remote source path is a symbolic link");
93247
94047
  }
93248
94048
  const canonical = realpathSync11(lexical);
@@ -93306,7 +94106,7 @@ function remoteAccessStateDirectory(homeDirectory) {
93306
94106
  return homeDirectory === void 0 ? resolveStateDir() : resolveStateDir({ homeDir: homeDirectory, stateDir: "", profile: "" });
93307
94107
  }
93308
94108
  function remoteAccessStorePath(homeDirectory) {
93309
- return join25(remoteAccessStateDirectory(homeDirectory), "remote-sources.json");
94109
+ return join26(remoteAccessStateDirectory(homeDirectory), "remote-sources.json");
93310
94110
  }
93311
94111
  function saveRemoteAccessSources(sources, homeDirectory) {
93312
94112
  const filePath = remoteAccessStorePath(homeDirectory);
@@ -93390,11 +94190,11 @@ function inheritedInstaller(executable, args) {
93390
94190
  import { spawnSync as spawnSync5 } from "child_process";
93391
94191
  import { readFileSync as readFileSync22, mkdirSync as mkdirSync20, writeFileSync as writeFileSync14, renameSync as renameSync10 } from "fs";
93392
94192
  import { homedir as homedir7 } from "os";
93393
- import { dirname as dirname14, basename as basename8, join as join26 } from "path";
94193
+ import { dirname as dirname14, basename as basename8, join as join27 } from "path";
93394
94194
  import { fileURLToPath as fileURLToPath3 } from "url";
93395
94195
  var PACKAGE_NAME = "openmates";
93396
94196
  function channelFile() {
93397
- return join26(homedir7(), ".openmates", "updates.json");
94197
+ return join27(homedir7(), ".openmates", "updates.json");
93398
94198
  }
93399
94199
  function parseChannel(value) {
93400
94200
  if (value === "dev") return "dev";
@@ -93764,7 +94564,7 @@ import { execFileSync as execFileSync6 } from "child_process";
93764
94564
  import { createSign, randomUUID as randomUUID22 } from "crypto";
93765
94565
  import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
93766
94566
  import { homedir as homedir8 } from "os";
93767
- import { join as join27, resolve as resolve17 } from "path";
94567
+ import { join as join28, resolve as resolve17 } from "path";
93768
94568
  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
94569
  var REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI = "https://app.dev.openmates.org/oauth/revolut-business/callback";
93770
94570
  var REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI = "https://openmates.org/oauth/revolut-business/callback";
@@ -93774,7 +94574,7 @@ var REVOLUT_BUSINESS_AUDIENCE = "https://revolut.com";
93774
94574
  var CERTIFICATE_DAYS = "1825";
93775
94575
  var RSA_BITS = "2048";
93776
94576
  function defaultRevolutBusinessCertificateDir(environment) {
93777
- return join27(homedir8(), ".openmates", "revolut-business", environment);
94577
+ return join28(homedir8(), ".openmates", "revolut-business", environment);
93778
94578
  }
93779
94579
  function defaultRevolutBusinessRedirectUri(environment) {
93780
94580
  return environment === "sandbox" ? REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI : REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI;
@@ -93797,7 +94597,7 @@ async function exchangeRevolutBusinessAuthorizationCode(options) {
93797
94597
  if (!clientId) throw new Error("Missing Revolut Business client ID.");
93798
94598
  const code = extractRevolutBusinessAuthorizationCode(options.codeOrRedirectUrl);
93799
94599
  if (!code) throw new Error("Missing Revolut Business authorization code.");
93800
- const privateKeyPem = options.privateKeyPem ?? readFileSync23(options.privateKeyPath ?? join27(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
94600
+ const privateKeyPem = options.privateKeyPem ?? readFileSync23(options.privateKeyPath ?? join28(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
93801
94601
  const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
93802
94602
  const clientAssertion = generateRevolutBusinessClientAssertion({ clientId, privateKeyPem, redirectUri });
93803
94603
  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 +94652,8 @@ function generateRevolutBusinessCertificate(options = {}) {
93852
94652
  if (!redirectUri.startsWith("https://")) throw new Error("Revolut OAuth redirect URI must start with https://.");
93853
94653
  mkdirSync22(outputDir, { recursive: true, mode: 448 });
93854
94654
  chmodSync14(outputDir, 448);
93855
- const privateKeyPath = join27(outputDir, "privatecert.pem");
93856
- const publicCertificatePath = join27(outputDir, "publiccert.cer");
94655
+ const privateKeyPath = join28(outputDir, "privatecert.pem");
94656
+ const publicCertificatePath = join28(outputDir, "publiccert.cer");
93857
94657
  if (!options.overwrite && (existsSync25(privateKeyPath) || existsSync25(publicCertificatePath))) {
93858
94658
  throw new Error(`Revolut certificate files already exist in ${outputDir}. Re-run with --overwrite to replace them.`);
93859
94659
  }
@@ -95548,7 +96348,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
95548
96348
  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
96349
  if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
95550
96350
  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() });
96351
+ 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
96352
  const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
95553
96353
  if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
95554
96354
  else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
@@ -96468,7 +97268,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
96468
97268
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
96469
97269
  key_wrappers: wrapping.teamId ? [{
96470
97270
  key_type: "team",
96471
- hashed_team_id: createHash29("sha256").update(wrapping.teamId).digest("hex"),
97271
+ hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
96472
97272
  team_key_epoch: 1,
96473
97273
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
96474
97274
  wrapper_version: 1,
@@ -97844,7 +98644,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
97844
98644
  encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
97845
98645
  key_wrappers: wrapping.teamId ? [{
97846
98646
  key_type: "team",
97847
- hashed_team_id: createHash29("sha256").update(wrapping.teamId).digest("hex"),
98647
+ hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
97848
98648
  team_key_epoch: 1,
97849
98649
  encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
97850
98650
  wrapper_version: 1,
@@ -97918,7 +98718,7 @@ function readRootProjectInstructions(rootPath) {
97918
98718
  return left.localeCompare(right);
97919
98719
  });
97920
98720
  const source = matchingNames[0];
97921
- if (source) return { instructions: readFileSync24(join28(rootPath, source), "utf8"), source };
98721
+ if (source) return { instructions: readFileSync24(join29(rootPath, source), "utf8"), source };
97922
98722
  }
97923
98723
  return null;
97924
98724
  }
@@ -98817,31 +99617,31 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
98817
99617
  } catch {
98818
99618
  }
98819
99619
  }
98820
- const { mkdir: mkdir3, writeFile: writeFile2 } = await import("fs/promises");
98821
- const { join: join29 } = await import("path");
99620
+ const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
99621
+ const { join: join30 } = await import("path");
98822
99622
  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);
99623
+ const tmpDir = join30(outputDir, `.${filenameBase}_tmp`);
99624
+ await mkdir4(tmpDir, { recursive: true });
99625
+ await writeFile3(join30(tmpDir, `${filenameBase}.yml`), yamlContent);
99626
+ await writeFile3(join30(tmpDir, `${filenameBase}.md`), mdContent);
98827
99627
  if (codeEmbeds.length > 0) {
98828
99628
  for (const ce of codeEmbeds) {
98829
99629
  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("/")), {
99630
+ const fullPath = join30(tmpDir, "code", fpath);
99631
+ await mkdir4(fullPath.substring(0, fullPath.lastIndexOf("/")), {
98832
99632
  recursive: true
98833
99633
  });
98834
- await writeFile2(fullPath, ce.content);
99634
+ await writeFile3(fullPath, ce.content);
98835
99635
  }
98836
99636
  }
98837
99637
  if (transcriptEmbeds.length > 0) {
98838
- const tDir = join29(tmpDir, "transcripts");
98839
- await mkdir3(tDir, { recursive: true });
99638
+ const tDir = join30(tmpDir, "transcripts");
99639
+ await mkdir4(tDir, { recursive: true });
98840
99640
  for (const te of transcriptEmbeds) {
98841
- await writeFile2(join29(tDir, te.filename), te.content);
99641
+ await writeFile3(join30(tDir, te.filename), te.content);
98842
99642
  }
98843
99643
  }
98844
- const zipPath = join29(outputDir, `${filenameBase}.zip`);
99644
+ const zipPath = join30(outputDir, `${filenameBase}.zip`);
98845
99645
  const { execSync: execSync2 } = await import("child_process");
98846
99646
  try {
98847
99647
  execSync2(`cd "${tmpDir}" && zip -r "${zipPath}" .`, { stdio: "pipe" });
@@ -98856,29 +99656,29 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
98856
99656
  );
98857
99657
  }
98858
99658
  } else {
98859
- const chatDir = join29(outputDir, filenameBase);
98860
- await mkdir3(chatDir, { recursive: true });
99659
+ const chatDir = join30(outputDir, filenameBase);
99660
+ await mkdir4(chatDir, { recursive: true });
98861
99661
  const written = [];
98862
- await writeFile2(join29(chatDir, `${filenameBase}.yml`), yamlContent);
99662
+ await writeFile3(join30(chatDir, `${filenameBase}.yml`), yamlContent);
98863
99663
  written.push(`${filenameBase}.yml`);
98864
- await writeFile2(join29(chatDir, `${filenameBase}.md`), mdContent);
99664
+ await writeFile3(join30(chatDir, `${filenameBase}.md`), mdContent);
98865
99665
  written.push(`${filenameBase}.md`);
98866
99666
  if (codeEmbeds.length > 0) {
98867
99667
  for (const ce of codeEmbeds) {
98868
99668
  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("/")), {
99669
+ const fullPath = join30(chatDir, "code", fpath);
99670
+ await mkdir4(fullPath.substring(0, fullPath.lastIndexOf("/")), {
98871
99671
  recursive: true
98872
99672
  });
98873
- await writeFile2(fullPath, ce.content);
99673
+ await writeFile3(fullPath, ce.content);
98874
99674
  written.push(`code/${fpath}`);
98875
99675
  }
98876
99676
  }
98877
99677
  if (transcriptEmbeds.length > 0) {
98878
- const tDir = join29(chatDir, "transcripts");
98879
- await mkdir3(tDir, { recursive: true });
99678
+ const tDir = join30(chatDir, "transcripts");
99679
+ await mkdir4(tDir, { recursive: true });
98880
99680
  for (const te of transcriptEmbeds) {
98881
- await writeFile2(join29(tDir, te.filename), te.content);
99681
+ await writeFile3(join30(tDir, te.filename), te.content);
98882
99682
  written.push(`transcripts/${te.filename}`);
98883
99683
  }
98884
99684
  }
@@ -98914,7 +99714,7 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
98914
99714
  printJson2({
98915
99715
  chat_id: chat.id,
98916
99716
  title: chat.title,
98917
- output_dir: useZip ? join29(outputDir, `${filenameBase}.zip`) : join29(outputDir, filenameBase),
99717
+ output_dir: useZip ? join30(outputDir, `${filenameBase}.zip`) : join30(outputDir, filenameBase),
98918
99718
  files,
98919
99719
  code_embeds: codeEmbeds.length,
98920
99720
  transcript_embeds: transcriptEmbeds.length
@@ -99235,7 +100035,7 @@ function normalizeApiKeyLoadedChat(payload) {
99235
100035
  const messages = rawMessages.map((message) => {
99236
100036
  const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
99237
100037
  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;
100038
+ const hashedMessageId = messageId ? createHash30("sha256").update(messageId).digest("hex") : null;
99239
100039
  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
100040
  return {
99241
100041
  id: messageId,
@@ -101381,8 +102181,8 @@ function printAccountExportBundle(bundle, flags) {
101381
102181
  }
101382
102182
  var ACCOUNT_IMPORT_SOURCES = /* @__PURE__ */ new Set(["openmates", "chatgpt", "claude", "gemini", "opencode", "other"]);
101383
102183
  async function parseAccountImportFile(parserFormat, file, flags = {}) {
101384
- const { readFile: readFile2 } = await import("fs/promises");
101385
- const payload = await readFile2(file);
102184
+ const { readFile: readFile3 } = await import("fs/promises");
102185
+ const payload = await readFile3(file);
101386
102186
  const requestedSource = typeof flags.source === "string" ? flags.source : parserFormat;
101387
102187
  if (!ACCOUNT_IMPORT_SOURCES.has(requestedSource)) throw new Error(`Unsupported account import source: ${requestedSource}`);
101388
102188
  const selectedSource = requestedSource;
@@ -101762,13 +102562,13 @@ function parseYamlScalar(value) {
101762
102562
  return trimmed;
101763
102563
  }
101764
102564
  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");
102565
+ const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
102566
+ const { join: join30, basename: basename10, dirname: dirname17 } = await import("path");
101767
102567
  const target = typeof output === "string" ? output : ".";
101768
102568
  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);
102569
+ const filePath = target.endsWith(".pdf") ? target : join30(target, filename);
102570
+ await mkdir4(dirname17(filePath), { recursive: true });
102571
+ await writeFile3(filePath, document.data);
101772
102572
  return filePath;
101773
102573
  }
101774
102574
  function printMates(json) {
@@ -102233,7 +103033,7 @@ async function promptPlainText(question) {
102233
103033
  }
102234
103034
  }
102235
103035
  async function writeSecretFile(filePath, content, force = false) {
102236
- const { mkdir: mkdir3, writeFile: writeFile2, stat: stat3 } = await import("fs/promises");
103036
+ const { mkdir: mkdir4, writeFile: writeFile3, stat: stat3 } = await import("fs/promises");
102237
103037
  const { dirname: dirname17 } = await import("path");
102238
103038
  try {
102239
103039
  await stat3(filePath);
@@ -102244,8 +103044,8 @@ async function writeSecretFile(filePath, content, force = false) {
102244
103044
  }
102245
103045
  if (error instanceof Error && !("code" in error)) throw error;
102246
103046
  }
102247
- await mkdir3(dirname17(filePath), { recursive: true });
102248
- await writeFile2(filePath, content, { mode: 384 });
103047
+ await mkdir4(dirname17(filePath), { recursive: true });
103048
+ await writeFile3(filePath, content, { mode: 384 });
102249
103049
  return filePath;
102250
103050
  }
102251
103051
  async function generateProvisioningPassword() {
@@ -102611,8 +103411,8 @@ async function handleSettings(client2, subcommand, rest, flags) {
102611
103411
  if (matches(tokens, ["account", "import-chat"])) {
102612
103412
  const file = rest[1];
102613
103413
  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");
103414
+ const { readFile: readFile3 } = await import("fs/promises");
103415
+ const content = await readFile3(file, "utf-8");
102616
103416
  await printSettingsMutationResult(
102617
103417
  client2.settingsPost("import-chat", parseChatImportPayload(content)),
102618
103418
  flags
@@ -106341,19 +107141,19 @@ async function handleDocs(client2, subcommand, rest, flags) {
106341
107141
  return;
106342
107142
  }
106343
107143
  if (subcommand === "download") {
106344
- const { writeFile: writeFile2, mkdir: mkdir3 } = await import("fs/promises");
106345
- const { join: join29, dirname: dirname17 } = await import("path");
107144
+ const { writeFile: writeFile3, mkdir: mkdir4 } = await import("fs/promises");
107145
+ const { join: join30, dirname: dirname17 } = await import("path");
106346
107146
  if (flags.all === true) {
106347
107147
  const outputDir = typeof flags.output === "string" ? flags.output : "./openmates-docs";
106348
107148
  const tree = await client2.listDocs();
106349
107149
  const slugs = collectSlugs(tree);
106350
- await mkdir3(outputDir, { recursive: true });
107150
+ await mkdir4(outputDir, { recursive: true });
106351
107151
  let count = 0;
106352
107152
  for (const slug2 of slugs) {
106353
107153
  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");
107154
+ const filePath = join30(outputDir, `${slug2}.md`);
107155
+ await mkdir4(dirname17(filePath), { recursive: true });
107156
+ await writeFile3(filePath, content2, "utf-8");
106357
107157
  count++;
106358
107158
  process.stderr.write(`\r Downloaded ${count}/${slugs.length}`);
106359
107159
  }
@@ -106372,7 +107172,7 @@ async function handleDocs(client2, subcommand, rest, flags) {
106372
107172
  }
106373
107173
  const content = await client2.getDoc(slug);
106374
107174
  const filename = typeof flags.output === "string" ? flags.output : `${slug.split("/").pop()}.md`;
106375
- await writeFile2(filename, content, "utf-8");
107175
+ await writeFile3(filename, content, "utf-8");
106376
107176
  console.log(`Saved to ${filename}`);
106377
107177
  return;
106378
107178
  }