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