openmates 0.27.0-alpha.3 → 0.27.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-USV2GHLA.js → chunk-P4SMXK2Z.js} +1360 -376
- package/dist/{cli-CoxPyuUN.d.ts → cli-Bq-RdTvp.d.ts} +6 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5553
|
+
import { join as join8 } from "path";
|
|
4761
5554
|
|
|
4762
5555
|
// src/remoteCommandPermissions.ts
|
|
4763
|
-
import { createHash as
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 :
|
|
5115
|
-
enabled: item.enabled === true ? true :
|
|
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 :
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
12598
|
+
hashed_user_id: createHash31("sha256").update(userId).digest("hex"),
|
|
11806
12599
|
encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
|
|
11807
|
-
provider_type_hash:
|
|
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 =
|
|
12557
|
-
const hashedTeamId = teamId ?
|
|
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 =
|
|
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 =
|
|
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:
|
|
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:
|
|
13599
|
-
const hashedEmbedId =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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,
|
|
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:
|
|
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 =
|
|
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:
|
|
18472
|
-
const hashedEmbedId =
|
|
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:
|
|
18589
|
-
const hashedEmbedId =
|
|
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:
|
|
19919
|
+
const { createHash: createHash31 } = await import("crypto");
|
|
19122
19920
|
const deletedHashes = new Set(
|
|
19123
|
-
reconciliation.deleted_chat_ids.map((id) =>
|
|
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
|
|
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
|
|
26596
|
+
import { dirname as dirname3, join as join9 } from "path";
|
|
25799
26597
|
|
|
25800
26598
|
// src/designIcons.ts
|
|
25801
|
-
import { mkdir as
|
|
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
|
|
26692
|
+
await mkdir3(directory, { recursive: true });
|
|
25895
26693
|
}
|
|
25896
|
-
await
|
|
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:
|
|
25958
|
-
const { join:
|
|
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 :
|
|
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 =
|
|
25975
|
-
await
|
|
25976
|
-
await
|
|
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
|
|
26790
|
+
await mkdir4(dirname17(outputPath), { recursive: true });
|
|
25993
26791
|
const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
25994
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ??
|
|
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:
|
|
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 ===
|
|
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 ?
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
30237
|
-
const pendingDirectory =
|
|
30238
|
-
const acknowledgedDirectory =
|
|
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(
|
|
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 =
|
|
30273
|
-
const path2 = existsSync9(acknowledgedPath) ? acknowledgedPath :
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
30363
|
-
import { createHash as
|
|
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
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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
|
|
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
|
|
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
|
|
32909
|
-
var UI_PACKAGE_RELATIVE_PATH =
|
|
32910
|
-
var GENERATED_LOCALES_RELATIVE_PATH =
|
|
32911
|
-
var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH =
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
32951
|
-
const buildScript =
|
|
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 =
|
|
32958
|
-
const yamlLink =
|
|
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
|
|
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(
|
|
33989
|
+
return resolve6(join12(dirname5(resolve6(openMatesPath)), OPENMATESCLOUD_OVERLAY_DIR));
|
|
33190
33990
|
}
|
|
33191
33991
|
function defaultOpenMatesCloudComposeFile(overlayPath) {
|
|
33192
|
-
return resolve6(
|
|
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
|
|
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
|
|
33638
|
-
var hash = (text4) =>
|
|
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(
|
|
34632
|
+
const directory = mkdtempSync(join13(tmpdir(), "openmates-caddy-merge-"));
|
|
33833
34633
|
try {
|
|
33834
|
-
const files = ["current", "previous", "target"].map((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 =
|
|
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
|
|
35445
|
+
import { dirname as dirname7, join as join14 } from "path";
|
|
34646
35446
|
function serverUpdateStatusFile(installPath, role) {
|
|
34647
|
-
return
|
|
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 =
|
|
34673
|
-
const lockPath =
|
|
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
|
|
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 =
|
|
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 =
|
|
35735
|
+
var SOURCE_COMPOSE_FILE = join16("backend", "core", "docker-compose.yml");
|
|
34936
35736
|
var ROLE_IMAGE_COMPOSE_FILES = {
|
|
34937
|
-
core:
|
|
34938
|
-
upload:
|
|
34939
|
-
preview:
|
|
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:
|
|
34943
|
-
upload:
|
|
34944
|
-
preview:
|
|
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 =
|
|
34947
|
-
var CORE_PROMTAIL_CONFIG_FILE =
|
|
34948
|
-
var CORE_ALERTMANAGER_CONFIG_FILE =
|
|
34949
|
-
var CORE_PROMETHEUS_CONFIG_DIR =
|
|
34950
|
-
var COMPOSE_OVERRIDE2 =
|
|
34951
|
-
var DEFAULT_INSTALL_PATH =
|
|
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 =
|
|
35042
|
-
var IMAGE_RUNTIME_CONFIG_FILE =
|
|
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 =
|
|
35179
|
-
const sharedConfig =
|
|
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 =
|
|
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(
|
|
35234
|
-
if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync13(
|
|
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(
|
|
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 =
|
|
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
|
|
36397
|
+
return join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
|
|
35598
36398
|
}
|
|
35599
36399
|
function packagedNoWebappTemplatePath() {
|
|
35600
|
-
return
|
|
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
|
|
36403
|
+
return join16(dirname8(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
|
|
35604
36404
|
}
|
|
35605
36405
|
function packagedCoreAlertmanagerTemplatePath() {
|
|
35606
|
-
return
|
|
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 =
|
|
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 =
|
|
35618
|
-
const runtimeDir =
|
|
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 =
|
|
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 =
|
|
35626
|
-
if (!existsSync13(runtimePath)) copyFileSync2(
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
35675
|
-
const vaultConfigDir =
|
|
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(
|
|
35678
|
-
writeFileSync8(
|
|
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(
|
|
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 =
|
|
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(
|
|
36489
|
+
writeFileSync8(join16(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
|
|
35690
36490
|
ensureImageRuntimeConfig(installPath);
|
|
35691
|
-
const envPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
35811
|
-
return
|
|
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
|
|
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 =
|
|
35840
|
-
const sourcePath =
|
|
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
|
|
36683
|
+
return join16(installPath, "backups");
|
|
35884
36684
|
}
|
|
35885
36685
|
function roleBackupDir(installPath, role) {
|
|
35886
|
-
return
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
37125
|
+
writeFileSync8(join16(rootDir, "checksums.sha256"), `${lines.sort().join("\n")}
|
|
36326
37126
|
`);
|
|
36327
37127
|
}
|
|
36328
37128
|
function verifyChecksums(rootDir) {
|
|
36329
|
-
const checksumsPath =
|
|
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 =
|
|
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) :
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
36366
|
-
copyIfExists(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
36421
|
-
copyIfExists(
|
|
36422
|
-
const postgresDump =
|
|
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 =
|
|
36710
|
-
if (!existsSync13(
|
|
36711
|
-
throw new Error(`Source Compose file not found: ${
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
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,
|
|
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 =
|
|
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 =
|
|
36942
|
-
const timerPath =
|
|
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(
|
|
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 =
|
|
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 =
|
|
37182
|
-
const reportMetricsDir =
|
|
37183
|
-
const startedMetricPath =
|
|
37184
|
-
const successMetricPath =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ${
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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) :
|
|
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(` ${
|
|
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
|
|
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
|
|
39854
|
+
const buffer = await readFile2(absolute);
|
|
39055
39855
|
addFile({
|
|
39056
39856
|
path: sandboxPath,
|
|
39057
39857
|
content_base64: buffer.toString("base64"),
|
|
@@ -60141,6 +60941,41 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
60141
60941
|
}
|
|
60142
60942
|
},
|
|
60143
60943
|
chat: {
|
|
60944
|
+
draft_embed_labels: {
|
|
60945
|
+
image: {
|
|
60946
|
+
text: "Image"
|
|
60947
|
+
},
|
|
60948
|
+
audio: {
|
|
60949
|
+
text: "Audio"
|
|
60950
|
+
},
|
|
60951
|
+
recording: {
|
|
60952
|
+
text: "Recording"
|
|
60953
|
+
},
|
|
60954
|
+
website: {
|
|
60955
|
+
text: "Website"
|
|
60956
|
+
},
|
|
60957
|
+
video: {
|
|
60958
|
+
text: "Video"
|
|
60959
|
+
},
|
|
60960
|
+
location: {
|
|
60961
|
+
text: "Location"
|
|
60962
|
+
},
|
|
60963
|
+
pdf: {
|
|
60964
|
+
text: "PDF"
|
|
60965
|
+
},
|
|
60966
|
+
file: {
|
|
60967
|
+
text: "File"
|
|
60968
|
+
},
|
|
60969
|
+
book: {
|
|
60970
|
+
text: "Book"
|
|
60971
|
+
},
|
|
60972
|
+
code: {
|
|
60973
|
+
text: "Code"
|
|
60974
|
+
},
|
|
60975
|
+
embed: {
|
|
60976
|
+
text: "Embed"
|
|
60977
|
+
}
|
|
60978
|
+
},
|
|
60144
60979
|
generated_by_cost: {
|
|
60145
60980
|
text: "Cost: {credits} credits"
|
|
60146
60981
|
},
|
|
@@ -74986,6 +75821,155 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
74986
75821
|
}
|
|
74987
75822
|
}
|
|
74988
75823
|
},
|
|
75824
|
+
local_models: {
|
|
75825
|
+
title: {
|
|
75826
|
+
text: "Local models lab"
|
|
75827
|
+
},
|
|
75828
|
+
description: {
|
|
75829
|
+
text: "Optional on-device experiments. Download models once, then test without a network connection. No credits are charged."
|
|
75830
|
+
},
|
|
75831
|
+
toggle: {
|
|
75832
|
+
text: "Use local models in this lab"
|
|
75833
|
+
},
|
|
75834
|
+
scope: {
|
|
75835
|
+
text: "This switch enables only the tests below. Chat speech recognition, speech playback and personal-data protection keep their current providers and settings."
|
|
75836
|
+
},
|
|
75837
|
+
download: {
|
|
75838
|
+
text: "Download"
|
|
75839
|
+
},
|
|
75840
|
+
not_downloaded: {
|
|
75841
|
+
text: "Not downloaded"
|
|
75842
|
+
},
|
|
75843
|
+
ready: {
|
|
75844
|
+
text: "Ready for offline tests"
|
|
75845
|
+
},
|
|
75846
|
+
downloading: {
|
|
75847
|
+
text: "Downloading {percent}%"
|
|
75848
|
+
},
|
|
75849
|
+
download_failed: {
|
|
75850
|
+
text: "Download failed. Retry when your connection and free storage are available."
|
|
75851
|
+
},
|
|
75852
|
+
run: {
|
|
75853
|
+
text: "Run local test"
|
|
75854
|
+
},
|
|
75855
|
+
import_audio: {
|
|
75856
|
+
text: "Import audio file"
|
|
75857
|
+
},
|
|
75858
|
+
record: {
|
|
75859
|
+
text: "Record microphone"
|
|
75860
|
+
},
|
|
75861
|
+
stop_recording: {
|
|
75862
|
+
text: "Stop recording"
|
|
75863
|
+
},
|
|
75864
|
+
audio_ready: {
|
|
75865
|
+
text: "Audio ready: {seconds} seconds"
|
|
75866
|
+
},
|
|
75867
|
+
speech_input: {
|
|
75868
|
+
text: "English text to speak"
|
|
75869
|
+
},
|
|
75870
|
+
privacy_input: {
|
|
75871
|
+
text: "Text to inspect for personal data"
|
|
75872
|
+
},
|
|
75873
|
+
input_placeholder: {
|
|
75874
|
+
text: "Enter disposable test text"
|
|
75875
|
+
},
|
|
75876
|
+
play: {
|
|
75877
|
+
text: "Play generated speech"
|
|
75878
|
+
},
|
|
75879
|
+
stop_playback: {
|
|
75880
|
+
text: "Stop playback"
|
|
75881
|
+
},
|
|
75882
|
+
running: {
|
|
75883
|
+
text: "Running locally\u2026"
|
|
75884
|
+
},
|
|
75885
|
+
cancelling: {
|
|
75886
|
+
text: "Cancelling and unloading\u2026"
|
|
75887
|
+
},
|
|
75888
|
+
elapsed: {
|
|
75889
|
+
text: "Elapsed time"
|
|
75890
|
+
},
|
|
75891
|
+
rtf: {
|
|
75892
|
+
text: "Real-time factor"
|
|
75893
|
+
},
|
|
75894
|
+
environment: {
|
|
75895
|
+
text: "{cores} CPU cores \xB7 {memory} device memory \xB7 thermal state: {thermal}"
|
|
75896
|
+
},
|
|
75897
|
+
thermal_nominal: {
|
|
75898
|
+
text: "normal"
|
|
75899
|
+
},
|
|
75900
|
+
thermal_fair: {
|
|
75901
|
+
text: "warm"
|
|
75902
|
+
},
|
|
75903
|
+
thermal_serious: {
|
|
75904
|
+
text: "hot"
|
|
75905
|
+
},
|
|
75906
|
+
thermal_critical: {
|
|
75907
|
+
text: "critical"
|
|
75908
|
+
},
|
|
75909
|
+
unavailable: {
|
|
75910
|
+
text: "Unavailable"
|
|
75911
|
+
},
|
|
75912
|
+
result: {
|
|
75913
|
+
text: "Local test result"
|
|
75914
|
+
},
|
|
75915
|
+
no_entities: {
|
|
75916
|
+
text: "No entities detected. This experimental result does not prove the text is safe."
|
|
75917
|
+
},
|
|
75918
|
+
audio_error: {
|
|
75919
|
+
text: "The audio file could not be read, recorded or played. Choose a supported audio file and try again."
|
|
75920
|
+
},
|
|
75921
|
+
microphone_error: {
|
|
75922
|
+
text: "Microphone access is required to record a test. You can also import an audio file."
|
|
75923
|
+
},
|
|
75924
|
+
run_error: {
|
|
75925
|
+
text: "The local test failed. Check that the model download is complete and this device supports its runtime. No cloud fallback was used."
|
|
75926
|
+
},
|
|
75927
|
+
privacy_note: {
|
|
75928
|
+
text: "Inputs, recordings and generated output are temporary and cleared when you leave this page. Installed models remain until removed. Run one model at a time; cancellation waits for the active native operation to finish."
|
|
75929
|
+
},
|
|
75930
|
+
whisper: {
|
|
75931
|
+
text: "WhisperKit large-v3 turbo \xB7 speech to text"
|
|
75932
|
+
},
|
|
75933
|
+
kokoro: {
|
|
75934
|
+
text: "Kokoro \xB7 English text to speech"
|
|
75935
|
+
},
|
|
75936
|
+
privacy_filter: {
|
|
75937
|
+
text: "OpenAI Privacy Filter \xB7 personal data"
|
|
75938
|
+
},
|
|
75939
|
+
entity: {
|
|
75940
|
+
text: "{label} \xB7 {start}\u2013{end} \xB7 confidence {score}"
|
|
75941
|
+
},
|
|
75942
|
+
installed_size: {
|
|
75943
|
+
text: "Download size"
|
|
75944
|
+
},
|
|
75945
|
+
revision: {
|
|
75946
|
+
text: "Revision"
|
|
75947
|
+
},
|
|
75948
|
+
waveform: {
|
|
75949
|
+
text: "Generated audio waveform"
|
|
75950
|
+
},
|
|
75951
|
+
cancelled: {
|
|
75952
|
+
text: "Download cancelled"
|
|
75953
|
+
},
|
|
75954
|
+
not_enough_space: {
|
|
75955
|
+
text: "Not enough free storage for this model"
|
|
75956
|
+
},
|
|
75957
|
+
kokoro_os_error: {
|
|
75958
|
+
text: "Kokoro is currently unavailable on iOS and macOS 27 or later because its upstream runtime can crash. Try another model or an earlier supported OS."
|
|
75959
|
+
},
|
|
75960
|
+
peak_memory: {
|
|
75961
|
+
text: "App peak resident memory"
|
|
75962
|
+
},
|
|
75963
|
+
verifying: {
|
|
75964
|
+
text: "Verifying installed files locally\u2026"
|
|
75965
|
+
},
|
|
75966
|
+
kokoro_input_limits: {
|
|
75967
|
+
text: "Use short, simple English text. Spell out decimals, currency amounts and times in words. Unsupported formats and foreign characters are rejected. This lab does not include the full upstream text normalization pipeline."
|
|
75968
|
+
},
|
|
75969
|
+
architecture_unavailable: {
|
|
75970
|
+
text: "These local models require an iPhone, iPad or Apple silicon Mac. Intel Macs and Apple Watch are unsupported; downloads and tests are disabled."
|
|
75971
|
+
}
|
|
75972
|
+
},
|
|
74989
75973
|
open_settings_menu: {
|
|
74990
75974
|
text: "Open settings menu"
|
|
74991
75975
|
},
|
|
@@ -81400,10 +82384,10 @@ function buildAssistantFeedbackDecision(rating) {
|
|
|
81400
82384
|
}
|
|
81401
82385
|
|
|
81402
82386
|
// src/benchmark.ts
|
|
81403
|
-
import { createHash as
|
|
82387
|
+
import { createHash as createHash20, randomUUID as randomUUID16 } from "crypto";
|
|
81404
82388
|
import { existsSync as existsSync14, mkdtempSync as mkdtempSync3, readFileSync as readFileSync15, readdirSync as readdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
81405
82389
|
import { tmpdir as tmpdir3 } from "os";
|
|
81406
|
-
import { dirname as dirname9, join as
|
|
82390
|
+
import { dirname as dirname9, join as join17, resolve as resolve9 } from "path";
|
|
81407
82391
|
import { fileURLToPath } from "url";
|
|
81408
82392
|
var DEFAULT_JUDGE_MODEL = "google/gemini-3-flash-preview";
|
|
81409
82393
|
var DEFAULT_PROMPT_BUDGET_MODEL = "google/gemini-3.5-flash";
|
|
@@ -82571,7 +83555,7 @@ function loadProviderPricing() {
|
|
|
82571
83555
|
if (!providersDir) return pricing;
|
|
82572
83556
|
for (const fileName of readdirSync7(providersDir)) {
|
|
82573
83557
|
if (!fileName.endsWith(".yml")) continue;
|
|
82574
|
-
const filePath =
|
|
83558
|
+
const filePath = join17(providersDir, fileName);
|
|
82575
83559
|
const text4 = readFileSync15(filePath, "utf-8");
|
|
82576
83560
|
const provider = parseProviderId(text4) ?? fileName.replace(/\.yml$/, "");
|
|
82577
83561
|
for (const modelPricing of parseModelPricing(text4, provider)) {
|
|
@@ -82626,9 +83610,9 @@ function findProvidersDir() {
|
|
|
82626
83610
|
const currentFile = fileURLToPath(import.meta.url);
|
|
82627
83611
|
let current = dirname9(currentFile);
|
|
82628
83612
|
for (let index = 0; index < 8; index += 1) {
|
|
82629
|
-
const candidate =
|
|
83613
|
+
const candidate = join17(current, "backend", "providers");
|
|
82630
83614
|
if (existsSync14(candidate)) return candidate;
|
|
82631
|
-
const parentCandidate =
|
|
83615
|
+
const parentCandidate = join17(current, "..", "..", "backend", "providers");
|
|
82632
83616
|
if (existsSync14(parentCandidate)) return resolve9(parentCandidate);
|
|
82633
83617
|
const next = dirname9(current);
|
|
82634
83618
|
if (next === current) break;
|
|
@@ -82834,14 +83818,14 @@ function hashPromptBudgetCorpus(cases) {
|
|
|
82834
83818
|
return stableHash(cases.map((benchmarkCase) => ({ id: benchmarkCase.id, turns: benchmarkCase.turns })));
|
|
82835
83819
|
}
|
|
82836
83820
|
function stableHash(value) {
|
|
82837
|
-
return
|
|
83821
|
+
return createHash20("sha256").update(JSON.stringify(value)).digest("hex");
|
|
82838
83822
|
}
|
|
82839
83823
|
function defaultImageFixturePath() {
|
|
82840
|
-
const fixtureDir =
|
|
82841
|
-
const fixturePath =
|
|
83824
|
+
const fixtureDir = join17(dirname9(fileURLToPath(import.meta.url)), "..", "fixtures");
|
|
83825
|
+
const fixturePath = join17(fixtureDir, "brandenburger-tor.png");
|
|
82842
83826
|
if (existsSync14(fixturePath)) return fixturePath;
|
|
82843
|
-
const tempDir = mkdtempSync3(
|
|
82844
|
-
const tempPath =
|
|
83827
|
+
const tempDir = mkdtempSync3(join17(tmpdir3(), "openmates-benchmark-"));
|
|
83828
|
+
const tempPath = join17(tempDir, "brandenburger-tor.svg");
|
|
82845
83829
|
writeFileSync9(tempPath, FIXTURE_IMAGE_SVG, "utf-8");
|
|
82846
83830
|
return tempPath;
|
|
82847
83831
|
}
|
|
@@ -83521,7 +84505,7 @@ var APP_GRADIENTS = {
|
|
|
83521
84505
|
var PRIMARY_GRADIENT = { "start": "#4867cd", "end": "#5a85eb" };
|
|
83522
84506
|
|
|
83523
84507
|
// src/tuiProjectsWorkspace.ts
|
|
83524
|
-
import { createHash as
|
|
84508
|
+
import { createHash as createHash21, randomBytes as randomBytes10, randomUUID as randomUUID17 } from "crypto";
|
|
83525
84509
|
|
|
83526
84510
|
// src/tuiForms.ts
|
|
83527
84511
|
function formValue(form, name) {
|
|
@@ -83697,7 +84681,7 @@ async function loadTuiProject(client2, id) {
|
|
|
83697
84681
|
client2.listProjectSources(id, context)
|
|
83698
84682
|
]);
|
|
83699
84683
|
const folderIds = itemsResponse.folders.map((folder) => String(folder.folder_id ?? "")).filter(Boolean);
|
|
83700
|
-
const folderByHash = new Map(folderIds.map((folderId) => [
|
|
84684
|
+
const folderByHash = new Map(folderIds.map((folderId) => [createHash21("sha256").update(folderId).digest("hex"), folderId]));
|
|
83701
84685
|
project.folders = await Promise.all(itemsResponse.folders.filter((folder) => typeof folder.folder_id === "string" && folder.folder_id).map(async (folder) => {
|
|
83702
84686
|
const parentHash = typeof folder.hashed_parent_folder_id === "string" ? folder.hashed_parent_folder_id : typeof folder.parent_folder_id === "string" && folderByHash.has(folder.parent_folder_id) ? folder.parent_folder_id : void 0;
|
|
83703
84687
|
return {
|
|
@@ -83957,7 +84941,7 @@ async function submitProjectForm(client2, form) {
|
|
|
83957
84941
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: focus }), key),
|
|
83958
84942
|
key_wrappers: wrapping.teamId ? [{
|
|
83959
84943
|
key_type: "team",
|
|
83960
|
-
hashed_team_id:
|
|
84944
|
+
hashed_team_id: createHash21("sha256").update(wrapping.teamId).digest("hex"),
|
|
83961
84945
|
team_key_epoch: 1,
|
|
83962
84946
|
encrypted_project_key: await encryptBytesWithAesGcm(key, wrapping.key),
|
|
83963
84947
|
wrapper_version: 1,
|
|
@@ -88020,13 +89004,13 @@ function clamp(value, min, max) {
|
|
|
88020
89004
|
}
|
|
88021
89005
|
|
|
88022
89006
|
// src/codexTaskBridge.ts
|
|
88023
|
-
import { createHash as
|
|
89007
|
+
import { createHash as createHash22 } from "crypto";
|
|
88024
89008
|
import { spawn as spawn3 } from "child_process";
|
|
88025
89009
|
import { existsSync as existsSync15, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
|
|
88026
|
-
import { isAbsolute as isAbsolute4, join as
|
|
89010
|
+
import { isAbsolute as isAbsolute4, join as join18, resolve as resolve10 } from "path";
|
|
88027
89011
|
import lockfile5 from "proper-lockfile";
|
|
88028
89012
|
function startCodexTaskBridges(roots, snapshots, stateRoot, notice) {
|
|
88029
|
-
const configPath =
|
|
89013
|
+
const configPath = join18(stateRoot, "codex-adapter.json");
|
|
88030
89014
|
const children = [];
|
|
88031
89015
|
if (existsSync15(configPath)) {
|
|
88032
89016
|
const config = JSON.parse(readFileSync16(configPath, "utf8"));
|
|
@@ -88063,13 +89047,13 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
88063
89047
|
const session = client2.getSession();
|
|
88064
89048
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
88065
89049
|
const account = JSON.stringify(scope2);
|
|
88066
|
-
const directory =
|
|
89050
|
+
const directory = join18(stateRoot, "codex-chat-deletions", createHash22("sha256").update(account).digest("hex"));
|
|
88067
89051
|
if (!existsSync15(directory)) return;
|
|
88068
89052
|
const release2 = await lockfile5.lock(directory, { retries: 0, stale: 24e4 });
|
|
88069
89053
|
try {
|
|
88070
89054
|
let attempts = 0;
|
|
88071
89055
|
for (const name of readdirSync8(directory).filter((name2) => /^[a-f0-9]{64}\.json$/.test(name2)).sort()) {
|
|
88072
|
-
const path2 =
|
|
89056
|
+
const path2 = join18(directory, name);
|
|
88073
89057
|
const record7 = JSON.parse(readFileSync16(path2, "utf8"));
|
|
88074
89058
|
if (record7.state !== "pending") continue;
|
|
88075
89059
|
if (attempts++ >= 3) break;
|
|
@@ -88097,18 +89081,18 @@ async function flushCodexChatDeletions(client2, teamId, stateRoot, notice) {
|
|
|
88097
89081
|
}
|
|
88098
89082
|
|
|
88099
89083
|
// src/taskCommandDelivery.ts
|
|
88100
|
-
import { createHash as
|
|
89084
|
+
import { createHash as createHash23 } from "crypto";
|
|
88101
89085
|
import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
|
|
88102
|
-
import { join as
|
|
89086
|
+
import { join as join19 } from "path";
|
|
88103
89087
|
import lockfile6 from "proper-lockfile";
|
|
88104
89088
|
async function flushTaskCommands(client2, teamId, root = resolveStateDir(), onNotice) {
|
|
88105
89089
|
const session = client2.getSession();
|
|
88106
89090
|
const scope2 = [session.apiUrl, session.hashedEmail, teamId || "personal"];
|
|
88107
|
-
const directory =
|
|
89091
|
+
const directory = join19(root, "task-command-delivery", createHash23("sha256").update(JSON.stringify(scope2)).digest("hex"));
|
|
88108
89092
|
if (!existsSync16(directory)) return;
|
|
88109
89093
|
const release2 = await lockfile6.lock(directory, { retries: 0, stale: 24e4 });
|
|
88110
89094
|
try {
|
|
88111
|
-
const records = readdirSync9(directory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name)).map((name) => ({ path:
|
|
89095
|
+
const records = readdirSync9(directory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name)).map((name) => ({ path: join19(directory, name), command: JSON.parse(readFileSync17(join19(directory, name), "utf8")) })).sort((a, b) => a.command.created_at - b.command.created_at);
|
|
88112
89096
|
let attempted = 0;
|
|
88113
89097
|
for (const { path: path2, command } of records) {
|
|
88114
89098
|
if (command.state !== "pending") continue;
|
|
@@ -88214,8 +89198,8 @@ import {
|
|
|
88214
89198
|
writeFileSync as writeFileSync13
|
|
88215
89199
|
} from "fs";
|
|
88216
89200
|
import { spawn as spawn7 } from "child_process";
|
|
88217
|
-
import { createHash as
|
|
88218
|
-
import { extname as extname4, join as
|
|
89201
|
+
import { createHash as createHash29 } from "crypto";
|
|
89202
|
+
import { extname as extname4, join as join26, resolve as resolve16, relative as relative11 } from "path";
|
|
88219
89203
|
|
|
88220
89204
|
// src/projectSourceRootPolicy.ts
|
|
88221
89205
|
import { existsSync as existsSync17, realpathSync as realpathSync2, statSync as statSync2 } from "fs";
|
|
@@ -88249,7 +89233,7 @@ function isEqualOrInside(parent, candidate) {
|
|
|
88249
89233
|
}
|
|
88250
89234
|
|
|
88251
89235
|
// src/projectPathPolicy.ts
|
|
88252
|
-
import { createHash as
|
|
89236
|
+
import { createHash as createHash24, randomBytes as randomBytes12 } from "crypto";
|
|
88253
89237
|
import {
|
|
88254
89238
|
chmodSync as chmodSync9,
|
|
88255
89239
|
closeSync as closeSync5,
|
|
@@ -88269,7 +89253,7 @@ import {
|
|
|
88269
89253
|
unlinkSync,
|
|
88270
89254
|
writeSync
|
|
88271
89255
|
} from "fs";
|
|
88272
|
-
import { dirname as dirname11, isAbsolute as isAbsolute5, join as
|
|
89256
|
+
import { dirname as dirname11, isAbsolute as isAbsolute5, join as join20, relative as relative4, resolve as resolve12 } from "path";
|
|
88273
89257
|
var DEFAULT_MAX_IGNORE_FILES = 128;
|
|
88274
89258
|
var DEFAULT_MAX_IGNORE_BYTES = 1024 * 1024;
|
|
88275
89259
|
var MAX_POLICY_ENTRIES = 5e4;
|
|
@@ -88362,7 +89346,7 @@ function loadProjectPathPolicy(sourceRoot, options = {}) {
|
|
|
88362
89346
|
function findGitPolicyRoot(sourceRoot) {
|
|
88363
89347
|
let current = sourceRoot;
|
|
88364
89348
|
while (true) {
|
|
88365
|
-
if (existsSync18(
|
|
89349
|
+
if (existsSync18(join20(current, ".git"))) return current;
|
|
88366
89350
|
const parent = dirname11(current);
|
|
88367
89351
|
if (parent === current) return sourceRoot;
|
|
88368
89352
|
current = parent;
|
|
@@ -88392,7 +89376,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
|
|
|
88392
89376
|
if (parent === current || !isInside2(policyRoot, parent)) throw new Error("Source root is outside its Git policy root");
|
|
88393
89377
|
current = parent;
|
|
88394
89378
|
}
|
|
88395
|
-
for (const directory of chain.reverse()) add(
|
|
89379
|
+
for (const directory of chain.reverse()) add(join20(directory, ".gitignore"));
|
|
88396
89380
|
for (const target of limits.targetPaths) {
|
|
88397
89381
|
const normalized = target === "." ? "" : normalizeSourceRelativePath(target);
|
|
88398
89382
|
const absolute = normalized ? resolve12(sourceRoot, normalized) : sourceRoot;
|
|
@@ -88406,7 +89390,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
|
|
|
88406
89390
|
directory = dirname11(directory);
|
|
88407
89391
|
}
|
|
88408
89392
|
for (const ancestor of ancestors.reverse()) {
|
|
88409
|
-
const ignorePath =
|
|
89393
|
+
const ignorePath = join20(ancestor, ".gitignore");
|
|
88410
89394
|
if (!files.some((file) => file.path === normalizeRelative(policyRoot, ignorePath))) add(ignorePath);
|
|
88411
89395
|
}
|
|
88412
89396
|
}
|
|
@@ -88419,7 +89403,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
|
|
|
88419
89403
|
for (const entry of readdirSync10(directory, { withFileTypes: true })) {
|
|
88420
89404
|
if (++entries > limits.maxEntries) throw new Error("Project path policy scan exceeds its entry limit");
|
|
88421
89405
|
if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
|
|
88422
|
-
const child =
|
|
89406
|
+
const child = join20(directory, entry.name);
|
|
88423
89407
|
const canonical = realpathSync3(child);
|
|
88424
89408
|
if (!isInside2(sourceRoot, canonical) || seen.has(canonical)) continue;
|
|
88425
89409
|
const sourceRelative = normalizeRelative(sourceRoot, child);
|
|
@@ -88427,7 +89411,7 @@ function collectIgnoreFiles(policyRoot, sourceRoot, limits) {
|
|
|
88427
89411
|
if (limits.privatePolicy.isPrivate(sourceRelative, true) || currentPolicy.isIgnored(policyRelative, true)) continue;
|
|
88428
89412
|
seen.add(canonical);
|
|
88429
89413
|
const before = files.length;
|
|
88430
|
-
add(
|
|
89414
|
+
add(join20(child, ".gitignore"));
|
|
88431
89415
|
if (files.length !== before) currentPolicy = createProjectPathPolicy({ ignoreFiles: files, privatePaths: [] });
|
|
88432
89416
|
directories.push(child);
|
|
88433
89417
|
}
|
|
@@ -88440,10 +89424,10 @@ function updateStickyPrivatePaths(sourceRoot, additions, stateDirectory) {
|
|
|
88440
89424
|
throw new Error("Project path policy state must stay outside the source root");
|
|
88441
89425
|
}
|
|
88442
89426
|
const key = sha2563(realpathSync3(sourceRoot));
|
|
88443
|
-
const path2 =
|
|
89427
|
+
const path2 = join20(stateRoot, `${key}.json`);
|
|
88444
89428
|
if (additions.length === 0 && !existsSync18(path2)) return [];
|
|
88445
89429
|
preparePrivateDirectory(stateRoot);
|
|
88446
|
-
const lock =
|
|
89430
|
+
const lock = join20(stateRoot, `${key}.lock`);
|
|
88447
89431
|
try {
|
|
88448
89432
|
mkdirSync14(lock, { mode: 448 });
|
|
88449
89433
|
} catch (error) {
|
|
@@ -88530,7 +89514,7 @@ function isInside2(root, candidate) {
|
|
|
88530
89514
|
return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
|
|
88531
89515
|
}
|
|
88532
89516
|
function sha2563(value) {
|
|
88533
|
-
return
|
|
89517
|
+
return createHash24("sha256").update(value).digest("hex");
|
|
88534
89518
|
}
|
|
88535
89519
|
function stableJson2(value) {
|
|
88536
89520
|
return JSON.stringify(value, (_key, item) => {
|
|
@@ -88540,7 +89524,7 @@ function stableJson2(value) {
|
|
|
88540
89524
|
}
|
|
88541
89525
|
|
|
88542
89526
|
// src/remoteFileWrites.ts
|
|
88543
|
-
import { createHash as
|
|
89527
|
+
import { createHash as createHash25, randomBytes as randomBytes13 } from "crypto";
|
|
88544
89528
|
import {
|
|
88545
89529
|
chmodSync as chmodSync10,
|
|
88546
89530
|
closeSync as closeSync7,
|
|
@@ -88560,7 +89544,7 @@ import {
|
|
|
88560
89544
|
writeFileSync as writeFileSync10
|
|
88561
89545
|
} from "fs";
|
|
88562
89546
|
import { tmpdir as tmpdir5 } from "os";
|
|
88563
|
-
import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute6, join as
|
|
89547
|
+
import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute6, join as join22, relative as relative5, resolve as resolve13 } from "path";
|
|
88564
89548
|
import lockfile7 from "proper-lockfile";
|
|
88565
89549
|
|
|
88566
89550
|
// src/projectFileRisk.ts
|
|
@@ -88632,7 +89616,7 @@ function matchesAdditionalPrivatePattern(path2, patterns) {
|
|
|
88632
89616
|
// src/remoteCommandSourceLock.ts
|
|
88633
89617
|
import { closeSync as closeSync6, constants as constants4, lstatSync as lstatSync5, mkdirSync as mkdirSync15, openSync as openSync6, realpathSync as realpathSync4, statSync as statSync4 } from "fs";
|
|
88634
89618
|
import { tmpdir as tmpdir4 } from "os";
|
|
88635
|
-
import { join as
|
|
89619
|
+
import { join as join21 } from "path";
|
|
88636
89620
|
import { spawn as spawn4, spawnSync as spawnSync2 } from "child_process";
|
|
88637
89621
|
var RemoteProjectSourceLockError = class extends Error {
|
|
88638
89622
|
constructor(message) {
|
|
@@ -88683,15 +89667,15 @@ async function acquireRemoteProjectFileEditLock(sourceRoot, options = {}) {
|
|
|
88683
89667
|
let abortListener = null;
|
|
88684
89668
|
await new Promise((resolvePromise, reject) => {
|
|
88685
89669
|
let stderr2 = "";
|
|
88686
|
-
const onError = (error) =>
|
|
88687
|
-
const onExit = (code, signal) =>
|
|
89670
|
+
const onError = (error) => fail4(`Failed to start Project lock holder: ${error.message}`);
|
|
89671
|
+
const onExit = (code, signal) => fail4(`Project lock holder exited before acquisition (${signal ?? code}): ${stderr2.trim()}`);
|
|
88688
89672
|
const cleanup = () => {
|
|
88689
89673
|
if (timer) clearTimeout(timer);
|
|
88690
89674
|
if (abortListener && options.signal) options.signal.removeEventListener("abort", abortListener);
|
|
88691
89675
|
child.removeListener("error", onError);
|
|
88692
89676
|
child.removeListener("exit", onExit);
|
|
88693
89677
|
};
|
|
88694
|
-
const
|
|
89678
|
+
const fail4 = (message) => {
|
|
88695
89679
|
cleanup();
|
|
88696
89680
|
child.kill("SIGKILL");
|
|
88697
89681
|
reject(new RemoteProjectSourceLockError(message));
|
|
@@ -88702,13 +89686,13 @@ async function acquireRemoteProjectFileEditLock(sourceRoot, options = {}) {
|
|
|
88702
89686
|
child.once("error", onError);
|
|
88703
89687
|
child.once("exit", onExit);
|
|
88704
89688
|
child.stdout.once("data", (chunk) => {
|
|
88705
|
-
if (chunk[0] !== LOCK_READY_BYTE) return
|
|
89689
|
+
if (chunk[0] !== LOCK_READY_BYTE) return fail4("Project lock holder returned an invalid readiness signal");
|
|
88706
89690
|
cleanup();
|
|
88707
89691
|
resolvePromise();
|
|
88708
89692
|
});
|
|
88709
|
-
timer = setTimeout(() =>
|
|
89693
|
+
timer = setTimeout(() => fail4(`Timed out waiting ${timeoutMs}ms for the Project file-edit lock`), timeoutMs);
|
|
88710
89694
|
if (options.signal) {
|
|
88711
|
-
abortListener = () =>
|
|
89695
|
+
abortListener = () => fail4("Project lock acquisition was canceled");
|
|
88712
89696
|
options.signal.addEventListener("abort", abortListener, { once: true });
|
|
88713
89697
|
}
|
|
88714
89698
|
});
|
|
@@ -88769,9 +89753,9 @@ function remoteProjectSourceLockPath(sourceRoot) {
|
|
|
88769
89753
|
if (!stat3.isDirectory()) throw new RemoteProjectSourceLockError("Project source root must be a directory");
|
|
88770
89754
|
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
88771
89755
|
if (uid === null) throw new RemoteProjectSourceLockError("Project source locks require a numeric user identity");
|
|
88772
|
-
const directory =
|
|
89756
|
+
const directory = join21(tmpdir4(), `openmates-project-source-locks-${uid}`);
|
|
88773
89757
|
preparePrivateDirectory2(directory, uid);
|
|
88774
|
-
const lockPath =
|
|
89758
|
+
const lockPath = join21(directory, "source-writes.lock");
|
|
88775
89759
|
const descriptor = openSync6(lockPath, constants4.O_CREAT | constants4.O_RDWR | constants4.O_NOFOLLOW, 384);
|
|
88776
89760
|
closeSync6(descriptor);
|
|
88777
89761
|
const lockStat = lstatSync5(lockPath);
|
|
@@ -88802,10 +89786,10 @@ var RemoteFileMutationError = class extends Error {
|
|
|
88802
89786
|
}
|
|
88803
89787
|
};
|
|
88804
89788
|
var MAX_FILE_BYTES2 = 200 * 1024;
|
|
88805
|
-
var LOCK_ROOT =
|
|
88806
|
-
var DEFAULT_JOURNAL_ROOT =
|
|
89789
|
+
var LOCK_ROOT = join22(tmpdir5(), `openmates-remote-file-locks-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
89790
|
+
var DEFAULT_JOURNAL_ROOT = join22(tmpdir5(), `openmates-remote-file-journal-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
|
|
88807
89791
|
function sha256ProjectFile(content) {
|
|
88808
|
-
return
|
|
89792
|
+
return createHash25("sha256").update(content).digest("hex");
|
|
88809
89793
|
}
|
|
88810
89794
|
async function executeRemoteFileMutation(options) {
|
|
88811
89795
|
const mutation = validateMutation(options.mutation);
|
|
@@ -88820,7 +89804,7 @@ async function executeRemoteFileMutation(options) {
|
|
|
88820
89804
|
}
|
|
88821
89805
|
const scopeDigest = sha256ProjectFile(options.operationScope);
|
|
88822
89806
|
const payloadDigest = sha256ProjectFile(stableJson3(mutation));
|
|
88823
|
-
const journalPath =
|
|
89807
|
+
const journalPath = join22(journalRoot, scopeDigest, `${sha256ProjectFile(`${options.operationScope}\0${mutation.operation_id}`)}.json`);
|
|
88824
89808
|
preparePrivateDirectory3(dirname12(journalPath));
|
|
88825
89809
|
await options.authorize();
|
|
88826
89810
|
const existingJournal = readJournal(journalPath);
|
|
@@ -88985,7 +89969,7 @@ function openPinnedParent(root, path2) {
|
|
|
88985
89969
|
function readPinnedFile(parent) {
|
|
88986
89970
|
let descriptor;
|
|
88987
89971
|
try {
|
|
88988
|
-
descriptor = openSync7(
|
|
89972
|
+
descriptor = openSync7(join22(parent.procPath, parent.targetName), constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
88989
89973
|
} catch (error) {
|
|
88990
89974
|
if (error.code === "ENOENT") return null;
|
|
88991
89975
|
if (error.code === "ELOOP") {
|
|
@@ -89058,8 +90042,8 @@ function assertPreviewStillCurrent(mutation, before, current) {
|
|
|
89058
90042
|
}
|
|
89059
90043
|
function createPinnedFile(parent, content) {
|
|
89060
90044
|
const temp = temporaryName();
|
|
89061
|
-
const tempPath =
|
|
89062
|
-
const targetPath =
|
|
90045
|
+
const tempPath = join22(parent.procPath, temp);
|
|
90046
|
+
const targetPath = join22(parent.procPath, parent.targetName);
|
|
89063
90047
|
let descriptor;
|
|
89064
90048
|
try {
|
|
89065
90049
|
descriptor = openSync7(tempPath, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 438);
|
|
@@ -89083,7 +90067,7 @@ function createPinnedFile(parent, content) {
|
|
|
89083
90067
|
}
|
|
89084
90068
|
}
|
|
89085
90069
|
function replacePinnedFile(parent, expected, content) {
|
|
89086
|
-
const tempPath =
|
|
90070
|
+
const tempPath = join22(parent.procPath, temporaryName());
|
|
89087
90071
|
let descriptor;
|
|
89088
90072
|
try {
|
|
89089
90073
|
descriptor = openSync7(tempPath, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, expected.mode);
|
|
@@ -89096,7 +90080,7 @@ function replacePinnedFile(parent, expected, content) {
|
|
|
89096
90080
|
if (!nearWrite || nearWrite.hash !== expected.hash || nearWrite.dev !== expected.dev || nearWrite.ino !== expected.ino) {
|
|
89097
90081
|
throw new RemoteFileMutationError("file_changed", "Project file changed at the final write boundary");
|
|
89098
90082
|
}
|
|
89099
|
-
renameSync8(tempPath,
|
|
90083
|
+
renameSync8(tempPath, join22(parent.procPath, parent.targetName));
|
|
89100
90084
|
fsyncSync3(parent.descriptor);
|
|
89101
90085
|
} catch (error) {
|
|
89102
90086
|
if (descriptor !== void 0) closeSync7(descriptor);
|
|
@@ -89123,7 +90107,7 @@ ${body}${hasFinalNewline ? "" : "\\n"}`;
|
|
|
89123
90107
|
}
|
|
89124
90108
|
async function acquirePathLock(parentIdentity, path2) {
|
|
89125
90109
|
preparePrivateDirectory3(LOCK_ROOT);
|
|
89126
|
-
const target =
|
|
90110
|
+
const target = join22(LOCK_ROOT, sha256ProjectFile(`${parentIdentity}\0${basename5(path2)}`));
|
|
89127
90111
|
preparePrivateDirectory3(target);
|
|
89128
90112
|
try {
|
|
89129
90113
|
return await lockfile7.lock(target, {
|
|
@@ -89493,13 +90477,13 @@ import { existsSync as existsSync23, realpathSync as realpathSync10, statSync as
|
|
|
89493
90477
|
import { basename as basename7, dirname as dirname13, relative as relative10, resolve as resolve15 } from "path";
|
|
89494
90478
|
|
|
89495
90479
|
// src/remoteCommandRuntime.ts
|
|
89496
|
-
import { createHash as
|
|
90480
|
+
import { createHash as createHash27, randomUUID as randomUUID21 } from "crypto";
|
|
89497
90481
|
import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
|
|
89498
90482
|
import { existsSync as existsSync21, lstatSync as lstatSync9, readdirSync as readdirSync12, realpathSync as realpathSync7, statSync as statSync6 } from "fs";
|
|
89499
|
-
import { isAbsolute as isAbsolute8, join as
|
|
90483
|
+
import { isAbsolute as isAbsolute8, join as join23, relative as relative7, resolve as resolve14, sep } from "path";
|
|
89500
90484
|
|
|
89501
90485
|
// src/remoteCommandAppArmor.ts
|
|
89502
|
-
import { createHash as
|
|
90486
|
+
import { createHash as createHash26 } from "crypto";
|
|
89503
90487
|
import { spawn as spawn5 } from "child_process";
|
|
89504
90488
|
import { lstatSync as lstatSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, statSync as statSync5 } from "fs";
|
|
89505
90489
|
import { isAbsolute as isAbsolute7, relative as relative6 } from "path";
|
|
@@ -89716,7 +90700,7 @@ function hasControl2(value) {
|
|
|
89716
90700
|
});
|
|
89717
90701
|
}
|
|
89718
90702
|
function sha2564(value) {
|
|
89719
|
-
return
|
|
90703
|
+
return createHash26("sha256").update(value).digest("hex");
|
|
89720
90704
|
}
|
|
89721
90705
|
|
|
89722
90706
|
// src/remoteCommandRuntime.ts
|
|
@@ -90142,7 +91126,7 @@ function createRemoteCommandPreflight(request) {
|
|
|
90142
91126
|
error instanceof Error ? error.message : "Project source root boundary cannot be established"
|
|
90143
91127
|
);
|
|
90144
91128
|
}
|
|
90145
|
-
const cwd = request.policy.cwd === "." ? root : realpath(
|
|
91129
|
+
const cwd = request.policy.cwd === "." ? root : realpath(join23(root, request.policy.cwd));
|
|
90146
91130
|
if (!isInside5(root, cwd) || !statSync6(cwd).isDirectory()) throw new RemoteCommandError("invalid_request", "Command cwd escapes the Project root");
|
|
90147
91131
|
const canonicalPolicy = { ...request.policy, argv: [...request.policy.argv], cwd: relative7(root, cwd).split(sep).join("/") || "." };
|
|
90148
91132
|
const toolchains = request.toolchain_paths.map(canonicalDirectory);
|
|
@@ -90260,7 +91244,7 @@ function prepareSandboxLaunch(preflight, authority, capability, network, appArmo
|
|
|
90260
91244
|
args.push(preflight.policy.source_access === "read_write" ? "--bind" : "--ro-bind", preflight.source_root, "/project");
|
|
90261
91245
|
if (preflight.policy.source_access === "read_write") {
|
|
90262
91246
|
for (const protectedPath of preflight.protected_project_paths) {
|
|
90263
|
-
args.push("--ro-bind",
|
|
91247
|
+
args.push("--ro-bind", join23(preflight.source_root, protectedPath.path), `/project/${protectedPath.path}`);
|
|
90264
91248
|
}
|
|
90265
91249
|
}
|
|
90266
91250
|
for (const masked of preflight.masked_project_paths) {
|
|
@@ -90394,13 +91378,13 @@ function discoverProjectPathProtection(root, policy) {
|
|
|
90394
91378
|
try {
|
|
90395
91379
|
while (directories.length) {
|
|
90396
91380
|
const parent = directories.pop();
|
|
90397
|
-
for (const entry of readdirSync12(
|
|
91381
|
+
for (const entry of readdirSync12(join23(root, parent), { withFileTypes: true })) {
|
|
90398
91382
|
visited += 1;
|
|
90399
91383
|
if (visited > MAX_PROJECT_MASK_SCAN_ENTRIES) {
|
|
90400
91384
|
throw new RemoteCommandError("confinement_unavailable", `Project credential scan exceeds ${MAX_PROJECT_MASK_SCAN_ENTRIES} entries`);
|
|
90401
91385
|
}
|
|
90402
91386
|
const path2 = parent ? `${parent}/${entry.name}` : entry.name;
|
|
90403
|
-
const fullPath =
|
|
91387
|
+
const fullPath = join23(root, path2);
|
|
90404
91388
|
const stat3 = lstatSync9(fullPath);
|
|
90405
91389
|
const kind = entry.isDirectory() ? "directory" : "file";
|
|
90406
91390
|
let masked = policy ? policy.isPrivate(path2, kind === "directory") || path2.toLowerCase() === ".git/config" : classifyProjectFileReadRisk(path2).isHighRisk || path2.toLowerCase() === ".git/config";
|
|
@@ -90557,7 +91541,7 @@ function stableJson4(value) {
|
|
|
90557
91541
|
return JSON.stringify(value);
|
|
90558
91542
|
}
|
|
90559
91543
|
function sha2565(value) {
|
|
90560
|
-
return
|
|
91544
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
90561
91545
|
}
|
|
90562
91546
|
function deepFreeze(value) {
|
|
90563
91547
|
if (value && typeof value === "object") {
|
|
@@ -90572,7 +91556,7 @@ import { lookup as dnsLookup } from "dns/promises";
|
|
|
90572
91556
|
import { chmodSync as chmodSync11, mkdtempSync as mkdtempSync4, realpathSync as realpathSync8, rmSync as rmSync10, statSync as statSync7, writeFileSync as writeFileSync11 } from "fs";
|
|
90573
91557
|
import { isIP as isIP2, createConnection, createServer } from "net";
|
|
90574
91558
|
import { tmpdir as tmpdir6 } from "os";
|
|
90575
|
-
import { isAbsolute as isAbsolute9, join as
|
|
91559
|
+
import { isAbsolute as isAbsolute9, join as join24, relative as relative8 } from "path";
|
|
90576
91560
|
var DEFAULT_BRIDGE_PORT = 43117;
|
|
90577
91561
|
var MAX_CONNECT_HEADER_BYTES = 8 * 1024;
|
|
90578
91562
|
var CONNECT_HEADER_TIMEOUT_MS = 1e4;
|
|
@@ -90648,8 +91632,8 @@ async function prepareRemoteHttpsConnectNetworkConfinement(claim, preflight, opt
|
|
|
90648
91632
|
throw capabilityError(`HTTPS network profiles require the trusted Node runtime in an approved toolchain (${nodeExecutable})`);
|
|
90649
91633
|
}
|
|
90650
91634
|
const controlDirectory = createPrivateControlDirectory(preflight, options.controlRoots);
|
|
90651
|
-
const socketPath =
|
|
90652
|
-
const bridgePath =
|
|
91635
|
+
const socketPath = join24(controlDirectory, "proxy.sock");
|
|
91636
|
+
const bridgePath = join24(controlDirectory, "bridge.mjs");
|
|
90653
91637
|
writeFileSync11(bridgePath, BRIDGE_SOURCE, { encoding: "utf8", mode: 320, flag: "wx" });
|
|
90654
91638
|
const activeSockets = /* @__PURE__ */ new Set();
|
|
90655
91639
|
const server2 = createServer((client2) => {
|
|
@@ -90778,7 +91762,7 @@ function createPrivateControlDirectory(preflight, configuredRoots) {
|
|
|
90778
91762
|
try {
|
|
90779
91763
|
if (!isAbsolute9(candidate) || !statSync7(candidate).isDirectory()) continue;
|
|
90780
91764
|
const root = realpathSync8(candidate);
|
|
90781
|
-
const directory = mkdtempSync4(
|
|
91765
|
+
const directory = mkdtempSync4(join24(root, "openmates-command-network-"));
|
|
90782
91766
|
chmodSync11(directory, 448);
|
|
90783
91767
|
const exposed = preflight.source_root === directory || isInside6(preflight.source_root, directory) || preflight.toolchain_paths.some((path2) => isInside6(path2, directory)) || preflight.writable_targets.some((target) => isInside6(target.host_path, directory));
|
|
90784
91768
|
if (!exposed) return directory;
|
|
@@ -90835,9 +91819,9 @@ function capabilityError(message) {
|
|
|
90835
91819
|
}
|
|
90836
91820
|
|
|
90837
91821
|
// src/remoteCommandResourceGrants.ts
|
|
90838
|
-
import { createHash as
|
|
91822
|
+
import { createHash as createHash28 } from "crypto";
|
|
90839
91823
|
import { chmodSync as chmodSync12, existsSync as existsSync22, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, realpathSync as realpathSync9, renameSync as renameSync9, statSync as statSync8, writeFileSync as writeFileSync12 } from "fs";
|
|
90840
|
-
import { isAbsolute as isAbsolute10, join as
|
|
91824
|
+
import { isAbsolute as isAbsolute10, join as join25, relative as relative9 } from "path";
|
|
90841
91825
|
var STORE_FILE = "remote-command-resource-grants.json";
|
|
90842
91826
|
var ID = /^[a-z][a-z0-9-]{0,63}$/;
|
|
90843
91827
|
var ENV = /^[A-Z_][A-Z0-9_]{0,127}$/;
|
|
@@ -90930,7 +91914,7 @@ function resolveRemoteCommandResourceGrants(context) {
|
|
|
90930
91914
|
return { writable_targets, network_profiles, credential_profiles };
|
|
90931
91915
|
}
|
|
90932
91916
|
function definitionDigest(kind, definition) {
|
|
90933
|
-
return
|
|
91917
|
+
return createHash28("sha256").update(JSON.stringify(["openmates-command-resource-v1", kind, definition])).digest("hex");
|
|
90934
91918
|
}
|
|
90935
91919
|
function upsert(grant) {
|
|
90936
91920
|
validateIdentity(grant.project_id, grant.source_id, grant.profile_id);
|
|
@@ -90938,7 +91922,7 @@ function upsert(grant) {
|
|
|
90938
91922
|
writeStore([...others, grant]);
|
|
90939
91923
|
}
|
|
90940
91924
|
function readStore() {
|
|
90941
|
-
const path2 =
|
|
91925
|
+
const path2 = join25(resolveStateDir(), STORE_FILE);
|
|
90942
91926
|
if (!existsSync22(path2)) return [];
|
|
90943
91927
|
const stat3 = lstatSync10(path2);
|
|
90944
91928
|
if (!stat3.isFile() || stat3.isSymbolicLink() || (stat3.mode & 63) !== 0) throw new Error("Remote command resource grant store is not private");
|
|
@@ -90950,7 +91934,7 @@ function writeStore(grants) {
|
|
|
90950
91934
|
const directory = resolveStateDir();
|
|
90951
91935
|
mkdirSync18(directory, { recursive: true, mode: 448 });
|
|
90952
91936
|
chmodSync12(directory, 448);
|
|
90953
|
-
const path2 =
|
|
91937
|
+
const path2 = join25(directory, STORE_FILE);
|
|
90954
91938
|
if (existsSync22(path2) && lstatSync10(path2).isSymbolicLink()) throw new Error("Remote command resource grant store cannot be a symbolic link");
|
|
90955
91939
|
const temporary = `${path2}.${process.pid}.tmp`;
|
|
90956
91940
|
writeFileSync12(temporary, `${JSON.stringify({ version: 1, grants }, null, 2)}
|
|
@@ -91933,7 +92917,7 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
91933
92917
|
}
|
|
91934
92918
|
for (const entry of entries) {
|
|
91935
92919
|
if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
|
|
91936
|
-
visit(
|
|
92920
|
+
visit(join26(directory, entry.name), approvedRoot);
|
|
91937
92921
|
}
|
|
91938
92922
|
};
|
|
91939
92923
|
for (const root of roots) visit(root, root);
|
|
@@ -91951,7 +92935,7 @@ function remoteAccessHostingCandidates(roots, explicitPath, discover = discoverR
|
|
|
91951
92935
|
};
|
|
91952
92936
|
}
|
|
91953
92937
|
function remoteAccessSourceType(rootPath) {
|
|
91954
|
-
const gitMarker =
|
|
92938
|
+
const gitMarker = join26(rootPath, ".git");
|
|
91955
92939
|
return existsSync24(gitMarker) ? "local_git_repository" : "local_folder";
|
|
91956
92940
|
}
|
|
91957
92941
|
function listRemoteAccessDirectory(options) {
|
|
@@ -92008,7 +92992,7 @@ function listRemoteAccessDirectory(options) {
|
|
|
92008
92992
|
remainingChildSummaryEntries -= 1;
|
|
92009
92993
|
const childPath = `${folderPath}/${child.name}`;
|
|
92010
92994
|
if (child.name.startsWith(".") || child.isSymbolicLink() || !child.isFile() && !child.isDirectory() || childPolicy.isIgnored(childPath, child.isDirectory()) || childPolicy.isPrivate(childPath, child.isDirectory())) continue;
|
|
92011
|
-
const fileStat = child.isFile() ? lstatSync11(
|
|
92995
|
+
const fileStat = child.isFile() ? lstatSync11(join26(absoluteFolder, child.name)) : null;
|
|
92012
92996
|
if (fileStat && fileStat.nlink > 1) continue;
|
|
92013
92997
|
if (child.isDirectory()) childFolderCount += 1;
|
|
92014
92998
|
else {
|
|
@@ -92029,7 +93013,7 @@ function listRemoteAccessDirectory(options) {
|
|
|
92029
93013
|
const compareNames = (a, b) => a.localeCompare(b) || (a < b ? -1 : a > b ? 1 : 0);
|
|
92030
93014
|
for (const entry of readdirSync13(directory, { withFileTypes: true }).sort((a, b) => compareNames(a.name, b.name))) {
|
|
92031
93015
|
if (cursor !== void 0 && compareNames(entry.name, cursor) <= 0) continue;
|
|
92032
|
-
const entryPath = relative11(root,
|
|
93016
|
+
const entryPath = relative11(root, join26(directory, entry.name)).replace(/\\/g, "/");
|
|
92033
93017
|
if (entry.name.startsWith(".") || entry.isSymbolicLink() || policy.isIgnored(entryPath, entry.isDirectory()) || policy.isPrivate(entryPath, entry.isDirectory())) {
|
|
92034
93018
|
excluded += 1;
|
|
92035
93019
|
continue;
|
|
@@ -92038,7 +93022,7 @@ function listRemoteAccessDirectory(options) {
|
|
|
92038
93022
|
excluded += 1;
|
|
92039
93023
|
continue;
|
|
92040
93024
|
}
|
|
92041
|
-
const fileStat = entry.isFile() ? lstatSync11(
|
|
93025
|
+
const fileStat = entry.isFile() ? lstatSync11(join26(directory, entry.name)) : void 0;
|
|
92042
93026
|
if (fileStat && fileStat.nlink > 1) {
|
|
92043
93027
|
excluded += 1;
|
|
92044
93028
|
continue;
|
|
@@ -92100,7 +93084,7 @@ function readRemoteAccessTextFile(options) {
|
|
|
92100
93084
|
truncated,
|
|
92101
93085
|
sizeBytes: stats.size,
|
|
92102
93086
|
lineCount: countTextLines(content),
|
|
92103
|
-
expected_base: truncated ? null :
|
|
93087
|
+
expected_base: truncated ? null : createHash29("sha256").update(bytes2.subarray(0, bytesRead)).digest("hex")
|
|
92104
93088
|
};
|
|
92105
93089
|
} catch (error) {
|
|
92106
93090
|
const code = error.code;
|
|
@@ -92150,7 +93134,7 @@ function readRemoteAccessImageChunk(options) {
|
|
|
92150
93134
|
mime_type: mimeType,
|
|
92151
93135
|
size_bytes: stats.size,
|
|
92152
93136
|
offset: options.offset,
|
|
92153
|
-
content_hash:
|
|
93137
|
+
content_hash: createHash29("sha256").update(bytes2).digest("hex")
|
|
92154
93138
|
};
|
|
92155
93139
|
} catch (error) {
|
|
92156
93140
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -92198,8 +93182,8 @@ function readRemoteAccessFileChunk(options) {
|
|
|
92198
93182
|
content_base64: bytes2.toString("base64"),
|
|
92199
93183
|
size_bytes: stats.size,
|
|
92200
93184
|
offset: options.offset,
|
|
92201
|
-
file_identity:
|
|
92202
|
-
chunk_hash:
|
|
93185
|
+
file_identity: createHash29("sha256").update(`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`).digest("hex"),
|
|
93186
|
+
chunk_hash: createHash29("sha256").update(bytes2).digest("hex")
|
|
92203
93187
|
};
|
|
92204
93188
|
} catch (error) {
|
|
92205
93189
|
if (error.code === "ELOOP") throw new Error("Remote source path is a symbolic link");
|
|
@@ -92307,13 +93291,13 @@ async function runRemoteAccessBridge(options) {
|
|
|
92307
93291
|
for (const binding of options.bindings) {
|
|
92308
93292
|
const projectId = binding.source.projectId;
|
|
92309
93293
|
if (caches.has(projectId)) continue;
|
|
92310
|
-
const cache = new ProjectTaskCache(options.taskCacheRoot ??
|
|
93294
|
+
const cache = new ProjectTaskCache(options.taskCacheRoot ?? join26(resolveStateDir(), "project-task-cache"), accountScope, projectId);
|
|
92311
93295
|
releases.push(await lockfile8.lock(cache.directory, { retries: 0, stale: 12e4 }));
|
|
92312
93296
|
caches.set(projectId, cache);
|
|
92313
93297
|
}
|
|
92314
93298
|
codexBridges = startCodexTaskBridges(
|
|
92315
93299
|
options.bindings.map((item) => item.source.rootPath),
|
|
92316
|
-
[...caches.values()].map((cache) =>
|
|
93300
|
+
[...caches.values()].map((cache) => join26(cache.directory, "snapshot.json")),
|
|
92317
93301
|
resolveStateDir(),
|
|
92318
93302
|
(status) => options.onTaskSync?.({ projectId: "", directory: "", status })
|
|
92319
93303
|
);
|
|
@@ -92672,7 +93656,7 @@ function waitForAbort2(signal, timeoutMs) {
|
|
|
92672
93656
|
}
|
|
92673
93657
|
function resolveRemoteCachePath(sourceId, homeDirectory) {
|
|
92674
93658
|
assertSafeSourceId(sourceId);
|
|
92675
|
-
return
|
|
93659
|
+
return join26(remoteAccessStateDirectory(homeDirectory), "remote-cache", sourceId);
|
|
92676
93660
|
}
|
|
92677
93661
|
function startRemoteAccessSource(input) {
|
|
92678
93662
|
assertSafeSourceId(input.sourceId);
|
|
@@ -92822,7 +93806,7 @@ function searchRemoteSourceWithoutRg(request, sourceRoot, targetPath, policy) {
|
|
|
92822
93806
|
continue;
|
|
92823
93807
|
}
|
|
92824
93808
|
for (const entry of entries) {
|
|
92825
|
-
const absolutePath =
|
|
93809
|
+
const absolutePath = join26(directory, entry.name);
|
|
92826
93810
|
const relativePath = relative11(root, absolutePath).replace(/\\/g, "/");
|
|
92827
93811
|
if (entry.name === ".git" || entry.isSymbolicLink() || policy.isIgnored(relativePath, entry.isDirectory()) || policy.isPrivate(relativePath, entry.isDirectory())) {
|
|
92828
93812
|
excluded += 1;
|
|
@@ -92952,15 +93936,15 @@ function searchRemoteFileNamesWithoutRg(request, sourceRoot, targetPath, policy,
|
|
|
92952
93936
|
enumerationTruncated = true;
|
|
92953
93937
|
break;
|
|
92954
93938
|
}
|
|
92955
|
-
const path2 = relative11(sourceRoot,
|
|
93939
|
+
const path2 = relative11(sourceRoot, join26(absolute, entry.name)).replace(/\\/g, "/");
|
|
92956
93940
|
const isDirectory = entry.isDirectory();
|
|
92957
|
-
if (skipPriority && priorityAbsolute ===
|
|
93941
|
+
if (skipPriority && priorityAbsolute === join26(absolute, entry.name)) continue;
|
|
92958
93942
|
if (entry.name.startsWith(".") || entry.isSymbolicLink() || !entry.isFile() && !isDirectory || policy.isIgnored(path2, isDirectory) || policy.isPrivate(path2, isDirectory)) {
|
|
92959
93943
|
excluded += 1;
|
|
92960
93944
|
continue;
|
|
92961
93945
|
}
|
|
92962
|
-
if (isDirectory) pending.push(
|
|
92963
|
-
else if (lstatSync11(
|
|
93946
|
+
if (isDirectory) pending.push(join26(absolute, entry.name));
|
|
93947
|
+
else if (lstatSync11(join26(absolute, entry.name)).nlink > 1) {
|
|
92964
93948
|
excluded += 1;
|
|
92965
93949
|
continue;
|
|
92966
93950
|
}
|
|
@@ -93165,7 +94149,7 @@ function collectSearchPathMetadata(sourceRoot, targetPath, policy, collectDirect
|
|
|
93165
94149
|
throw new Error("Remote source search alias scan exceeded its bounded file limit");
|
|
93166
94150
|
}
|
|
93167
94151
|
if (entry.name === ".git" || entry.isSymbolicLink()) continue;
|
|
93168
|
-
const absolute =
|
|
94152
|
+
const absolute = join26(directory, entry.name);
|
|
93169
94153
|
const path2 = relative11(sourceRoot, absolute).replace(/\\/g, "/");
|
|
93170
94154
|
if (entry.isDirectory()) {
|
|
93171
94155
|
if (!policy.isIgnored(path2, true) && !policy.isPrivate(path2, true) && (!collectDirectories || !entry.name.startsWith("."))) {
|
|
@@ -93242,7 +94226,7 @@ function resolveApprovedPath(sourceRoot, relativePath) {
|
|
|
93242
94226
|
const relation = relative11(sourceRoot, lexical);
|
|
93243
94227
|
let current = sourceRoot;
|
|
93244
94228
|
for (const part of relation.split(/[\\/]/).filter(Boolean)) {
|
|
93245
|
-
current =
|
|
94229
|
+
current = join26(current, part);
|
|
93246
94230
|
if (lstatSync11(current).isSymbolicLink()) throw new Error("Remote source path is a symbolic link");
|
|
93247
94231
|
}
|
|
93248
94232
|
const canonical = realpathSync11(lexical);
|
|
@@ -93306,7 +94290,7 @@ function remoteAccessStateDirectory(homeDirectory) {
|
|
|
93306
94290
|
return homeDirectory === void 0 ? resolveStateDir() : resolveStateDir({ homeDir: homeDirectory, stateDir: "", profile: "" });
|
|
93307
94291
|
}
|
|
93308
94292
|
function remoteAccessStorePath(homeDirectory) {
|
|
93309
|
-
return
|
|
94293
|
+
return join26(remoteAccessStateDirectory(homeDirectory), "remote-sources.json");
|
|
93310
94294
|
}
|
|
93311
94295
|
function saveRemoteAccessSources(sources, homeDirectory) {
|
|
93312
94296
|
const filePath = remoteAccessStorePath(homeDirectory);
|
|
@@ -93390,11 +94374,11 @@ function inheritedInstaller(executable, args) {
|
|
|
93390
94374
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
93391
94375
|
import { readFileSync as readFileSync22, mkdirSync as mkdirSync20, writeFileSync as writeFileSync14, renameSync as renameSync10 } from "fs";
|
|
93392
94376
|
import { homedir as homedir7 } from "os";
|
|
93393
|
-
import { dirname as dirname14, basename as basename8, join as
|
|
94377
|
+
import { dirname as dirname14, basename as basename8, join as join27 } from "path";
|
|
93394
94378
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
93395
94379
|
var PACKAGE_NAME = "openmates";
|
|
93396
94380
|
function channelFile() {
|
|
93397
|
-
return
|
|
94381
|
+
return join27(homedir7(), ".openmates", "updates.json");
|
|
93398
94382
|
}
|
|
93399
94383
|
function parseChannel(value) {
|
|
93400
94384
|
if (value === "dev") return "dev";
|
|
@@ -93764,7 +94748,7 @@ import { execFileSync as execFileSync6 } from "child_process";
|
|
|
93764
94748
|
import { createSign, randomUUID as randomUUID22 } from "crypto";
|
|
93765
94749
|
import { chmodSync as chmodSync14, existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync23 } from "fs";
|
|
93766
94750
|
import { homedir as homedir8 } from "os";
|
|
93767
|
-
import { join as
|
|
94751
|
+
import { join as join28, resolve as resolve17 } from "path";
|
|
93768
94752
|
var REVOLUT_BUSINESS_CERTIFICATE_DOCS_URL = "https://developer.revolut.com/docs/guides/manage-accounts/get-started/make-your-first-api-request#generate-a-private-and-a-public-certificate";
|
|
93769
94753
|
var REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI = "https://app.dev.openmates.org/oauth/revolut-business/callback";
|
|
93770
94754
|
var REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI = "https://openmates.org/oauth/revolut-business/callback";
|
|
@@ -93774,7 +94758,7 @@ var REVOLUT_BUSINESS_AUDIENCE = "https://revolut.com";
|
|
|
93774
94758
|
var CERTIFICATE_DAYS = "1825";
|
|
93775
94759
|
var RSA_BITS = "2048";
|
|
93776
94760
|
function defaultRevolutBusinessCertificateDir(environment) {
|
|
93777
|
-
return
|
|
94761
|
+
return join28(homedir8(), ".openmates", "revolut-business", environment);
|
|
93778
94762
|
}
|
|
93779
94763
|
function defaultRevolutBusinessRedirectUri(environment) {
|
|
93780
94764
|
return environment === "sandbox" ? REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI : REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI;
|
|
@@ -93797,7 +94781,7 @@ async function exchangeRevolutBusinessAuthorizationCode(options) {
|
|
|
93797
94781
|
if (!clientId) throw new Error("Missing Revolut Business client ID.");
|
|
93798
94782
|
const code = extractRevolutBusinessAuthorizationCode(options.codeOrRedirectUrl);
|
|
93799
94783
|
if (!code) throw new Error("Missing Revolut Business authorization code.");
|
|
93800
|
-
const privateKeyPem = options.privateKeyPem ?? readFileSync23(options.privateKeyPath ??
|
|
94784
|
+
const privateKeyPem = options.privateKeyPem ?? readFileSync23(options.privateKeyPath ?? join28(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
|
|
93801
94785
|
const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
|
|
93802
94786
|
const clientAssertion = generateRevolutBusinessClientAssertion({ clientId, privateKeyPem, redirectUri });
|
|
93803
94787
|
const response = await fetch(environment === "sandbox" ? "https://sandbox-b2b.revolut.com/api/1.0/auth/token" : "https://b2b.revolut.com/api/1.0/auth/token", {
|
|
@@ -93852,8 +94836,8 @@ function generateRevolutBusinessCertificate(options = {}) {
|
|
|
93852
94836
|
if (!redirectUri.startsWith("https://")) throw new Error("Revolut OAuth redirect URI must start with https://.");
|
|
93853
94837
|
mkdirSync22(outputDir, { recursive: true, mode: 448 });
|
|
93854
94838
|
chmodSync14(outputDir, 448);
|
|
93855
|
-
const privateKeyPath =
|
|
93856
|
-
const publicCertificatePath =
|
|
94839
|
+
const privateKeyPath = join28(outputDir, "privatecert.pem");
|
|
94840
|
+
const publicCertificatePath = join28(outputDir, "publiccert.cer");
|
|
93857
94841
|
if (!options.overwrite && (existsSync25(privateKeyPath) || existsSync25(publicCertificatePath))) {
|
|
93858
94842
|
throw new Error(`Revolut certificate files already exist in ${outputDir}. Re-run with --overwrite to replace them.`);
|
|
93859
94843
|
}
|
|
@@ -95548,7 +96532,7 @@ async function handlePlans(client2, subcommand, rest, flags, redactor) {
|
|
|
95548
96532
|
if (action === "diff") throw new Error("Revision diffs are reviewed in the authenticated web review page; use submit-for-review to print its URL.");
|
|
95549
96533
|
if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
|
|
95550
96534
|
const snapshot2 = JSON.stringify(planToJson(plan));
|
|
95551
|
-
const revision = await client2.submitPlanRevision(plan.planId, { fingerprint:
|
|
96535
|
+
const revision = await client2.submitPlanRevision(plan.planId, { fingerprint: createHash30("sha256").update(snapshot2).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot2), created_at: nowSeconds3() });
|
|
95552
96536
|
const reviewUrl = `${deriveAppUrl(client2.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
|
|
95553
96537
|
if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
|
|
95554
96538
|
else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
|
|
@@ -96468,7 +97452,7 @@ async function handleProjects(client2, subcommand, rest, flags, redactor) {
|
|
|
96468
97452
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
96469
97453
|
key_wrappers: wrapping.teamId ? [{
|
|
96470
97454
|
key_type: "team",
|
|
96471
|
-
hashed_team_id:
|
|
97455
|
+
hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
|
|
96472
97456
|
team_key_epoch: 1,
|
|
96473
97457
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
96474
97458
|
wrapper_version: 1,
|
|
@@ -97844,7 +98828,7 @@ async function createEncryptedRemoteAccessProject(client2, name, rootPath, write
|
|
|
97844
98828
|
encrypted_settings: await encryptWithAesGcmCombined(JSON.stringify({ default_focus: defaultFocus }), projectKey),
|
|
97845
98829
|
key_wrappers: wrapping.teamId ? [{
|
|
97846
98830
|
key_type: "team",
|
|
97847
|
-
hashed_team_id:
|
|
98831
|
+
hashed_team_id: createHash30("sha256").update(wrapping.teamId).digest("hex"),
|
|
97848
98832
|
team_key_epoch: 1,
|
|
97849
98833
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
97850
98834
|
wrapper_version: 1,
|
|
@@ -97918,7 +98902,7 @@ function readRootProjectInstructions(rootPath) {
|
|
|
97918
98902
|
return left.localeCompare(right);
|
|
97919
98903
|
});
|
|
97920
98904
|
const source = matchingNames[0];
|
|
97921
|
-
if (source) return { instructions: readFileSync24(
|
|
98905
|
+
if (source) return { instructions: readFileSync24(join29(rootPath, source), "utf8"), source };
|
|
97922
98906
|
}
|
|
97923
98907
|
return null;
|
|
97924
98908
|
}
|
|
@@ -98817,31 +99801,31 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
98817
99801
|
} catch {
|
|
98818
99802
|
}
|
|
98819
99803
|
}
|
|
98820
|
-
const { mkdir:
|
|
98821
|
-
const { join:
|
|
99804
|
+
const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
|
|
99805
|
+
const { join: join30 } = await import("path");
|
|
98822
99806
|
if (useZip) {
|
|
98823
|
-
const tmpDir =
|
|
98824
|
-
await
|
|
98825
|
-
await
|
|
98826
|
-
await
|
|
99807
|
+
const tmpDir = join30(outputDir, `.${filenameBase}_tmp`);
|
|
99808
|
+
await mkdir4(tmpDir, { recursive: true });
|
|
99809
|
+
await writeFile3(join30(tmpDir, `${filenameBase}.yml`), yamlContent);
|
|
99810
|
+
await writeFile3(join30(tmpDir, `${filenameBase}.md`), mdContent);
|
|
98827
99811
|
if (codeEmbeds.length > 0) {
|
|
98828
99812
|
for (const ce of codeEmbeds) {
|
|
98829
99813
|
const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
|
|
98830
|
-
const fullPath =
|
|
98831
|
-
await
|
|
99814
|
+
const fullPath = join30(tmpDir, "code", fpath);
|
|
99815
|
+
await mkdir4(fullPath.substring(0, fullPath.lastIndexOf("/")), {
|
|
98832
99816
|
recursive: true
|
|
98833
99817
|
});
|
|
98834
|
-
await
|
|
99818
|
+
await writeFile3(fullPath, ce.content);
|
|
98835
99819
|
}
|
|
98836
99820
|
}
|
|
98837
99821
|
if (transcriptEmbeds.length > 0) {
|
|
98838
|
-
const tDir =
|
|
98839
|
-
await
|
|
99822
|
+
const tDir = join30(tmpDir, "transcripts");
|
|
99823
|
+
await mkdir4(tDir, { recursive: true });
|
|
98840
99824
|
for (const te of transcriptEmbeds) {
|
|
98841
|
-
await
|
|
99825
|
+
await writeFile3(join30(tDir, te.filename), te.content);
|
|
98842
99826
|
}
|
|
98843
99827
|
}
|
|
98844
|
-
const zipPath =
|
|
99828
|
+
const zipPath = join30(outputDir, `${filenameBase}.zip`);
|
|
98845
99829
|
const { execSync: execSync2 } = await import("child_process");
|
|
98846
99830
|
try {
|
|
98847
99831
|
execSync2(`cd "${tmpDir}" && zip -r "${zipPath}" .`, { stdio: "pipe" });
|
|
@@ -98856,29 +99840,29 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
98856
99840
|
);
|
|
98857
99841
|
}
|
|
98858
99842
|
} else {
|
|
98859
|
-
const chatDir =
|
|
98860
|
-
await
|
|
99843
|
+
const chatDir = join30(outputDir, filenameBase);
|
|
99844
|
+
await mkdir4(chatDir, { recursive: true });
|
|
98861
99845
|
const written = [];
|
|
98862
|
-
await
|
|
99846
|
+
await writeFile3(join30(chatDir, `${filenameBase}.yml`), yamlContent);
|
|
98863
99847
|
written.push(`${filenameBase}.yml`);
|
|
98864
|
-
await
|
|
99848
|
+
await writeFile3(join30(chatDir, `${filenameBase}.md`), mdContent);
|
|
98865
99849
|
written.push(`${filenameBase}.md`);
|
|
98866
99850
|
if (codeEmbeds.length > 0) {
|
|
98867
99851
|
for (const ce of codeEmbeds) {
|
|
98868
99852
|
const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
|
|
98869
|
-
const fullPath =
|
|
98870
|
-
await
|
|
99853
|
+
const fullPath = join30(chatDir, "code", fpath);
|
|
99854
|
+
await mkdir4(fullPath.substring(0, fullPath.lastIndexOf("/")), {
|
|
98871
99855
|
recursive: true
|
|
98872
99856
|
});
|
|
98873
|
-
await
|
|
99857
|
+
await writeFile3(fullPath, ce.content);
|
|
98874
99858
|
written.push(`code/${fpath}`);
|
|
98875
99859
|
}
|
|
98876
99860
|
}
|
|
98877
99861
|
if (transcriptEmbeds.length > 0) {
|
|
98878
|
-
const tDir =
|
|
98879
|
-
await
|
|
99862
|
+
const tDir = join30(chatDir, "transcripts");
|
|
99863
|
+
await mkdir4(tDir, { recursive: true });
|
|
98880
99864
|
for (const te of transcriptEmbeds) {
|
|
98881
|
-
await
|
|
99865
|
+
await writeFile3(join30(tDir, te.filename), te.content);
|
|
98882
99866
|
written.push(`transcripts/${te.filename}`);
|
|
98883
99867
|
}
|
|
98884
99868
|
}
|
|
@@ -98914,7 +99898,7 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
98914
99898
|
printJson2({
|
|
98915
99899
|
chat_id: chat.id,
|
|
98916
99900
|
title: chat.title,
|
|
98917
|
-
output_dir: useZip ?
|
|
99901
|
+
output_dir: useZip ? join30(outputDir, `${filenameBase}.zip`) : join30(outputDir, filenameBase),
|
|
98918
99902
|
files,
|
|
98919
99903
|
code_embeds: codeEmbeds.length,
|
|
98920
99904
|
transcript_embeds: transcriptEmbeds.length
|
|
@@ -99235,7 +100219,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
99235
100219
|
const messages = rawMessages.map((message) => {
|
|
99236
100220
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
99237
100221
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
99238
|
-
const hashedMessageId = messageId ?
|
|
100222
|
+
const hashedMessageId = messageId ? createHash30("sha256").update(messageId).digest("hex") : null;
|
|
99239
100223
|
const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
|
|
99240
100224
|
return {
|
|
99241
100225
|
id: messageId,
|
|
@@ -101381,8 +102365,8 @@ function printAccountExportBundle(bundle, flags) {
|
|
|
101381
102365
|
}
|
|
101382
102366
|
var ACCOUNT_IMPORT_SOURCES = /* @__PURE__ */ new Set(["openmates", "chatgpt", "claude", "gemini", "opencode", "other"]);
|
|
101383
102367
|
async function parseAccountImportFile(parserFormat, file, flags = {}) {
|
|
101384
|
-
const { readFile:
|
|
101385
|
-
const payload = await
|
|
102368
|
+
const { readFile: readFile3 } = await import("fs/promises");
|
|
102369
|
+
const payload = await readFile3(file);
|
|
101386
102370
|
const requestedSource = typeof flags.source === "string" ? flags.source : parserFormat;
|
|
101387
102371
|
if (!ACCOUNT_IMPORT_SOURCES.has(requestedSource)) throw new Error(`Unsupported account import source: ${requestedSource}`);
|
|
101388
102372
|
const selectedSource = requestedSource;
|
|
@@ -101762,13 +102746,13 @@ function parseYamlScalar(value) {
|
|
|
101762
102746
|
return trimmed;
|
|
101763
102747
|
}
|
|
101764
102748
|
async function saveDownloadedDocument(document, output) {
|
|
101765
|
-
const { mkdir:
|
|
101766
|
-
const { join:
|
|
102749
|
+
const { mkdir: mkdir4, writeFile: writeFile3 } = await import("fs/promises");
|
|
102750
|
+
const { join: join30, basename: basename10, dirname: dirname17 } = await import("path");
|
|
101767
102751
|
const target = typeof output === "string" ? output : ".";
|
|
101768
102752
|
const filename = basename10(document.filename || "document.pdf");
|
|
101769
|
-
const filePath = target.endsWith(".pdf") ? target :
|
|
101770
|
-
await
|
|
101771
|
-
await
|
|
102753
|
+
const filePath = target.endsWith(".pdf") ? target : join30(target, filename);
|
|
102754
|
+
await mkdir4(dirname17(filePath), { recursive: true });
|
|
102755
|
+
await writeFile3(filePath, document.data);
|
|
101772
102756
|
return filePath;
|
|
101773
102757
|
}
|
|
101774
102758
|
function printMates(json) {
|
|
@@ -102233,7 +103217,7 @@ async function promptPlainText(question) {
|
|
|
102233
103217
|
}
|
|
102234
103218
|
}
|
|
102235
103219
|
async function writeSecretFile(filePath, content, force = false) {
|
|
102236
|
-
const { mkdir:
|
|
103220
|
+
const { mkdir: mkdir4, writeFile: writeFile3, stat: stat3 } = await import("fs/promises");
|
|
102237
103221
|
const { dirname: dirname17 } = await import("path");
|
|
102238
103222
|
try {
|
|
102239
103223
|
await stat3(filePath);
|
|
@@ -102244,8 +103228,8 @@ async function writeSecretFile(filePath, content, force = false) {
|
|
|
102244
103228
|
}
|
|
102245
103229
|
if (error instanceof Error && !("code" in error)) throw error;
|
|
102246
103230
|
}
|
|
102247
|
-
await
|
|
102248
|
-
await
|
|
103231
|
+
await mkdir4(dirname17(filePath), { recursive: true });
|
|
103232
|
+
await writeFile3(filePath, content, { mode: 384 });
|
|
102249
103233
|
return filePath;
|
|
102250
103234
|
}
|
|
102251
103235
|
async function generateProvisioningPassword() {
|
|
@@ -102611,8 +103595,8 @@ async function handleSettings(client2, subcommand, rest, flags) {
|
|
|
102611
103595
|
if (matches(tokens, ["account", "import-chat"])) {
|
|
102612
103596
|
const file = rest[1];
|
|
102613
103597
|
if (!file) throw new Error("Missing import file. Example: openmates settings account import-chat ./chat.yml");
|
|
102614
|
-
const { readFile:
|
|
102615
|
-
const content = await
|
|
103598
|
+
const { readFile: readFile3 } = await import("fs/promises");
|
|
103599
|
+
const content = await readFile3(file, "utf-8");
|
|
102616
103600
|
await printSettingsMutationResult(
|
|
102617
103601
|
client2.settingsPost("import-chat", parseChatImportPayload(content)),
|
|
102618
103602
|
flags
|
|
@@ -106341,19 +107325,19 @@ async function handleDocs(client2, subcommand, rest, flags) {
|
|
|
106341
107325
|
return;
|
|
106342
107326
|
}
|
|
106343
107327
|
if (subcommand === "download") {
|
|
106344
|
-
const { writeFile:
|
|
106345
|
-
const { join:
|
|
107328
|
+
const { writeFile: writeFile3, mkdir: mkdir4 } = await import("fs/promises");
|
|
107329
|
+
const { join: join30, dirname: dirname17 } = await import("path");
|
|
106346
107330
|
if (flags.all === true) {
|
|
106347
107331
|
const outputDir = typeof flags.output === "string" ? flags.output : "./openmates-docs";
|
|
106348
107332
|
const tree = await client2.listDocs();
|
|
106349
107333
|
const slugs = collectSlugs(tree);
|
|
106350
|
-
await
|
|
107334
|
+
await mkdir4(outputDir, { recursive: true });
|
|
106351
107335
|
let count = 0;
|
|
106352
107336
|
for (const slug2 of slugs) {
|
|
106353
107337
|
const content2 = await client2.getDoc(slug2);
|
|
106354
|
-
const filePath =
|
|
106355
|
-
await
|
|
106356
|
-
await
|
|
107338
|
+
const filePath = join30(outputDir, `${slug2}.md`);
|
|
107339
|
+
await mkdir4(dirname17(filePath), { recursive: true });
|
|
107340
|
+
await writeFile3(filePath, content2, "utf-8");
|
|
106357
107341
|
count++;
|
|
106358
107342
|
process.stderr.write(`\r Downloaded ${count}/${slugs.length}`);
|
|
106359
107343
|
}
|
|
@@ -106372,7 +107356,7 @@ async function handleDocs(client2, subcommand, rest, flags) {
|
|
|
106372
107356
|
}
|
|
106373
107357
|
const content = await client2.getDoc(slug);
|
|
106374
107358
|
const filename = typeof flags.output === "string" ? flags.output : `${slug.split("/").pop()}.md`;
|
|
106375
|
-
await
|
|
107359
|
+
await writeFile3(filename, content, "utf-8");
|
|
106376
107360
|
console.log(`Saved to ${filename}`);
|
|
106377
107361
|
return;
|
|
106378
107362
|
}
|