@plinth-music/cli 0.23.2 → 0.23.3
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/CHANGELOG.md +44 -0
- package/dist/build-stamp.json +2 -2
- package/dist/cli.js +972 -252
- package/package.json +4 -1
package/dist/cli.js
CHANGED
|
@@ -4671,7 +4671,7 @@ var require_omap = __commonJS((exports, module) => {
|
|
|
4671
4671
|
function resolveYamlOmap(data) {
|
|
4672
4672
|
if (data === null)
|
|
4673
4673
|
return true;
|
|
4674
|
-
var objectKeys =
|
|
4674
|
+
var objectKeys = {}, index, length, pair, pairKey, pairHasKey, object = data;
|
|
4675
4675
|
for (index = 0, length = object.length;index < length; index += 1) {
|
|
4676
4676
|
pair = object[index];
|
|
4677
4677
|
pairHasKey = false;
|
|
@@ -4687,10 +4687,9 @@ var require_omap = __commonJS((exports, module) => {
|
|
|
4687
4687
|
}
|
|
4688
4688
|
if (!pairHasKey)
|
|
4689
4689
|
return false;
|
|
4690
|
-
if (
|
|
4691
|
-
objectKeys.push(pairKey);
|
|
4692
|
-
else
|
|
4690
|
+
if (_hasOwnProperty.call(objectKeys, pairKey))
|
|
4693
4691
|
return false;
|
|
4692
|
+
Object.defineProperty(objectKeys, pairKey, { value: true });
|
|
4694
4693
|
}
|
|
4695
4694
|
return true;
|
|
4696
4695
|
}
|
|
@@ -5033,6 +5032,7 @@ var require_loader = __commonJS((exports, module) => {
|
|
|
5033
5032
|
this.legacy = options2["legacy"] || false;
|
|
5034
5033
|
this.json = options2["json"] || false;
|
|
5035
5034
|
this.listener = options2["listener"] || null;
|
|
5035
|
+
this.maxTotalMergeKeys = typeof options2["maxTotalMergeKeys"] === "number" ? options2["maxTotalMergeKeys"] : 1e4;
|
|
5036
5036
|
this.implicitTypes = this.schema.compiledImplicit;
|
|
5037
5037
|
this.typeMap = this.schema.compiledTypeMap;
|
|
5038
5038
|
this.length = input.length;
|
|
@@ -5040,6 +5040,7 @@ var require_loader = __commonJS((exports, module) => {
|
|
|
5040
5040
|
this.line = 0;
|
|
5041
5041
|
this.lineStart = 0;
|
|
5042
5042
|
this.lineIndent = 0;
|
|
5043
|
+
this.totalMergeKeys = 0;
|
|
5043
5044
|
this.documents = [];
|
|
5044
5045
|
}
|
|
5045
5046
|
function generateError(state, message) {
|
|
@@ -5113,14 +5114,22 @@ var require_loader = __commonJS((exports, module) => {
|
|
|
5113
5114
|
state.result += _result;
|
|
5114
5115
|
}
|
|
5115
5116
|
}
|
|
5117
|
+
function chargeMergeWork(state) {
|
|
5118
|
+
state.totalMergeKeys += 1;
|
|
5119
|
+
if (state.maxTotalMergeKeys !== -1 && state.totalMergeKeys > state.maxTotalMergeKeys) {
|
|
5120
|
+
throwError(state, "merge keys exceeded maxTotalMergeKeys (" + state.maxTotalMergeKeys + ")");
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5116
5123
|
function mergeMappings(state, destination, source, overridableKeys) {
|
|
5117
5124
|
var sourceKeys, key, index, quantity;
|
|
5118
5125
|
if (!common.isObject(source)) {
|
|
5119
5126
|
throwError(state, "cannot merge mappings; the provided source object is unacceptable");
|
|
5120
5127
|
}
|
|
5128
|
+
chargeMergeWork(state);
|
|
5121
5129
|
sourceKeys = Object.keys(source);
|
|
5122
5130
|
for (index = 0, quantity = sourceKeys.length;index < quantity; index += 1) {
|
|
5123
5131
|
key = sourceKeys[index];
|
|
5132
|
+
chargeMergeWork(state);
|
|
5124
5133
|
if (!_hasOwnProperty.call(destination, key)) {
|
|
5125
5134
|
setProperty(destination, key, source[key]);
|
|
5126
5135
|
overridableKeys[key] = true;
|
|
@@ -5149,6 +5158,9 @@ var require_loader = __commonJS((exports, module) => {
|
|
|
5149
5158
|
}
|
|
5150
5159
|
if (keyTag === "tag:yaml.org,2002:merge") {
|
|
5151
5160
|
if (Array.isArray(valueNode)) {
|
|
5161
|
+
if (valueNode.length > 100) {
|
|
5162
|
+
throwError(state, "abnormal merge sequence size");
|
|
5163
|
+
}
|
|
5152
5164
|
for (index = 0, quantity = valueNode.length;index < quantity; index += 1) {
|
|
5153
5165
|
mergeMappings(state, _result, valueNode[index], overridableKeys);
|
|
5154
5166
|
}
|
|
@@ -7584,7 +7596,7 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
|
|
|
7584
7596
|
// package.json
|
|
7585
7597
|
var package_default = {
|
|
7586
7598
|
name: "@plinth-music/cli",
|
|
7587
|
-
version: "0.23.
|
|
7599
|
+
version: "0.23.3",
|
|
7588
7600
|
description: "Plinth capstone sync daemon and CLI",
|
|
7589
7601
|
type: "module",
|
|
7590
7602
|
private: false,
|
|
@@ -7628,6 +7640,9 @@ var package_default = {
|
|
|
7628
7640
|
"gray-matter": "^4.0.3",
|
|
7629
7641
|
ora: "^9.4.0",
|
|
7630
7642
|
zod: "^4.4.3"
|
|
7643
|
+
},
|
|
7644
|
+
overrides: {
|
|
7645
|
+
"js-yaml": "3.15.2"
|
|
7631
7646
|
}
|
|
7632
7647
|
};
|
|
7633
7648
|
|
|
@@ -26404,6 +26419,23 @@ var RejectedWriteSchema = exports_external.object({
|
|
|
26404
26419
|
max_bytes: exports_external.number().int().nonnegative().optional(),
|
|
26405
26420
|
reported_at: exports_external.string().datetime({ offset: true }).optional()
|
|
26406
26421
|
});
|
|
26422
|
+
var FilePullHoldSchema = exports_external.object({
|
|
26423
|
+
target: exports_external.string().min(1),
|
|
26424
|
+
path: exports_external.string().min(1),
|
|
26425
|
+
kind: exports_external.string().min(1),
|
|
26426
|
+
row_id: exports_external.string().min(1),
|
|
26427
|
+
row_updated_at: exports_external.string().min(1),
|
|
26428
|
+
row_hash: exports_external.string().min(1),
|
|
26429
|
+
reason: exports_external.string().optional(),
|
|
26430
|
+
code: exports_external.string().optional(),
|
|
26431
|
+
local: exports_external.object({
|
|
26432
|
+
size: exports_external.number().int().nonnegative(),
|
|
26433
|
+
mtime_ms: exports_external.number(),
|
|
26434
|
+
ino: exports_external.number().int().nonnegative()
|
|
26435
|
+
}).nullable().optional(),
|
|
26436
|
+
attempts: exports_external.number().int().positive(),
|
|
26437
|
+
next_retry_at: exports_external.string().optional()
|
|
26438
|
+
});
|
|
26407
26439
|
var TeamFileRejectionSchema = exports_external.object({
|
|
26408
26440
|
path: exports_external.string().min(1),
|
|
26409
26441
|
reason: exports_external.string().min(1),
|
|
@@ -26431,6 +26463,7 @@ var WorkspaceStateV2Schema = exports_external.object({
|
|
|
26431
26463
|
files: FileEntriesSchema.optional(),
|
|
26432
26464
|
unsynced: exports_external.array(UnsyncedOpSchema).optional(),
|
|
26433
26465
|
rejected: exports_external.array(RejectedWriteSchema).optional(),
|
|
26466
|
+
file_pull_holds: exports_external.array(FilePullHoldSchema).optional(),
|
|
26434
26467
|
workspace_rules: WorkspaceRulesStateSchema.optional(),
|
|
26435
26468
|
workspace_memory: WorkspaceMemoryStateSchema.optional(),
|
|
26436
26469
|
user_memory: UserMemoryStateSchema.optional(),
|
|
@@ -26526,25 +26559,50 @@ function isLaterVersion(a, b) {
|
|
|
26526
26559
|
return ta > tb;
|
|
26527
26560
|
}
|
|
26528
26561
|
function committedEntryIfNewer(workspaceSlug, entityType, entry) {
|
|
26562
|
+
const committed = readCommittedEntry(workspaceSlug, entityType, entry.id);
|
|
26563
|
+
return isLaterVersion(committed, entry) ? committed : undefined;
|
|
26564
|
+
}
|
|
26565
|
+
function readCommittedEntry(workspaceSlug, entityType, id) {
|
|
26566
|
+
const parsed = readCommittedJson(workspaceSlug);
|
|
26567
|
+
const entries = parsed?.entries;
|
|
26568
|
+
const committed = entries?.[entityType]?.[id];
|
|
26569
|
+
if (committed === undefined)
|
|
26570
|
+
return;
|
|
26571
|
+
const valid = StateEntrySchema.safeParse(committed);
|
|
26572
|
+
return valid.success ? valid.data : undefined;
|
|
26573
|
+
}
|
|
26574
|
+
function readCommittedFileEntry(workspaceSlug, relPath) {
|
|
26575
|
+
const read = readCommittedFileAck(workspaceSlug, relPath);
|
|
26576
|
+
return read.kind === "entry" ? read.entry : undefined;
|
|
26577
|
+
}
|
|
26578
|
+
function readCommittedFileAck(workspaceSlug, relPath) {
|
|
26579
|
+
const json2 = readCommittedJson(workspaceSlug);
|
|
26580
|
+
if (!json2)
|
|
26581
|
+
return { kind: "unknown" };
|
|
26582
|
+
const files = json2.files;
|
|
26583
|
+
if (files === undefined)
|
|
26584
|
+
return { kind: "absent" };
|
|
26585
|
+
if (files === null || typeof files !== "object")
|
|
26586
|
+
return { kind: "unknown" };
|
|
26587
|
+
const committed = files[relPath];
|
|
26588
|
+
if (committed === undefined)
|
|
26589
|
+
return { kind: "absent" };
|
|
26590
|
+
const valid = FileStateEntrySchema.safeParse(committed);
|
|
26591
|
+
return valid.success ? { kind: "entry", entry: valid.data } : { kind: "unknown" };
|
|
26592
|
+
}
|
|
26593
|
+
function readCommittedJson(workspaceSlug) {
|
|
26529
26594
|
let raw;
|
|
26530
26595
|
try {
|
|
26531
26596
|
raw = readFileSync7(stateFilePath(workspaceSlug), "utf8");
|
|
26532
26597
|
} catch {
|
|
26533
26598
|
return;
|
|
26534
26599
|
}
|
|
26535
|
-
let committed;
|
|
26536
26600
|
try {
|
|
26537
26601
|
const parsed = JSON.parse(raw);
|
|
26538
|
-
|
|
26602
|
+
return parsed !== null && typeof parsed === "object" ? parsed : undefined;
|
|
26539
26603
|
} catch {
|
|
26540
26604
|
return;
|
|
26541
26605
|
}
|
|
26542
|
-
if (committed === undefined)
|
|
26543
|
-
return;
|
|
26544
|
-
const valid = StateEntrySchema.safeParse(committed);
|
|
26545
|
-
if (!valid.success)
|
|
26546
|
-
return;
|
|
26547
|
-
return isLaterVersion(valid.data, entry) ? valid.data : undefined;
|
|
26548
26606
|
}
|
|
26549
26607
|
function mergeKey(fresh, before, after) {
|
|
26550
26608
|
if (before === after)
|
|
@@ -26651,6 +26709,7 @@ function mergeWorkspaceState(fresh, before, after, wantCorrected = true) {
|
|
|
26651
26709
|
files: mergedFiles,
|
|
26652
26710
|
unsynced: mergedList(fresh.unsynced, before.unsynced, after.unsynced),
|
|
26653
26711
|
rejected: mergedList(fresh.rejected, before.rejected, after.rejected),
|
|
26712
|
+
file_pull_holds: mergedList(fresh.file_pull_holds, before.file_pull_holds, after.file_pull_holds),
|
|
26654
26713
|
workspace_rules: rules.value,
|
|
26655
26714
|
workspace_memory: memory.value,
|
|
26656
26715
|
user_memory: userMemory.value,
|
|
@@ -26788,6 +26847,27 @@ function removeFileEntry(state, path) {
|
|
|
26788
26847
|
delete nextFiles[path];
|
|
26789
26848
|
return { state: { ...state, files: nextFiles }, changed: true };
|
|
26790
26849
|
}
|
|
26850
|
+
function listFilePullHolds(state) {
|
|
26851
|
+
return (state.file_pull_holds ?? []).filter((h) => h.target === "file");
|
|
26852
|
+
}
|
|
26853
|
+
function getFilePullHold(state, path) {
|
|
26854
|
+
return state.file_pull_holds?.find((h) => h.target === "file" && h.path === path);
|
|
26855
|
+
}
|
|
26856
|
+
function upsertFilePullHold(state, hold) {
|
|
26857
|
+
const list = state.file_pull_holds ?? [];
|
|
26858
|
+
const at = list.findIndex((h) => h.target === hold.target && h.path === hold.path);
|
|
26859
|
+
if (at !== -1 && stableHash(list[at]) === stableHash(hold))
|
|
26860
|
+
return state;
|
|
26861
|
+
const next = at === -1 ? [...list, hold] : list.map((h, i) => i === at ? hold : h);
|
|
26862
|
+
return { ...state, file_pull_holds: next };
|
|
26863
|
+
}
|
|
26864
|
+
function removeFilePullHold(state, path, kind) {
|
|
26865
|
+
const list = state.file_pull_holds;
|
|
26866
|
+
const hit = (h) => h.target === "file" && h.path === path && (kind === undefined || h.kind === kind);
|
|
26867
|
+
if (!list?.some(hit))
|
|
26868
|
+
return state;
|
|
26869
|
+
return { ...state, file_pull_holds: list.filter((h) => !hit(h)) };
|
|
26870
|
+
}
|
|
26791
26871
|
function fileEntryCount(state) {
|
|
26792
26872
|
return Object.keys(state.files ?? {}).length;
|
|
26793
26873
|
}
|
|
@@ -30588,6 +30668,9 @@ function entityPushHeldLine(f) {
|
|
|
30588
30668
|
function entityRestoreDeferredLine(f) {
|
|
30589
30669
|
return `[ENTITY_RESTORE_DEFERRED entityType=${f.entityType} slug=${f.slug} wait_s=${f.waitSeconds}]`;
|
|
30590
30670
|
}
|
|
30671
|
+
function entityPullDeferredLine(f) {
|
|
30672
|
+
return `[PULL_DEFERRED entityType=${f.entityType} slug=${f.slug} id=${f.id} reason=${f.reason}]`;
|
|
30673
|
+
}
|
|
30591
30674
|
function fileRestoreDeferredLine(f) {
|
|
30592
30675
|
return `[FILE_RESTORE_DEFERRED path=${f.path} wait_s=${f.waitSeconds}]`;
|
|
30593
30676
|
}
|
|
@@ -30606,6 +30689,16 @@ function fileStashFailedLine(f) {
|
|
|
30606
30689
|
const codePart = f.code ? ` code=${f.code}` : "";
|
|
30607
30690
|
return `[FILE_STASH_FAILED path=${f.path} reason=${f.reason}${codePart}]`;
|
|
30608
30691
|
}
|
|
30692
|
+
function fileRestoreSkippedLine(f) {
|
|
30693
|
+
return `[FILE_RESTORE_SKIPPED path=${f.path} row_id=${f.rowId} reason=${f.reason}]`;
|
|
30694
|
+
}
|
|
30695
|
+
function filePullSkippedLine(f) {
|
|
30696
|
+
return `[FILE_PULL_SKIPPED path=${f.path} row_id=${f.rowId} reason=${f.reason}]`;
|
|
30697
|
+
}
|
|
30698
|
+
function filePullHeldLine(f) {
|
|
30699
|
+
const retryPart = f.retryInSeconds !== undefined ? ` retry_s=${f.retryInSeconds}` : "";
|
|
30700
|
+
return `[FILE_PULL_HELD path=${f.path} kind=${f.kind}${retryPart}]`;
|
|
30701
|
+
}
|
|
30609
30702
|
function fileRejectionSyncFailedLine(f) {
|
|
30610
30703
|
const parts = [`[FILE_REJECTION_SYNC_FAILED verb=${f.verb}`];
|
|
30611
30704
|
if (f.path !== undefined)
|
|
@@ -34387,7 +34480,7 @@ var sessionCommand = defineCommand({
|
|
|
34387
34480
|
});
|
|
34388
34481
|
|
|
34389
34482
|
// src/commands/start.ts
|
|
34390
|
-
import { existsSync as existsSync10, readFileSync as
|
|
34483
|
+
import { existsSync as existsSync10, readFileSync as readFileSync33, statSync as statSync18 } from "node:fs";
|
|
34391
34484
|
|
|
34392
34485
|
// src/lib/daemon-log.ts
|
|
34393
34486
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9, renameSync as renameSync7, statSync as statSync4 } from "node:fs";
|
|
@@ -34460,7 +34553,20 @@ import { basename as basename4 } from "node:path";
|
|
|
34460
34553
|
import { readFileSync as readFileSync15 } from "node:fs";
|
|
34461
34554
|
|
|
34462
34555
|
// src/lib/conflicts.ts
|
|
34463
|
-
import {
|
|
34556
|
+
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
34557
|
+
import {
|
|
34558
|
+
closeSync as closeSync4,
|
|
34559
|
+
constants as fsConstants2,
|
|
34560
|
+
fstatSync as fstatSync2,
|
|
34561
|
+
mkdirSync as mkdirSync10,
|
|
34562
|
+
openSync as openSync4,
|
|
34563
|
+
readFileSync as readFileSync14,
|
|
34564
|
+
readdirSync as readdirSync6,
|
|
34565
|
+
renameSync as renameSync8,
|
|
34566
|
+
statSync as statSync5,
|
|
34567
|
+
unlinkSync as unlinkSync8,
|
|
34568
|
+
writeFileSync as writeFileSync7
|
|
34569
|
+
} from "node:fs";
|
|
34464
34570
|
import { dirname as dirname9, join as join12 } from "node:path";
|
|
34465
34571
|
function conflictsRoot(workspaceSlug) {
|
|
34466
34572
|
return join12(plinthHome(), "_conflicts", workspaceSlug);
|
|
@@ -34502,13 +34608,95 @@ function fileConflictPath(args) {
|
|
|
34502
34608
|
return join12(conflictsRoot(workspaceSlug), `${fileConflictStem(relPath)}.${iso}.${side}`);
|
|
34503
34609
|
}
|
|
34504
34610
|
function fileConflictStem(relPath) {
|
|
34505
|
-
|
|
34611
|
+
const flat = `files.${relPath.replace(/[/\\]/g, "__")}`;
|
|
34612
|
+
if (Buffer.byteLength(flat) + STAMP_AND_SIDE_BYTES <= NAME_MAX_BYTES && !LONG_STEM_RE.test(flat)) {
|
|
34613
|
+
return flat;
|
|
34614
|
+
}
|
|
34615
|
+
return `${truncateUtf8(flat, LONG_PREFIX_BYTES)}~${pathHash(relPath)}`;
|
|
34616
|
+
}
|
|
34617
|
+
var NAME_MAX_BYTES = 255;
|
|
34618
|
+
var STAMP_AND_SIDE_BYTES = 1 + 24 + 1 + 5;
|
|
34619
|
+
var PATH_HASH_HEX = 16;
|
|
34620
|
+
var LONG_PREFIX_BYTES = NAME_MAX_BYTES - STAMP_AND_SIDE_BYTES - 1 - PATH_HASH_HEX;
|
|
34621
|
+
var LONG_STEM_RE = /^(files\..*)~([0-9a-f]{16})$/;
|
|
34622
|
+
function pathHash(relPath) {
|
|
34623
|
+
return createHash3("sha256").update(relPath, "utf8").digest("hex").slice(0, PATH_HASH_HEX);
|
|
34624
|
+
}
|
|
34625
|
+
function truncateUtf8(s, maxBytes) {
|
|
34626
|
+
let bytes = 0;
|
|
34627
|
+
let out = "";
|
|
34628
|
+
for (const ch of s) {
|
|
34629
|
+
const n = Buffer.byteLength(ch);
|
|
34630
|
+
if (bytes + n > maxBytes)
|
|
34631
|
+
break;
|
|
34632
|
+
bytes += n;
|
|
34633
|
+
out += ch;
|
|
34634
|
+
}
|
|
34635
|
+
return out;
|
|
34636
|
+
}
|
|
34637
|
+
function pathRecordFile(workspaceSlug, hash2) {
|
|
34638
|
+
return join12(conflictsRoot(workspaceSlug), `.${hash2}.path`);
|
|
34639
|
+
}
|
|
34640
|
+
function ensurePathRecord(workspaceSlug, relPath) {
|
|
34641
|
+
const target = pathRecordFile(workspaceSlug, pathHash(relPath));
|
|
34642
|
+
if (readPathRecord(target) === relPath)
|
|
34643
|
+
return;
|
|
34644
|
+
const tmp = `${target}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
34645
|
+
writeFileSync7(tmp, relPath, { mode: 384 });
|
|
34646
|
+
try {
|
|
34647
|
+
renameSync8(tmp, target);
|
|
34648
|
+
} catch (err) {
|
|
34649
|
+
try {
|
|
34650
|
+
unlinkSync8(tmp);
|
|
34651
|
+
} catch {}
|
|
34652
|
+
throw err;
|
|
34653
|
+
}
|
|
34654
|
+
}
|
|
34655
|
+
function readStashFolderFile(target) {
|
|
34656
|
+
let fd;
|
|
34657
|
+
try {
|
|
34658
|
+
fd = openSync4(target, fsConstants2.O_RDONLY | fsConstants2.O_NONBLOCK | fsConstants2.O_NOFOLLOW);
|
|
34659
|
+
} catch {
|
|
34660
|
+
return null;
|
|
34661
|
+
}
|
|
34662
|
+
try {
|
|
34663
|
+
return fstatSync2(fd).isFile() ? readFileSync14(fd) : null;
|
|
34664
|
+
} catch {
|
|
34665
|
+
return null;
|
|
34666
|
+
} finally {
|
|
34667
|
+
closeSync4(fd);
|
|
34668
|
+
}
|
|
34669
|
+
}
|
|
34670
|
+
function readPathRecord(target) {
|
|
34671
|
+
return readStashFolderFile(target)?.toString("utf8") ?? null;
|
|
34672
|
+
}
|
|
34673
|
+
function prepareFileStash(workspaceSlug, relPath) {
|
|
34674
|
+
mkdirSync10(conflictsRoot(workspaceSlug), { recursive: true, mode: 448 });
|
|
34675
|
+
if (LONG_STEM_RE.test(fileConflictStem(relPath)))
|
|
34676
|
+
ensurePathRecord(workspaceSlug, relPath);
|
|
34677
|
+
}
|
|
34678
|
+
function resolveLongStem(workspaceSlug, head) {
|
|
34679
|
+
const m = LONG_STEM_RE.exec(head);
|
|
34680
|
+
if (!m)
|
|
34681
|
+
return null;
|
|
34682
|
+
const recorded = readPathRecord(pathRecordFile(workspaceSlug, m[2]));
|
|
34683
|
+
return recorded !== null && fileConflictStem(recorded) === head ? recorded : null;
|
|
34506
34684
|
}
|
|
34507
34685
|
function stashFileConflict(args) {
|
|
34508
|
-
|
|
34509
|
-
|
|
34510
|
-
|
|
34511
|
-
|
|
34686
|
+
prepareFileStash(args.workspaceSlug, args.relPath);
|
|
34687
|
+
let stamp = args.timestamp;
|
|
34688
|
+
for (let attempt = 0;attempt < 1000; attempt++) {
|
|
34689
|
+
const target = fileConflictPath({ ...args, timestamp: stamp });
|
|
34690
|
+
try {
|
|
34691
|
+
writeFileSync7(target, args.bytes, { mode: 384, flag: "wx" });
|
|
34692
|
+
return target;
|
|
34693
|
+
} catch (err) {
|
|
34694
|
+
if (err?.code !== "EEXIST")
|
|
34695
|
+
throw err;
|
|
34696
|
+
stamp = new Date(stamp.getTime() + 1);
|
|
34697
|
+
}
|
|
34698
|
+
}
|
|
34699
|
+
throw new Error(`no free recovery-copy name for files/${args.relPath} near ${args.timestamp.toISOString()}`);
|
|
34512
34700
|
}
|
|
34513
34701
|
function stashFileConflictOnce(args) {
|
|
34514
34702
|
const dir = conflictsRoot(args.workspaceSlug);
|
|
@@ -34523,10 +34711,13 @@ function stashFileConflictOnce(args) {
|
|
|
34523
34711
|
const newest = names.filter((n) => n.startsWith(head) && tail.test(n.slice(head.length))).sort().pop();
|
|
34524
34712
|
if (newest !== undefined) {
|
|
34525
34713
|
const path5 = join12(dir, newest);
|
|
34526
|
-
|
|
34527
|
-
|
|
34528
|
-
|
|
34529
|
-
|
|
34714
|
+
const existing = readStashFolderFile(path5);
|
|
34715
|
+
if (existing !== null && Buffer.compare(args.bytes, existing) === 0) {
|
|
34716
|
+
try {
|
|
34717
|
+
prepareFileStash(args.workspaceSlug, args.relPath);
|
|
34718
|
+
} catch {}
|
|
34719
|
+
return { path: path5, reused: true };
|
|
34720
|
+
}
|
|
34530
34721
|
}
|
|
34531
34722
|
return { path: stashFileConflict(args), reused: false };
|
|
34532
34723
|
}
|
|
@@ -34536,18 +34727,28 @@ function parseStamp(stamp) {
|
|
|
34536
34727
|
const d = new Date(iso);
|
|
34537
34728
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
34538
34729
|
}
|
|
34539
|
-
function labelFor(file2) {
|
|
34730
|
+
function labelFor(file2, workspaceSlug) {
|
|
34540
34731
|
const m = STAMP_RE2.exec(file2);
|
|
34541
34732
|
if (!m)
|
|
34542
|
-
return file2;
|
|
34733
|
+
return { label: file2 };
|
|
34543
34734
|
const head = file2.slice(0, m.index);
|
|
34735
|
+
const exact = LONG_STEM_RE.test(head) ? resolveLongStem(workspaceSlug, head) : null;
|
|
34736
|
+
if (exact !== null)
|
|
34737
|
+
return { label: `files/${exact}`, pathRecorded: true };
|
|
34738
|
+
if (LONG_STEM_RE.test(head)) {
|
|
34739
|
+
const cut = LONG_STEM_RE.exec(head)[1];
|
|
34740
|
+
return {
|
|
34741
|
+
label: `files/${cut.slice("files.".length).replace(/__/g, "/")}…`,
|
|
34742
|
+
pathUnrecorded: true
|
|
34743
|
+
};
|
|
34744
|
+
}
|
|
34544
34745
|
if (head.startsWith("files.")) {
|
|
34545
|
-
return `files/${head.slice("files.".length).replace(/__/g, "/")}
|
|
34746
|
+
return { label: `files/${head.slice("files.".length).replace(/__/g, "/")}` };
|
|
34546
34747
|
}
|
|
34547
34748
|
const dot = head.indexOf(".");
|
|
34548
34749
|
if (dot === -1)
|
|
34549
|
-
return head;
|
|
34550
|
-
return `${head.slice(0, dot)}/${head.slice(dot + 1)}
|
|
34750
|
+
return { label: head };
|
|
34751
|
+
return { label: `${head.slice(0, dot)}/${head.slice(dot + 1)}` };
|
|
34551
34752
|
}
|
|
34552
34753
|
function listConflictStashes(workspaceSlug) {
|
|
34553
34754
|
const dir = conflictsRoot(workspaceSlug);
|
|
@@ -34572,10 +34773,13 @@ function listConflictStashes(workspaceSlug) {
|
|
|
34572
34773
|
continue;
|
|
34573
34774
|
const m = STAMP_RE2.exec(file2);
|
|
34574
34775
|
const side = m?.[2];
|
|
34776
|
+
const labelled = labelFor(file2, workspaceSlug);
|
|
34575
34777
|
out.push({
|
|
34576
34778
|
file: file2,
|
|
34577
34779
|
absPath,
|
|
34578
|
-
label:
|
|
34780
|
+
label: labelled.label,
|
|
34781
|
+
...labelled.pathUnrecorded ? { pathUnrecorded: true } : {},
|
|
34782
|
+
...labelled.pathRecorded ? { pathRecorded: true } : {},
|
|
34579
34783
|
stashedAt: m ? parseStamp(m[1]) : null,
|
|
34580
34784
|
side: side === "local" || side === "cloud" ? side : null,
|
|
34581
34785
|
mtimeMs: st.mtimeMs
|
|
@@ -34944,26 +35148,26 @@ class DestructiveGate {
|
|
|
34944
35148
|
}
|
|
34945
35149
|
|
|
34946
35150
|
// src/lib/resolve-pending.ts
|
|
34947
|
-
import { readFileSync as
|
|
35151
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
34948
35152
|
|
|
34949
35153
|
// src/lib/files/push.ts
|
|
34950
|
-
import { readFileSync as
|
|
35154
|
+
import { readFileSync as readFileSync19, statSync as statSync7 } from "node:fs";
|
|
34951
35155
|
|
|
34952
35156
|
// src/lib/permanent-deletions.ts
|
|
34953
|
-
import { createHash as
|
|
35157
|
+
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
34954
35158
|
import {
|
|
34955
35159
|
chmodSync,
|
|
34956
|
-
closeSync as
|
|
35160
|
+
closeSync as closeSync5,
|
|
34957
35161
|
constants as constants2,
|
|
34958
35162
|
existsSync as existsSync4,
|
|
34959
35163
|
fsyncSync as fsyncSync2,
|
|
34960
|
-
fstatSync as
|
|
35164
|
+
fstatSync as fstatSync3,
|
|
34961
35165
|
lstatSync as lstatSync4,
|
|
34962
35166
|
mkdirSync as mkdirSync11,
|
|
34963
|
-
openSync as
|
|
35167
|
+
openSync as openSync5,
|
|
34964
35168
|
readFileSync as readFileSync17,
|
|
34965
|
-
renameSync as
|
|
34966
|
-
unlinkSync as
|
|
35169
|
+
renameSync as renameSync9,
|
|
35170
|
+
unlinkSync as unlinkSync9,
|
|
34967
35171
|
writeSync as writeSync2
|
|
34968
35172
|
} from "node:fs";
|
|
34969
35173
|
import { dirname as dirname10, join as join13 } from "node:path";
|
|
@@ -35061,13 +35265,13 @@ function canonical(value) {
|
|
|
35061
35265
|
return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${canonical(obj[key])}`).join(",")}}`;
|
|
35062
35266
|
}
|
|
35063
35267
|
function canonicalPayloadDigest(value) {
|
|
35064
|
-
return
|
|
35268
|
+
return createHash4("sha256").update(canonical(value), "utf8").digest("hex");
|
|
35065
35269
|
}
|
|
35066
35270
|
function fileCreatePayloadDigest(contentHash, mimeType) {
|
|
35067
35271
|
return canonicalPayloadDigest({ content_hash: contentHash, mime_type: mimeType });
|
|
35068
35272
|
}
|
|
35069
35273
|
function checksum(body) {
|
|
35070
|
-
return
|
|
35274
|
+
return createHash4("sha256").update(canonical(body), "utf8").digest("hex");
|
|
35071
35275
|
}
|
|
35072
35276
|
function emptyJournal(workspaceSlug) {
|
|
35073
35277
|
return { version: 1, revision: 0, workspace_slug: workspaceSlug, records: [], refresh: [] };
|
|
@@ -35139,11 +35343,11 @@ function parseJournal(workspaceSlug, raw) {
|
|
|
35139
35343
|
return body;
|
|
35140
35344
|
}
|
|
35141
35345
|
function fsyncDirectory(path5) {
|
|
35142
|
-
const fd =
|
|
35346
|
+
const fd = openSync5(path5, constants2.O_RDONLY);
|
|
35143
35347
|
try {
|
|
35144
35348
|
fsyncSync2(fd);
|
|
35145
35349
|
} finally {
|
|
35146
|
-
|
|
35350
|
+
closeSync5(fd);
|
|
35147
35351
|
}
|
|
35148
35352
|
}
|
|
35149
35353
|
function loadPermanentDeletionJournal(workspaceSlug) {
|
|
@@ -35187,8 +35391,8 @@ function writePermanentDeletionJournal(prior, next) {
|
|
|
35187
35391
|
`;
|
|
35188
35392
|
let fd;
|
|
35189
35393
|
try {
|
|
35190
|
-
fd =
|
|
35191
|
-
const stat =
|
|
35394
|
+
fd = openSync5(tmp, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0), 384);
|
|
35395
|
+
const stat = fstatSync3(fd);
|
|
35192
35396
|
if (!stat.isFile() || (stat.mode & 511) !== 384) {
|
|
35193
35397
|
throw new PermanentDeletionJournalError("journal temporary file is not owner-only", "prepare");
|
|
35194
35398
|
}
|
|
@@ -35197,16 +35401,16 @@ function writePermanentDeletionJournal(prior, next) {
|
|
|
35197
35401
|
while (offset < bytes.length)
|
|
35198
35402
|
offset += writeSync2(fd, bytes, offset, bytes.length - offset);
|
|
35199
35403
|
fsyncSync2(fd);
|
|
35200
|
-
|
|
35404
|
+
closeSync5(fd);
|
|
35201
35405
|
fd = undefined;
|
|
35202
35406
|
} catch (error52) {
|
|
35203
35407
|
if (fd !== undefined) {
|
|
35204
35408
|
try {
|
|
35205
|
-
|
|
35409
|
+
closeSync5(fd);
|
|
35206
35410
|
} catch {}
|
|
35207
35411
|
}
|
|
35208
35412
|
try {
|
|
35209
|
-
|
|
35413
|
+
unlinkSync9(tmp);
|
|
35210
35414
|
} catch {}
|
|
35211
35415
|
if (error52 instanceof PermanentDeletionJournalError)
|
|
35212
35416
|
throw error52;
|
|
@@ -35214,7 +35418,7 @@ function writePermanentDeletionJournal(prior, next) {
|
|
|
35214
35418
|
throw new PermanentDeletionJournalError(`could not prepare journal write: ${detail}`, "write");
|
|
35215
35419
|
}
|
|
35216
35420
|
try {
|
|
35217
|
-
|
|
35421
|
+
renameSync9(tmp, target);
|
|
35218
35422
|
} catch (error52) {
|
|
35219
35423
|
let installed2 = false;
|
|
35220
35424
|
try {
|
|
@@ -35225,7 +35429,7 @@ function writePermanentDeletionJournal(prior, next) {
|
|
|
35225
35429
|
installed2 = false;
|
|
35226
35430
|
}
|
|
35227
35431
|
try {
|
|
35228
|
-
|
|
35432
|
+
unlinkSync9(tmp);
|
|
35229
35433
|
} catch {}
|
|
35230
35434
|
if (!installed2) {
|
|
35231
35435
|
const detail = error52 instanceof Error ? error52.message : String(error52);
|
|
@@ -35584,9 +35788,109 @@ async function prepareOrResolveGenerationCreateLocked(args) {
|
|
|
35584
35788
|
}
|
|
35585
35789
|
|
|
35586
35790
|
// src/lib/files/content-hash.ts
|
|
35587
|
-
import { createHash as
|
|
35791
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
35588
35792
|
function sha256Bytes(bytes) {
|
|
35589
|
-
return
|
|
35793
|
+
return createHash5("sha256").update(bytes).digest("hex");
|
|
35794
|
+
}
|
|
35795
|
+
|
|
35796
|
+
// src/lib/files/keep-local.ts
|
|
35797
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
35798
|
+
import { lstatSync as lstatSync5, readFileSync as readFileSync18, unlinkSync as unlinkSync10, writeFileSync as writeFileSync8 } from "node:fs";
|
|
35799
|
+
import { dirname as dirname11, join as join14 } from "node:path";
|
|
35800
|
+
function keepUnsentLocalCopy(args) {
|
|
35801
|
+
const read = readKeepable(args.workspaceSlug, args.abs);
|
|
35802
|
+
if (read.kind === "failed")
|
|
35803
|
+
return read.failure;
|
|
35804
|
+
if (read.kind === "absent")
|
|
35805
|
+
return null;
|
|
35806
|
+
const localHash = sha256Bytes(read.bytes);
|
|
35807
|
+
if (args.safeHashes.includes(localHash))
|
|
35808
|
+
return null;
|
|
35809
|
+
let stashed;
|
|
35810
|
+
try {
|
|
35811
|
+
stashed = stashFileConflictOnce({
|
|
35812
|
+
workspaceSlug: args.workspaceSlug,
|
|
35813
|
+
relPath: args.relPath,
|
|
35814
|
+
timestamp: args.now(),
|
|
35815
|
+
bytes: read.bytes,
|
|
35816
|
+
side: "local"
|
|
35817
|
+
});
|
|
35818
|
+
} catch (err) {
|
|
35819
|
+
return { reason: "stash_io", code: errCode(err) };
|
|
35820
|
+
}
|
|
35821
|
+
const key = `reused:${stashed.path}:${args.versionKey}`;
|
|
35822
|
+
if (stashed.reused && args.loggedStashNotices?.has(key))
|
|
35823
|
+
return null;
|
|
35824
|
+
if (stashed.reused)
|
|
35825
|
+
args.loggedStashNotices?.add(key);
|
|
35826
|
+
args.output?.log(fileConflictLine({ path: args.relPath, stash: stashed.path, via: args.via, reused: stashed.reused }));
|
|
35827
|
+
args.output?.warn(stashed.reused ? `Conflict on files/${args.relPath} → your local edit is already kept at ${stashed.path}.` : `Conflict on files/${args.relPath} → stashed local edit to ${stashed.path} (cloud-wins${args.onBoot ? " on boot" : ""}).`);
|
|
35828
|
+
return null;
|
|
35829
|
+
}
|
|
35830
|
+
function probeKeep(args) {
|
|
35831
|
+
const read = readKeepable(args.workspaceSlug, args.abs);
|
|
35832
|
+
if (read.kind === "failed")
|
|
35833
|
+
return read.failure;
|
|
35834
|
+
if (read.kind === "absent")
|
|
35835
|
+
return null;
|
|
35836
|
+
if (args.safeHashes.includes(sha256Bytes(read.bytes)))
|
|
35837
|
+
return null;
|
|
35838
|
+
const dir = conflictsRoot(args.workspaceSlug);
|
|
35839
|
+
const probe2 = join14(dir, `.keep-probe-${process.pid}-${randomBytes2(6).toString("hex")}`);
|
|
35840
|
+
try {
|
|
35841
|
+
prepareFileStash(args.workspaceSlug, args.relPath);
|
|
35842
|
+
writeFileSync8(probe2, read.bytes, { mode: 384 });
|
|
35843
|
+
} catch (err) {
|
|
35844
|
+
return { reason: "stash_io", code: errCode(err) };
|
|
35845
|
+
} finally {
|
|
35846
|
+
try {
|
|
35847
|
+
unlinkSync10(probe2);
|
|
35848
|
+
} catch {}
|
|
35849
|
+
}
|
|
35850
|
+
return null;
|
|
35851
|
+
}
|
|
35852
|
+
function unsentBytesAt(args) {
|
|
35853
|
+
const read = readKeepable(args.workspaceSlug, args.abs);
|
|
35854
|
+
if (read.kind === "failed")
|
|
35855
|
+
return read;
|
|
35856
|
+
if (read.kind === "absent")
|
|
35857
|
+
return { kind: "none" };
|
|
35858
|
+
return args.safeHashes.includes(sha256Bytes(read.bytes)) ? { kind: "none" } : { kind: "unsent" };
|
|
35859
|
+
}
|
|
35860
|
+
function readLocalForKeep(workspaceSlug, abs) {
|
|
35861
|
+
return readKeepable(workspaceSlug, abs);
|
|
35862
|
+
}
|
|
35863
|
+
function localFingerprint(abs) {
|
|
35864
|
+
try {
|
|
35865
|
+
const st = lstatSync5(abs);
|
|
35866
|
+
return { size: st.size, mtime_ms: st.mtimeMs, ino: st.ino };
|
|
35867
|
+
} catch (err) {
|
|
35868
|
+
return err?.code === "ENOENT" ? null : undefined;
|
|
35869
|
+
}
|
|
35870
|
+
}
|
|
35871
|
+
function readKeepable(workspaceSlug, abs) {
|
|
35872
|
+
if (hasSymlinkedComponent(workspaceSlug, dirname11(abs))) {
|
|
35873
|
+
return { kind: "failed", failure: { reason: "symlink" } };
|
|
35874
|
+
}
|
|
35875
|
+
let st;
|
|
35876
|
+
try {
|
|
35877
|
+
st = lstatSync5(abs);
|
|
35878
|
+
} catch (err) {
|
|
35879
|
+
if (err?.code === "ENOENT")
|
|
35880
|
+
return { kind: "absent" };
|
|
35881
|
+
return { kind: "failed", failure: { reason: "unreadable", code: errCode(err) } };
|
|
35882
|
+
}
|
|
35883
|
+
if (!st.isFile())
|
|
35884
|
+
return { kind: "failed", failure: { reason: "not_a_file" } };
|
|
35885
|
+
try {
|
|
35886
|
+
return { kind: "read", bytes: readFileSync18(abs) };
|
|
35887
|
+
} catch (err) {
|
|
35888
|
+
return { kind: "failed", failure: { reason: "unreadable", code: errCode(err) } };
|
|
35889
|
+
}
|
|
35890
|
+
}
|
|
35891
|
+
function errCode(err) {
|
|
35892
|
+
const code = err?.code;
|
|
35893
|
+
return typeof code === "string" ? code : undefined;
|
|
35590
35894
|
}
|
|
35591
35895
|
|
|
35592
35896
|
// src/lib/files/mime.ts
|
|
@@ -35658,23 +35962,63 @@ async function restoreFileFromCloud(args) {
|
|
|
35658
35962
|
const { workspaceSlug, baseUrl, pat, id, relPath, state, fetchImpl, suppressor, output } = args;
|
|
35659
35963
|
const got = args.prefetched ?? await getFile({ baseUrl, pat, id, fetchImpl });
|
|
35660
35964
|
const abs = filesSubtreePath(workspaceSlug, relPath);
|
|
35965
|
+
const ack = getFileEntry(state, relPath);
|
|
35966
|
+
const keptHash = args.kept ? sha256Bytes(args.kept) : undefined;
|
|
35967
|
+
const blocked = (cloud) => {
|
|
35968
|
+
const read = readCommittedFileAck(workspaceSlug, relPath);
|
|
35969
|
+
if (read.kind === "unknown")
|
|
35970
|
+
return null;
|
|
35971
|
+
if (read.kind === "absent")
|
|
35972
|
+
return ack ? "removed" : null;
|
|
35973
|
+
const committed = read.entry;
|
|
35974
|
+
if (committed.id !== id)
|
|
35975
|
+
return "replaced";
|
|
35976
|
+
const against = cloud ?? ack;
|
|
35977
|
+
return against && isLaterVersion(committed, against) ? "newer_ack" : null;
|
|
35978
|
+
};
|
|
35979
|
+
const unsent = (cloudHash) => {
|
|
35980
|
+
const committed = readCommittedFileEntry(workspaceSlug, relPath);
|
|
35981
|
+
const disk = unsentBytesAt({
|
|
35982
|
+
workspaceSlug,
|
|
35983
|
+
abs,
|
|
35984
|
+
safeHashes: [
|
|
35985
|
+
cloudHash,
|
|
35986
|
+
ack?.content_hash,
|
|
35987
|
+
keptHash,
|
|
35988
|
+
committed?.id === id ? committed.content_hash : undefined
|
|
35989
|
+
]
|
|
35990
|
+
});
|
|
35991
|
+
if (disk.kind === "failed")
|
|
35992
|
+
return disk.failure.reason;
|
|
35993
|
+
return disk.kind === "unsent" ? "newer_save" : null;
|
|
35994
|
+
};
|
|
35995
|
+
const skipped = (reason2) => {
|
|
35996
|
+
output?.log(fileRestoreSkippedLine({ path: relPath, rowId: id, reason: reason2 }));
|
|
35997
|
+
return { state, outcome: "skipped", reason: reason2 };
|
|
35998
|
+
};
|
|
35661
35999
|
if (!got.ok) {
|
|
36000
|
+
const reason2 = blocked(null) ?? unsent(undefined);
|
|
36001
|
+
if (reason2)
|
|
36002
|
+
return skipped(reason2);
|
|
35662
36003
|
suppressor?.registerDelete(abs);
|
|
35663
36004
|
deleteFilesFile(workspaceSlug, relPath);
|
|
35664
|
-
return dropFiles(state, relPath);
|
|
36005
|
+
return { state: dropFiles(state, relPath), outcome: "removed" };
|
|
35665
36006
|
}
|
|
35666
36007
|
if (got.row.download_url === null) {
|
|
35667
36008
|
output?.warn(`files/${relPath}: Plinth holds no bytes for this file, so there is nothing to restore from cloud — the local copy and its state were left exactly as they are.`);
|
|
35668
|
-
return state;
|
|
36009
|
+
return { state, outcome: "no_bytes" };
|
|
35669
36010
|
}
|
|
35670
36011
|
const bytes = await downloadBytes(got.row.download_url, fetchImpl);
|
|
36012
|
+
const reason = blocked(got.row) ?? unsent(got.row.content_hash);
|
|
36013
|
+
if (reason)
|
|
36014
|
+
return skipped(reason);
|
|
35671
36015
|
suppressor?.registerWrite(abs, {
|
|
35672
36016
|
bodyHash: got.row.content_hash,
|
|
35673
36017
|
frontmatterHash: ""
|
|
35674
36018
|
});
|
|
35675
36019
|
writeFilesBytes(workspaceSlug, relPath, bytes);
|
|
35676
36020
|
deleteExtractedText(workspaceSlug, relPath);
|
|
35677
|
-
|
|
36021
|
+
const restored = persistFiles(state, {
|
|
35678
36022
|
id: got.row.id,
|
|
35679
36023
|
path: relPath,
|
|
35680
36024
|
content_hash: got.row.content_hash,
|
|
@@ -35683,6 +36027,7 @@ async function restoreFileFromCloud(args) {
|
|
|
35683
36027
|
updated_at: got.row.updated_at,
|
|
35684
36028
|
inode: safeInode(abs)
|
|
35685
36029
|
});
|
|
36030
|
+
return { state: restored, outcome: "restored" };
|
|
35686
36031
|
}
|
|
35687
36032
|
async function pushFilesUpsert(args) {
|
|
35688
36033
|
return withPermanentDeletionLock("workspace_file", args.relPath, () => pushFilesUpsertLocked(args));
|
|
@@ -35740,7 +36085,7 @@ async function pushFilesUpsertLocked(args) {
|
|
|
35740
36085
|
state: next
|
|
35741
36086
|
};
|
|
35742
36087
|
}
|
|
35743
|
-
bytes =
|
|
36088
|
+
bytes = readFileSync19(absolutePath);
|
|
35744
36089
|
hash2 = sha256Bytes(bytes);
|
|
35745
36090
|
inode = st.ino;
|
|
35746
36091
|
}
|
|
@@ -35947,8 +36292,9 @@ async function pushFilesUpsertLocked(args) {
|
|
|
35947
36292
|
side: "local"
|
|
35948
36293
|
});
|
|
35949
36294
|
output?.log(fileConflictLine({ path: relPath, stash: stashPath, reused }));
|
|
36295
|
+
let restore;
|
|
35950
36296
|
if (existing) {
|
|
35951
|
-
|
|
36297
|
+
const restored = await restoreFileFromCloud({
|
|
35952
36298
|
workspaceSlug,
|
|
35953
36299
|
baseUrl,
|
|
35954
36300
|
pat,
|
|
@@ -35958,10 +36304,16 @@ async function pushFilesUpsertLocked(args) {
|
|
|
35958
36304
|
fetchImpl,
|
|
35959
36305
|
suppressor,
|
|
35960
36306
|
output,
|
|
35961
|
-
prefetched: cloudRow
|
|
36307
|
+
prefetched: cloudRow,
|
|
36308
|
+
kept: bytes
|
|
35962
36309
|
});
|
|
36310
|
+
nextState = restored.state;
|
|
36311
|
+
restore = restored.outcome;
|
|
35963
36312
|
}
|
|
35964
|
-
return {
|
|
36313
|
+
return {
|
|
36314
|
+
outcome: { kind: "conflict", relPath, stashPath, ...restore ? { restore } : {} },
|
|
36315
|
+
state: nextState
|
|
36316
|
+
};
|
|
35965
36317
|
}
|
|
35966
36318
|
async function pushFilesDelete(args) {
|
|
35967
36319
|
const { workspaceSlug, baseUrl, pat, relPath, state, deps } = args;
|
|
@@ -35987,7 +36339,7 @@ async function pushFilesDelete(args) {
|
|
|
35987
36339
|
output?.log(fileDeleteLine({ path: relPath, id: existing.id, note: "already_absent" }));
|
|
35988
36340
|
return { outcome: { kind: "deleted", relPath, id: existing.id }, state: dropFiles(state, relPath) };
|
|
35989
36341
|
}
|
|
35990
|
-
const
|
|
36342
|
+
const restore = await restoreFileFromCloud({
|
|
35991
36343
|
workspaceSlug,
|
|
35992
36344
|
baseUrl,
|
|
35993
36345
|
pat,
|
|
@@ -35998,15 +36350,18 @@ async function pushFilesDelete(args) {
|
|
|
35998
36350
|
suppressor,
|
|
35999
36351
|
output
|
|
36000
36352
|
});
|
|
36001
|
-
return {
|
|
36353
|
+
return {
|
|
36354
|
+
outcome: { kind: "conflict", relPath, stashPath: "", restore: restore.outcome },
|
|
36355
|
+
state: restore.state
|
|
36356
|
+
};
|
|
36002
36357
|
}
|
|
36003
36358
|
|
|
36004
36359
|
// src/lib/push.ts
|
|
36005
|
-
import { existsSync as existsSync5, readFileSync as
|
|
36360
|
+
import { existsSync as existsSync5, readFileSync as readFileSync21, statSync as statSync8 } from "node:fs";
|
|
36006
36361
|
import { basename as basename5, relative as relative5 } from "node:path";
|
|
36007
36362
|
|
|
36008
36363
|
// src/lib/frontmatter-held.ts
|
|
36009
|
-
import { lstatSync as
|
|
36364
|
+
import { lstatSync as lstatSync6, readFileSync as readFileSync20 } from "node:fs";
|
|
36010
36365
|
import { resolve as resolve4, sep as sep5 } from "node:path";
|
|
36011
36366
|
function holdUnreadableFrontmatter(args) {
|
|
36012
36367
|
const next = recordEntityFrontmatterUnreadable(args);
|
|
@@ -36049,9 +36404,9 @@ function stillHeld(workspaceSlug, root, recordPath) {
|
|
|
36049
36404
|
return true;
|
|
36050
36405
|
let raw;
|
|
36051
36406
|
try {
|
|
36052
|
-
if (!
|
|
36407
|
+
if (!lstatSync6(absolutePath).isFile())
|
|
36053
36408
|
return true;
|
|
36054
|
-
raw =
|
|
36409
|
+
raw = readFileSync20(absolutePath, "utf8");
|
|
36055
36410
|
} catch (err) {
|
|
36056
36411
|
const code = err.code;
|
|
36057
36412
|
return code !== "ENOENT" && code !== "ENOTDIR";
|
|
@@ -36214,7 +36569,7 @@ function stampStateFromMirror(args) {
|
|
|
36214
36569
|
const { strategy, workspaceSlug, doc: doc2, state, priorMemoryHash } = args;
|
|
36215
36570
|
const targetSlug = args.pathSlug ?? doc2.slug;
|
|
36216
36571
|
const mirrorPath = entityFilePath(workspaceSlug, strategy.type, targetSlug);
|
|
36217
|
-
const mirrorRaw =
|
|
36572
|
+
const mirrorRaw = readFileSync21(mirrorPath, "utf8");
|
|
36218
36573
|
const mirrorParsed = matter(mirrorRaw);
|
|
36219
36574
|
const mirrorFm = mirrorParsed.data ?? {};
|
|
36220
36575
|
const mirrorLifted = splitUserFrontmatter(strategy, mirrorFm);
|
|
@@ -36367,7 +36722,7 @@ async function pushFile(args) {
|
|
|
36367
36722
|
}
|
|
36368
36723
|
let raw;
|
|
36369
36724
|
try {
|
|
36370
|
-
raw =
|
|
36725
|
+
raw = readFileSync21(absolutePath, "utf8");
|
|
36371
36726
|
} catch (err) {
|
|
36372
36727
|
if (err?.code === "ENOENT") {
|
|
36373
36728
|
return runPushDelete({
|
|
@@ -36726,7 +37081,7 @@ async function runPushUpsert(args) {
|
|
|
36726
37081
|
if (liveEntry !== snapshotEntry) {
|
|
36727
37082
|
let onDisk = null;
|
|
36728
37083
|
try {
|
|
36729
|
-
onDisk =
|
|
37084
|
+
onDisk = readFileSync21(args.absolutePath, "utf8");
|
|
36730
37085
|
} catch {
|
|
36731
37086
|
onDisk = null;
|
|
36732
37087
|
}
|
|
@@ -37188,7 +37543,7 @@ function writeCreatedIdBack(args) {
|
|
|
37188
37543
|
if (entityFilePath(args.workspaceSlug, args.strategy.type, args.slug) !== args.absolutePath) {
|
|
37189
37544
|
return;
|
|
37190
37545
|
}
|
|
37191
|
-
const readBack = args.readBack ?? ((p) =>
|
|
37546
|
+
const readBack = args.readBack ?? ((p) => readFileSync21(p, "utf8"));
|
|
37192
37547
|
if (readBack(args.absolutePath) !== args.raw)
|
|
37193
37548
|
return;
|
|
37194
37549
|
args.suppressor?.registerWrite(args.absolutePath, computeMirrorHashes(args.strategy, write.content));
|
|
@@ -37660,7 +38015,7 @@ async function pushRename(args) {
|
|
|
37660
38015
|
};
|
|
37661
38016
|
if (!updated.body_hash || !updated.frontmatter_hash) {
|
|
37662
38017
|
try {
|
|
37663
|
-
const mirrorRaw =
|
|
38018
|
+
const mirrorRaw = readFileSync21(toPath, "utf8");
|
|
37664
38019
|
const mirrorParsed = matter(mirrorRaw);
|
|
37665
38020
|
const renameFm = mirrorParsed.data ?? {};
|
|
37666
38021
|
const renameLifted = splitUserFrontmatter(strategy, renameFm);
|
|
@@ -37697,7 +38052,7 @@ async function pushRename(args) {
|
|
|
37697
38052
|
if (patchResult.reason === "slug_conflict") {
|
|
37698
38053
|
let contents2 = "";
|
|
37699
38054
|
try {
|
|
37700
|
-
contents2 =
|
|
38055
|
+
contents2 = readFileSync21(toPath, "utf8");
|
|
37701
38056
|
} catch {}
|
|
37702
38057
|
const stashPath2 = stashConflict({
|
|
37703
38058
|
workspaceSlug,
|
|
@@ -37745,7 +38100,7 @@ async function pushRename(args) {
|
|
|
37745
38100
|
}
|
|
37746
38101
|
let contents = "";
|
|
37747
38102
|
try {
|
|
37748
|
-
contents =
|
|
38103
|
+
contents = readFileSync21(toPath, "utf8");
|
|
37749
38104
|
} catch {}
|
|
37750
38105
|
const stashPath = stashConflict({
|
|
37751
38106
|
workspaceSlug,
|
|
@@ -37793,7 +38148,7 @@ async function drainResolvedBatches(deps) {
|
|
|
37793
38148
|
deps.output?.log(`[DESTRUCTIVE_APPLIED batch=${batch.batch_id} ops=${batch.ops.length}]`);
|
|
37794
38149
|
applied++;
|
|
37795
38150
|
} else {
|
|
37796
|
-
await discardBatchOps(batch, deps);
|
|
38151
|
+
await discardBatchOps(batch, deps, mutex2);
|
|
37797
38152
|
deps.output?.log(`[DESTRUCTIVE_DISCARDED batch=${batch.batch_id} ops=${batch.ops.length}]`);
|
|
37798
38153
|
discarded++;
|
|
37799
38154
|
}
|
|
@@ -37866,11 +38221,11 @@ async function applyFileOp(op, deps) {
|
|
|
37866
38221
|
});
|
|
37867
38222
|
deps.applyState(base2, result.state);
|
|
37868
38223
|
}
|
|
37869
|
-
async function discardBatchOps(batch, deps) {
|
|
38224
|
+
async function discardBatchOps(batch, deps, mutex2) {
|
|
37870
38225
|
for (const op of batch.ops) {
|
|
37871
38226
|
try {
|
|
37872
38227
|
if (op.target === "file") {
|
|
37873
|
-
await discardFileOp(op, deps);
|
|
38228
|
+
await discardFileOp(op, deps, mutex2);
|
|
37874
38229
|
continue;
|
|
37875
38230
|
}
|
|
37876
38231
|
const strategy = requireStrategy(op.entityType);
|
|
@@ -37878,7 +38233,7 @@ async function discardBatchOps(batch, deps) {
|
|
|
37878
38233
|
const path5 = opPath(deps.workspaceSlug, op);
|
|
37879
38234
|
let local = null;
|
|
37880
38235
|
try {
|
|
37881
|
-
local =
|
|
38236
|
+
local = readFileSync22(path5, "utf8");
|
|
37882
38237
|
} catch {
|
|
37883
38238
|
local = null;
|
|
37884
38239
|
}
|
|
@@ -37913,35 +38268,50 @@ async function discardBatchOps(batch, deps) {
|
|
|
37913
38268
|
}
|
|
37914
38269
|
}
|
|
37915
38270
|
}
|
|
37916
|
-
async function discardFileOp(op, deps) {
|
|
37917
|
-
|
|
37918
|
-
|
|
37919
|
-
|
|
37920
|
-
|
|
37921
|
-
|
|
37922
|
-
|
|
37923
|
-
|
|
37924
|
-
|
|
37925
|
-
|
|
38271
|
+
async function discardFileOp(op, deps, mutex2) {
|
|
38272
|
+
await mutex2.run(`files:${op.path}`, async () => {
|
|
38273
|
+
const abs = filesSubtreePath(deps.workspaceSlug, op.path);
|
|
38274
|
+
const skip = (reason, why) => {
|
|
38275
|
+
deps.output?.log(fileRestoreSkippedLine({ path: op.path, rowId: op.id, reason }));
|
|
38276
|
+
deps.output?.warn(`Discard of files/${op.path} left the file as it is: ${why}.`);
|
|
38277
|
+
};
|
|
38278
|
+
const committed = readCommittedFileEntry(deps.workspaceSlug, op.path);
|
|
38279
|
+
if (committed && committed.id !== op.id) {
|
|
38280
|
+
skip("replaced", "another file now has that path");
|
|
38281
|
+
return;
|
|
38282
|
+
}
|
|
38283
|
+
const read = readLocalForKeep(deps.workspaceSlug, abs);
|
|
38284
|
+
if (read.kind === "failed") {
|
|
38285
|
+
skip(read.failure.reason, "the file there could not be read safely");
|
|
38286
|
+
return;
|
|
38287
|
+
}
|
|
38288
|
+
const local = read.kind === "read" ? read.bytes : null;
|
|
38289
|
+
if (local !== null) {
|
|
38290
|
+
stashFileConflict({
|
|
38291
|
+
workspaceSlug: deps.workspaceSlug,
|
|
38292
|
+
relPath: op.path,
|
|
38293
|
+
timestamp: (deps.now ?? (() => new Date))(),
|
|
38294
|
+
bytes: local,
|
|
38295
|
+
side: "local"
|
|
38296
|
+
});
|
|
38297
|
+
}
|
|
38298
|
+
const base2 = deps.getState();
|
|
38299
|
+
const restore = await restoreFileFromCloud({
|
|
37926
38300
|
workspaceSlug: deps.workspaceSlug,
|
|
38301
|
+
baseUrl: deps.baseUrl,
|
|
38302
|
+
pat: deps.pat,
|
|
38303
|
+
id: op.id,
|
|
37927
38304
|
relPath: op.path,
|
|
37928
|
-
|
|
37929
|
-
|
|
37930
|
-
|
|
38305
|
+
state: base2,
|
|
38306
|
+
fetchImpl: deps.fetchImpl,
|
|
38307
|
+
output: deps.output,
|
|
38308
|
+
kept: local
|
|
37931
38309
|
});
|
|
37932
|
-
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
37936
|
-
baseUrl: deps.baseUrl,
|
|
37937
|
-
pat: deps.pat,
|
|
37938
|
-
id: op.id,
|
|
37939
|
-
relPath: op.path,
|
|
37940
|
-
state: base2,
|
|
37941
|
-
fetchImpl: deps.fetchImpl,
|
|
37942
|
-
output: deps.output
|
|
38310
|
+
if (restore.outcome === "skipped") {
|
|
38311
|
+
deps.output?.warn(`Discard of files/${op.path} left the file as it is (${restore.reason}); daemon.log has the line.`);
|
|
38312
|
+
}
|
|
38313
|
+
deps.applyState(base2, restore.state);
|
|
37943
38314
|
});
|
|
37944
|
-
deps.applyState(base2, next);
|
|
37945
38315
|
}
|
|
37946
38316
|
function opPath(workspaceSlug, op) {
|
|
37947
38317
|
try {
|
|
@@ -38889,10 +39259,10 @@ class FileDestructiveGate {
|
|
|
38889
39259
|
}
|
|
38890
39260
|
|
|
38891
39261
|
// src/lib/files/reconcile.ts
|
|
38892
|
-
import { readFileSync as
|
|
39262
|
+
import { readFileSync as readFileSync23, statSync as statSync10 } from "node:fs";
|
|
38893
39263
|
|
|
38894
39264
|
// src/lib/unsynced.ts
|
|
38895
|
-
import { join as
|
|
39265
|
+
import { join as join15, relative as relative8 } from "node:path";
|
|
38896
39266
|
var RECORDABLE_REASONS = new Set([
|
|
38897
39267
|
"rate_limit",
|
|
38898
39268
|
"server",
|
|
@@ -38954,7 +39324,7 @@ async function drainUnsyncedOps(args) {
|
|
|
38954
39324
|
attempted += 1;
|
|
38955
39325
|
try {
|
|
38956
39326
|
if (op.target === "entity") {
|
|
38957
|
-
const absolutePath =
|
|
39327
|
+
const absolutePath = join15(workspaceMirrorPath(workspaceSlug), op.path);
|
|
38958
39328
|
const classified = classifyMirrorPath({ workspaceSlug, absolutePath });
|
|
38959
39329
|
if (!classified) {
|
|
38960
39330
|
clear("entity", op.path);
|
|
@@ -39767,9 +40137,9 @@ class NodeFsHandler {
|
|
|
39767
40137
|
if (this.fsw.closed) {
|
|
39768
40138
|
return;
|
|
39769
40139
|
}
|
|
39770
|
-
const
|
|
40140
|
+
const dirname13 = sp.dirname(file2);
|
|
39771
40141
|
const basename7 = sp.basename(file2);
|
|
39772
|
-
const parent = this.fsw._getWatchedDir(
|
|
40142
|
+
const parent = this.fsw._getWatchedDir(dirname13);
|
|
39773
40143
|
let prevStats = stats;
|
|
39774
40144
|
if (parent.has(basename7))
|
|
39775
40145
|
return;
|
|
@@ -39796,7 +40166,7 @@ class NodeFsHandler {
|
|
|
39796
40166
|
prevStats = newStats2;
|
|
39797
40167
|
}
|
|
39798
40168
|
} catch (error52) {
|
|
39799
|
-
this.fsw._remove(
|
|
40169
|
+
this.fsw._remove(dirname13, basename7);
|
|
39800
40170
|
}
|
|
39801
40171
|
} else if (parent.has(basename7)) {
|
|
39802
40172
|
const at = newStats.atimeMs;
|
|
@@ -40834,6 +41204,12 @@ class DebounceMap {
|
|
|
40834
41204
|
this.pending.delete(oldestKey);
|
|
40835
41205
|
this.handler(entry2.event);
|
|
40836
41206
|
}
|
|
41207
|
+
nudge(absolutePath) {
|
|
41208
|
+
if (this.pending.has(absolutePath))
|
|
41209
|
+
return false;
|
|
41210
|
+
this.schedule({ kind: "upsert", absolutePath });
|
|
41211
|
+
return true;
|
|
41212
|
+
}
|
|
40837
41213
|
async drain() {
|
|
40838
41214
|
const entries = Array.from(this.pending.entries());
|
|
40839
41215
|
this.pending.clear();
|
|
@@ -40898,6 +41274,9 @@ function startWatcher(opts, handler) {
|
|
|
40898
41274
|
drainPending() {
|
|
40899
41275
|
return debounce.drain();
|
|
40900
41276
|
},
|
|
41277
|
+
nudge(absolutePath) {
|
|
41278
|
+
return debounce.nudge(absolutePath);
|
|
41279
|
+
},
|
|
40901
41280
|
pendingCount() {
|
|
40902
41281
|
return debounce.size();
|
|
40903
41282
|
}
|
|
@@ -40928,7 +41307,7 @@ function readBlobForPush(args) {
|
|
|
40928
41307
|
}
|
|
40929
41308
|
let bytes;
|
|
40930
41309
|
try {
|
|
40931
|
-
bytes =
|
|
41310
|
+
bytes = readFileSync23(absolutePath);
|
|
40932
41311
|
} catch (err) {
|
|
40933
41312
|
if (err?.code === "ENOENT")
|
|
40934
41313
|
return { kind: "gone" };
|
|
@@ -41989,10 +42368,10 @@ function anyUnknown(state) {
|
|
|
41989
42368
|
}
|
|
41990
42369
|
|
|
41991
42370
|
// src/lib/memory/write.ts
|
|
41992
|
-
import { readFileSync as
|
|
42371
|
+
import { readFileSync as readFileSync24, statSync as statSync11 } from "node:fs";
|
|
41993
42372
|
|
|
41994
42373
|
// src/lib/files/extracted-text.ts
|
|
41995
|
-
import { createHash as
|
|
42374
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
41996
42375
|
var DERIVED_TEXT_VERSION = 2;
|
|
41997
42376
|
function yamlString3(value) {
|
|
41998
42377
|
return JSON.stringify(value);
|
|
@@ -42066,7 +42445,7 @@ ${doc2.text}
|
|
|
42066
42445
|
`;
|
|
42067
42446
|
}
|
|
42068
42447
|
function extractedTextHash(rendered) {
|
|
42069
|
-
return
|
|
42448
|
+
return createHash6("sha256").update(rendered, "utf8").digest("hex");
|
|
42070
42449
|
}
|
|
42071
42450
|
|
|
42072
42451
|
// src/lib/memory/splice.ts
|
|
@@ -42183,7 +42562,7 @@ function writeDocumentAnnotation(args) {
|
|
|
42183
42562
|
}
|
|
42184
42563
|
let current;
|
|
42185
42564
|
try {
|
|
42186
|
-
current =
|
|
42565
|
+
current = readFileSync24(target, "utf8");
|
|
42187
42566
|
} catch {
|
|
42188
42567
|
return { kind: "absent" };
|
|
42189
42568
|
}
|
|
@@ -44142,7 +44521,7 @@ class PullSuppressor {
|
|
|
44142
44521
|
}
|
|
44143
44522
|
|
|
44144
44523
|
// src/lib/discover-entities.ts
|
|
44145
|
-
import { readFileSync as
|
|
44524
|
+
import { readFileSync as readFileSync25, statSync as statSync12 } from "node:fs";
|
|
44146
44525
|
var DISCOVERY_WRITE_CAP_DEFAULT = 10;
|
|
44147
44526
|
function discoveryWriteCap() {
|
|
44148
44527
|
const raw = process.env.PLINTH_DISCOVERY_WRITE_CAP;
|
|
@@ -44246,7 +44625,7 @@ function planDiscoveryWrites(args) {
|
|
|
44246
44625
|
counts.discovered++;
|
|
44247
44626
|
let raw;
|
|
44248
44627
|
try {
|
|
44249
|
-
raw =
|
|
44628
|
+
raw = readFileSync25(absolutePath, "utf8");
|
|
44250
44629
|
} catch {
|
|
44251
44630
|
counts.discovered--;
|
|
44252
44631
|
continue;
|
|
@@ -44494,7 +44873,7 @@ function recordDiscoveryFailure(args) {
|
|
|
44494
44873
|
}
|
|
44495
44874
|
|
|
44496
44875
|
// src/lib/frontmatter-id-backfill.ts
|
|
44497
|
-
import { readFileSync as
|
|
44876
|
+
import { readFileSync as readFileSync26, statSync as statSync13 } from "node:fs";
|
|
44498
44877
|
var FRONTMATTER_ID_BACKFILL_VERSION = 1;
|
|
44499
44878
|
function backfillWorthReporting(r) {
|
|
44500
44879
|
return r.written > 0 || r.failed > 0 || r.skipped.foreignId > 0 || r.skipped.unwritable > 0 || r.skipped.slugCollision > 0;
|
|
@@ -44537,7 +44916,7 @@ function safeInode3(path5) {
|
|
|
44537
44916
|
function backfillFrontmatterIds(args) {
|
|
44538
44917
|
const { workspaceSlug, state } = args;
|
|
44539
44918
|
const compose = args.compose ?? withFrontmatterId;
|
|
44540
|
-
const readRaw = args.readFile ?? ((p) =>
|
|
44919
|
+
const readRaw = args.readFile ?? ((p) => readFileSync26(p, "utf8"));
|
|
44541
44920
|
const result = emptyResult();
|
|
44542
44921
|
if (state.frontmatter_id_backfill_version === FRONTMATTER_ID_BACKFILL_VERSION) {
|
|
44543
44922
|
return result;
|
|
@@ -44665,7 +45044,7 @@ function backfillFrontmatterIds(args) {
|
|
|
44665
45044
|
}
|
|
44666
45045
|
|
|
44667
45046
|
// src/lib/reconcile.ts
|
|
44668
|
-
import { readFileSync as
|
|
45047
|
+
import { readFileSync as readFileSync27, statSync as statSync14 } from "node:fs";
|
|
44669
45048
|
async function reconcileDivergedRows(args) {
|
|
44670
45049
|
const { workspaceSlug, baseUrl, pat, fetchImpl, output, isFrozen } = args;
|
|
44671
45050
|
const now = args.now ?? (() => new Date);
|
|
@@ -44777,7 +45156,7 @@ async function reconcileDivergedRows(args) {
|
|
|
44777
45156
|
}
|
|
44778
45157
|
let content;
|
|
44779
45158
|
try {
|
|
44780
|
-
content =
|
|
45159
|
+
content = readFileSync27(absolutePath, "utf8");
|
|
44781
45160
|
} catch (err) {
|
|
44782
45161
|
if (err?.code === "ENOENT") {
|
|
44783
45162
|
state = clearEntityCloudRowDeleted(state, strategy.mirrorRelativePath(entry2.slug));
|
|
@@ -46142,17 +46521,17 @@ function createThrottledFetch(baseFetch, opts = {}) {
|
|
|
46142
46521
|
// src/commands/sync.ts
|
|
46143
46522
|
import {
|
|
46144
46523
|
existsSync as existsSync9,
|
|
46145
|
-
lstatSync as
|
|
46524
|
+
lstatSync as lstatSync8,
|
|
46146
46525
|
readdirSync as readdirSync7,
|
|
46147
|
-
readFileSync as
|
|
46526
|
+
readFileSync as readFileSync32,
|
|
46148
46527
|
statSync as statSync17,
|
|
46149
|
-
unlinkSync as
|
|
46528
|
+
unlinkSync as unlinkSync11
|
|
46150
46529
|
} from "node:fs";
|
|
46151
|
-
import { join as
|
|
46530
|
+
import { join as join19 } from "node:path";
|
|
46152
46531
|
|
|
46153
46532
|
// src/lib/conflicts-view.ts
|
|
46154
46533
|
import { existsSync as existsSync7 } from "node:fs";
|
|
46155
|
-
import { join as
|
|
46534
|
+
import { join as join18 } from "node:path";
|
|
46156
46535
|
var NO_GENERATED_MEMORY = {
|
|
46157
46536
|
artistMemoryGenerated: () => false,
|
|
46158
46537
|
workspaceMemoryGenerated: () => false,
|
|
@@ -46194,11 +46573,13 @@ function resolveConflictRows(workspaceSlug, stashes, generatedMemory = NO_GENERA
|
|
|
46194
46573
|
if (root === null || !stash.label.startsWith("files/")) {
|
|
46195
46574
|
return { kind: "entity", stash };
|
|
46196
46575
|
}
|
|
46576
|
+
if (stash.pathUnrecorded)
|
|
46577
|
+
return { kind: "unsafe-path", stash };
|
|
46197
46578
|
const rel = stash.label.slice("files/".length);
|
|
46198
46579
|
if (validateFilesRelPath(rel) === null) {
|
|
46199
46580
|
return { kind: "unsafe-path", stash };
|
|
46200
46581
|
}
|
|
46201
|
-
const abs =
|
|
46582
|
+
const abs = join18(root, "files", rel);
|
|
46202
46583
|
if (hasSymlinkedComponent(workspaceSlug, abs)) {
|
|
46203
46584
|
return { kind: "unsafe-path", stash };
|
|
46204
46585
|
}
|
|
@@ -46265,7 +46646,9 @@ function renderRow(row) {
|
|
|
46265
46646
|
`);
|
|
46266
46647
|
}
|
|
46267
46648
|
out.push("To put your version back, run this and then delete the stash:", "", "```sh", `cp ${shellQuote2(stash.absPath)} ${shellQuote2(row.mirrorPath)}`, "```");
|
|
46268
|
-
if (!row.mirrorPathExists) {
|
|
46649
|
+
if (!row.mirrorPathExists && stash.pathRecorded) {
|
|
46650
|
+
out.push("", "**Nothing is at that path right now**, so the cloud copy was probably", "deleted. Open the stash and check before running it.");
|
|
46651
|
+
} else if (!row.mirrorPathExists) {
|
|
46269
46652
|
out.push("", "**Nothing is at that path right now.** It is derived from the stash", "filename, which cannot represent a `/` and a `__` differently, so either", "the cloud copy was deleted, or the original path contained `__` and the", "line above names the wrong file. Open the stash and check before running it.");
|
|
46270
46653
|
}
|
|
46271
46654
|
return out.join(`
|
|
@@ -46328,17 +46711,16 @@ function refreshConflictsView(workspaceSlug) {
|
|
|
46328
46711
|
}
|
|
46329
46712
|
|
|
46330
46713
|
// src/lib/files/pull.ts
|
|
46331
|
-
import { existsSync as existsSync8,
|
|
46332
|
-
import { dirname as dirname13 } from "node:path";
|
|
46714
|
+
import { existsSync as existsSync8, readFileSync as readFileSync29, statSync as statSync15 } from "node:fs";
|
|
46333
46715
|
|
|
46334
46716
|
// src/lib/permanent-disposition.ts
|
|
46335
|
-
import { createHash as
|
|
46336
|
-
import { readFileSync as
|
|
46717
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
46718
|
+
import { readFileSync as readFileSync28 } from "node:fs";
|
|
46337
46719
|
function sha256(value) {
|
|
46338
|
-
return
|
|
46720
|
+
return createHash7("sha256").update(value).digest("hex");
|
|
46339
46721
|
}
|
|
46340
46722
|
function verifyStash(path5, expectedHash) {
|
|
46341
|
-
const actual = sha256(
|
|
46723
|
+
const actual = sha256(readFileSync28(path5));
|
|
46342
46724
|
if (actual !== expectedHash) {
|
|
46343
46725
|
throw new Error(`permanent deletion stash verification failed for ${path5}`);
|
|
46344
46726
|
}
|
|
@@ -46382,7 +46764,7 @@ function disposePermanentDocumentLocked(args) {
|
|
|
46382
46764
|
const target = entityFilePath(args.workspaceSlug, args.strategy.type, retiredEntry?.slug ?? t.logicalKey);
|
|
46383
46765
|
let raw;
|
|
46384
46766
|
try {
|
|
46385
|
-
raw =
|
|
46767
|
+
raw = readFileSync28(target, "utf8");
|
|
46386
46768
|
} catch (error52) {
|
|
46387
46769
|
if (error52?.code !== "ENOENT")
|
|
46388
46770
|
throw error52;
|
|
@@ -46480,7 +46862,7 @@ function disposePermanentFileLocked(args) {
|
|
|
46480
46862
|
const target = filesSubtreePath(args.workspaceSlug, t.logicalKey);
|
|
46481
46863
|
let bytes;
|
|
46482
46864
|
try {
|
|
46483
|
-
bytes =
|
|
46865
|
+
bytes = readFileSync28(target);
|
|
46484
46866
|
} catch (error52) {
|
|
46485
46867
|
if (error52?.code !== "ENOENT")
|
|
46486
46868
|
throw error52;
|
|
@@ -46527,6 +46909,34 @@ function disposePermanentFileLocked(args) {
|
|
|
46527
46909
|
return { state, deleted, textDeleted, ...stashPath ? { stashPath } : {} };
|
|
46528
46910
|
}
|
|
46529
46911
|
|
|
46912
|
+
// src/lib/files/pull-ack.ts
|
|
46913
|
+
function currentFileAck(args) {
|
|
46914
|
+
const read = readCommittedFileAck(args.workspaceSlug, args.relPath);
|
|
46915
|
+
if (read.kind === "unknown")
|
|
46916
|
+
return args.snapshot;
|
|
46917
|
+
if (read.kind === "absent") {
|
|
46918
|
+
const retired = args.committedBase !== undefined && sameFileVersion(args.snapshot, args.committedBase);
|
|
46919
|
+
return retired ? undefined : args.snapshot;
|
|
46920
|
+
}
|
|
46921
|
+
const committed = read.entry;
|
|
46922
|
+
const known = args.snapshot ?? args.committedBase;
|
|
46923
|
+
if (!known)
|
|
46924
|
+
return committed;
|
|
46925
|
+
if (committed.id !== known.id) {
|
|
46926
|
+
if (isLaterVersion(known, committed))
|
|
46927
|
+
return args.snapshot;
|
|
46928
|
+
if (isLaterVersion(committed, known))
|
|
46929
|
+
return committed;
|
|
46930
|
+
return args.snapshot !== undefined && !sameFileVersion(args.snapshot, args.committedBase) ? args.snapshot : committed;
|
|
46931
|
+
}
|
|
46932
|
+
return isLaterVersion(committed, known) ? committed : args.snapshot;
|
|
46933
|
+
}
|
|
46934
|
+
function sameFileVersion(a, b) {
|
|
46935
|
+
if (!a || !b)
|
|
46936
|
+
return a === b;
|
|
46937
|
+
return a.id === b.id && a.updated_at === b.updated_at && a.content_hash === b.content_hash;
|
|
46938
|
+
}
|
|
46939
|
+
|
|
46530
46940
|
// src/lib/files/pull.ts
|
|
46531
46941
|
function dueFileRefreshMarkers(workspaceSlug, now) {
|
|
46532
46942
|
try {
|
|
@@ -46653,7 +47063,9 @@ async function replayPermanentFiles(args, markers) {
|
|
|
46653
47063
|
loggedTombstoneSkips: args.loggedTombstoneSkips,
|
|
46654
47064
|
forceTextRewalk: args.forceTextRewalk,
|
|
46655
47065
|
loggedStashNotices: args.loggedStashNotices,
|
|
46656
|
-
recentUnlinks: args.recentUnlinks
|
|
47066
|
+
recentUnlinks: args.recentUnlinks,
|
|
47067
|
+
committedBase: lastCommittedState,
|
|
47068
|
+
commit: args.commit
|
|
46657
47069
|
});
|
|
46658
47070
|
state = outcome.state;
|
|
46659
47071
|
if (outcome.action === "written")
|
|
@@ -46726,6 +47138,7 @@ async function pullFiles(args) {
|
|
|
46726
47138
|
let finalHighWater = null;
|
|
46727
47139
|
let markRefused = false;
|
|
46728
47140
|
let cursorBlocked = permanentJournalBlocked;
|
|
47141
|
+
const deliveredPaths = new Set;
|
|
46729
47142
|
while (true) {
|
|
46730
47143
|
let page;
|
|
46731
47144
|
try {
|
|
@@ -46743,6 +47156,7 @@ async function pullFiles(args) {
|
|
|
46743
47156
|
let pageTextFailed = false;
|
|
46744
47157
|
for (const row of page.rows) {
|
|
46745
47158
|
fetched++;
|
|
47159
|
+
deliveredPaths.add(row.path);
|
|
46746
47160
|
if (row.deleted_at !== null && row.permanent_delete === true && row.purge_generation !== undefined) {
|
|
46747
47161
|
if (permanentJournalBlocked) {
|
|
46748
47162
|
pageTextFailed = true;
|
|
@@ -46773,7 +47187,9 @@ async function pullFiles(args) {
|
|
|
46773
47187
|
loggedTombstoneSkips: args.loggedTombstoneSkips,
|
|
46774
47188
|
forceTextRewalk: args.forceTextRewalk,
|
|
46775
47189
|
loggedStashNotices: args.loggedStashNotices,
|
|
46776
|
-
recentUnlinks: args.recentUnlinks
|
|
47190
|
+
recentUnlinks: args.recentUnlinks,
|
|
47191
|
+
committedBase: pageBase,
|
|
47192
|
+
commit: args.commit
|
|
46777
47193
|
});
|
|
46778
47194
|
state = outcome.state;
|
|
46779
47195
|
if (!outcome.symlinkRefused) {
|
|
@@ -46827,6 +47243,14 @@ async function pullFiles(args) {
|
|
|
46827
47243
|
return replayPermanentFiles({ ...args, state }, markers);
|
|
46828
47244
|
}
|
|
46829
47245
|
}
|
|
47246
|
+
const pruned = pruneFilePullHolds({
|
|
47247
|
+
state,
|
|
47248
|
+
workspaceSlug,
|
|
47249
|
+
deliveredPaths,
|
|
47250
|
+
cursorPass: markRefused || args.cursor === baseCursor
|
|
47251
|
+
});
|
|
47252
|
+
if (pruned !== state)
|
|
47253
|
+
state = args.commit ? args.commit(state, pruned) : pruned;
|
|
46830
47254
|
if (textUnknown > 0) {
|
|
46831
47255
|
args.output?.warn(`${textUnknown} file ${textUnknown === 1 ? "row" : "rows"} arrived without a readable \`has_extracted_text\` — this server predates the extracted-text contract, or the field was dropped from the payload. Derived text under views/extracted/ was left untouched rather than deleted, and is not being updated either.`);
|
|
46832
47256
|
}
|
|
@@ -46899,6 +47323,9 @@ async function materialiseExtractedText(args) {
|
|
|
46899
47323
|
});
|
|
46900
47324
|
} else {
|
|
46901
47325
|
const got = await getFileText({ baseUrl, pat, id: row.id, fetchImpl });
|
|
47326
|
+
if (args.stillCurrent && !args.stillCurrent()) {
|
|
47327
|
+
return { textHash: undefined, written: 0, deleted: 0, stale: true };
|
|
47328
|
+
}
|
|
46902
47329
|
if (!got.ok)
|
|
46903
47330
|
return { textHash: undefined, written: 0, deleted: 0 };
|
|
46904
47331
|
if (got.row.text === null) {
|
|
@@ -46960,7 +47387,10 @@ async function applyFileRow(args) {
|
|
|
46960
47387
|
symlinkRefused: true
|
|
46961
47388
|
};
|
|
46962
47389
|
}
|
|
46963
|
-
const
|
|
47390
|
+
const snapshotEntry = getFileEntry(state, relPath);
|
|
47391
|
+
const baseEntry = getFileEntry(args.committedBase, relPath);
|
|
47392
|
+
const ackNow = () => currentFileAck({ workspaceSlug, relPath, snapshot: snapshotEntry, committedBase: baseEntry });
|
|
47393
|
+
const existing = ackNow();
|
|
46964
47394
|
if (row.deleted_at !== null) {
|
|
46965
47395
|
if (existing && existing.id !== row.id) {
|
|
46966
47396
|
const key = `${row.id}:${existing.id}:${relPath}`;
|
|
@@ -46968,40 +47398,63 @@ async function applyFileRow(args) {
|
|
|
46968
47398
|
args.loggedTombstoneSkips?.add(key);
|
|
46969
47399
|
output?.log(`[FILE_TOMBSTONE_SKIP path=${relPath} row_id=${row.id} tracked_id=${existing.id} reason=id_mismatch]`);
|
|
46970
47400
|
}
|
|
46971
|
-
return { state, action: "skipped" };
|
|
47401
|
+
return { state: clearHoldsOfRow(state, relPath, row.id), action: "skipped" };
|
|
46972
47402
|
}
|
|
46973
47403
|
let localBytes;
|
|
46974
47404
|
try {
|
|
46975
|
-
localBytes =
|
|
47405
|
+
localBytes = readFileSync29(abs);
|
|
46976
47406
|
} catch {
|
|
46977
47407
|
localBytes = undefined;
|
|
46978
47408
|
}
|
|
46979
47409
|
const isOurs = localBytes === undefined || existing !== undefined && sha256Bytes(localBytes) === existing.content_hash;
|
|
46980
47410
|
if (!isOurs) {
|
|
46981
47411
|
output?.log(`[FILE_TOMBSTONE_KEEP path=${relPath} row_id=${row.id} reason=${existing === undefined ? "untracked" : "diverged"}]`);
|
|
46982
|
-
|
|
46983
|
-
|
|
47412
|
+
if (!existing)
|
|
47413
|
+
return { state: clearHoldsOfRow(state, relPath, row.id), action: "skipped" };
|
|
47414
|
+
const kept = upsertFilePullHold(clearRowHolds(dropAck(state, relPath, existing, snapshotEntry, args.commit), relPath), {
|
|
47415
|
+
target: "file",
|
|
47416
|
+
path: relPath,
|
|
47417
|
+
kind: "kept_after_delete",
|
|
47418
|
+
row_id: row.id,
|
|
47419
|
+
row_updated_at: row.deleted_at,
|
|
47420
|
+
row_hash: existing.content_hash,
|
|
47421
|
+
local: localFingerprint(abs) ?? null,
|
|
47422
|
+
attempts: 1
|
|
47423
|
+
});
|
|
47424
|
+
return { state: kept, action: "skipped" };
|
|
46984
47425
|
}
|
|
46985
47426
|
suppressor?.registerDelete(abs);
|
|
46986
47427
|
const didUnlink = deleteFilesFile(workspaceSlug, relPath);
|
|
46987
47428
|
const removedText = safeDeleteExtractedText(workspaceSlug, relPath, output);
|
|
46988
47429
|
const textDeleted = removedText.removed ? 1 : 0;
|
|
46989
47430
|
if (existing) {
|
|
46990
|
-
const { state: next2 } = removeFileEntry(state, relPath);
|
|
46991
47431
|
return {
|
|
46992
|
-
state:
|
|
47432
|
+
state: clearRowHolds(dropAck(state, relPath, existing, snapshotEntry, args.commit), relPath),
|
|
46993
47433
|
action: "deleted",
|
|
46994
47434
|
textDeleted,
|
|
46995
47435
|
textFailed: removedText.failed
|
|
46996
47436
|
};
|
|
46997
47437
|
}
|
|
46998
47438
|
return {
|
|
46999
|
-
state,
|
|
47439
|
+
state: clearRowHolds(state, relPath),
|
|
47000
47440
|
action: didUnlink ? "deleted" : "skipped",
|
|
47001
47441
|
textDeleted,
|
|
47002
47442
|
textFailed: removedText.failed
|
|
47003
47443
|
};
|
|
47004
47444
|
}
|
|
47445
|
+
if (existing && isLaterVersion(existing, row)) {
|
|
47446
|
+
const key = `skip:${relPath}:${row.id}:${row.updated_at}`;
|
|
47447
|
+
if (!args.loggedStashNotices?.has(key)) {
|
|
47448
|
+
args.loggedStashNotices?.add(key);
|
|
47449
|
+
output?.log(filePullSkippedLine({
|
|
47450
|
+
path: relPath,
|
|
47451
|
+
rowId: row.id,
|
|
47452
|
+
reason: existing.id === row.id ? "older" : "replaced"
|
|
47453
|
+
}));
|
|
47454
|
+
}
|
|
47455
|
+
return { state: clearRowHolds(state, relPath), action: "skipped" };
|
|
47456
|
+
}
|
|
47457
|
+
const stillAt = (decided) => () => sameFileVersion(ackNow(), decided);
|
|
47005
47458
|
const textSignal = extractedTextSignal(row);
|
|
47006
47459
|
const textUnknown = textSignal === "unknown";
|
|
47007
47460
|
if (storedBytesSignal(row) === "absent") {
|
|
@@ -47015,7 +47468,8 @@ async function applyFileRow(args) {
|
|
|
47015
47468
|
existing,
|
|
47016
47469
|
textSignal,
|
|
47017
47470
|
fetchImpl,
|
|
47018
|
-
output
|
|
47471
|
+
output,
|
|
47472
|
+
stillCurrent: stillAt(existing)
|
|
47019
47473
|
});
|
|
47020
47474
|
}
|
|
47021
47475
|
if (existing && existing.content_hash === row.content_hash && !existsSync8(abs)) {
|
|
@@ -47029,9 +47483,9 @@ async function applyFileRow(args) {
|
|
|
47029
47483
|
} else {
|
|
47030
47484
|
presentAgain(args.recentUnlinks, abs);
|
|
47031
47485
|
}
|
|
47032
|
-
if (existing && existing.content_hash === row.content_hash && existsSync8(abs)) {
|
|
47486
|
+
if (existing && existing.id === row.id && existing.content_hash === row.content_hash && existsSync8(abs)) {
|
|
47033
47487
|
if (!args.forceTextRewalk && textIsCurrent(existing, row, workspaceSlug, relPath)) {
|
|
47034
|
-
return { state, action: "noop", textUnknown };
|
|
47488
|
+
return { state: clearRowHolds(state, relPath), action: "noop", textUnknown };
|
|
47035
47489
|
}
|
|
47036
47490
|
const text2 = await applyExtractedText({
|
|
47037
47491
|
workspaceSlug,
|
|
@@ -47041,9 +47495,13 @@ async function applyFileRow(args) {
|
|
|
47041
47495
|
relPath,
|
|
47042
47496
|
bytesInMirror: true,
|
|
47043
47497
|
fetchImpl,
|
|
47044
|
-
output
|
|
47498
|
+
output,
|
|
47499
|
+
stillCurrent: stillAt(existing)
|
|
47045
47500
|
});
|
|
47046
|
-
|
|
47501
|
+
if (text2.stale) {
|
|
47502
|
+
return { state, action: "noop", textUnknown };
|
|
47503
|
+
}
|
|
47504
|
+
const { state: next2 } = upsertFileEntry(clearRowHolds(state, relPath), {
|
|
47047
47505
|
...existing,
|
|
47048
47506
|
updated_at: row.updated_at,
|
|
47049
47507
|
text_hash: textUnknown ? existing.text_hash : text2.textHash
|
|
@@ -47057,6 +47515,23 @@ async function applyFileRow(args) {
|
|
|
47057
47515
|
textUnknown
|
|
47058
47516
|
};
|
|
47059
47517
|
}
|
|
47518
|
+
const held = stillHeld2({
|
|
47519
|
+
hold: getFilePullHold(state, relPath),
|
|
47520
|
+
row,
|
|
47521
|
+
abs,
|
|
47522
|
+
relPath,
|
|
47523
|
+
workspaceSlug,
|
|
47524
|
+
ackHash: existing?.content_hash,
|
|
47525
|
+
now
|
|
47526
|
+
});
|
|
47527
|
+
if (held) {
|
|
47528
|
+
const key = `held:${relPath}:${row.updated_at}:${held.kind}`;
|
|
47529
|
+
if (!args.loggedStashNotices?.has(key)) {
|
|
47530
|
+
args.loggedStashNotices?.add(key);
|
|
47531
|
+
output?.log(filePullHeldLine({ path: relPath, kind: held.kind, retryInSeconds: held.retryInSeconds }));
|
|
47532
|
+
}
|
|
47533
|
+
return held.kind === "stash_failed" ? { state, action: "skipped", stashFailed: true } : { state, action: "skipped", integrityFailed: true };
|
|
47534
|
+
}
|
|
47060
47535
|
const got = await getFile({ baseUrl, pat, id: row.id, fetchImpl });
|
|
47061
47536
|
if (!got.ok) {
|
|
47062
47537
|
return { state, action: "skipped" };
|
|
@@ -47075,22 +47550,51 @@ async function applyFileRow(args) {
|
|
|
47075
47550
|
existing,
|
|
47076
47551
|
textSignal,
|
|
47077
47552
|
fetchImpl,
|
|
47078
|
-
output
|
|
47553
|
+
output,
|
|
47554
|
+
stillCurrent: stillAt(existing)
|
|
47079
47555
|
});
|
|
47080
47556
|
}
|
|
47081
47557
|
const bytes = await downloadBytes(got.row.download_url, fetchImpl);
|
|
47082
47558
|
if (sha256Bytes(bytes) !== row.content_hash) {
|
|
47083
47559
|
output?.warn(`Integrity check failed for files/${relPath} (hash mismatch) — skipped this pull.`);
|
|
47084
|
-
return {
|
|
47560
|
+
return {
|
|
47561
|
+
state: holdRow(state, getFilePullHold(state, relPath), {
|
|
47562
|
+
row,
|
|
47563
|
+
relPath,
|
|
47564
|
+
kind: "integrity_failed",
|
|
47565
|
+
abs,
|
|
47566
|
+
now
|
|
47567
|
+
}),
|
|
47568
|
+
action: "skipped",
|
|
47569
|
+
integrityFailed: true
|
|
47570
|
+
};
|
|
47571
|
+
}
|
|
47572
|
+
const ackAtWrite = ackNow();
|
|
47573
|
+
if (ackAtWrite && isLaterVersion(ackAtWrite, row)) {
|
|
47574
|
+
const key = `skip:${relPath}:${row.id}:${row.updated_at}`;
|
|
47575
|
+
if (!args.loggedStashNotices?.has(key)) {
|
|
47576
|
+
args.loggedStashNotices?.add(key);
|
|
47577
|
+
output?.log(filePullSkippedLine({
|
|
47578
|
+
path: relPath,
|
|
47579
|
+
rowId: row.id,
|
|
47580
|
+
reason: ackAtWrite.id === row.id ? "older" : "replaced"
|
|
47581
|
+
}));
|
|
47582
|
+
}
|
|
47583
|
+
return { state: clearRowHolds(state, relPath), action: "skipped" };
|
|
47584
|
+
}
|
|
47585
|
+
const ackIsRow = ackAtWrite !== undefined && ackAtWrite.id === row.id && ackAtWrite.updated_at === row.updated_at && ackAtWrite.content_hash === row.content_hash;
|
|
47586
|
+
if (ackIsRow && (!sameFileVersion(ackAtWrite, existing) || unsentBytesAt({ workspaceSlug, abs, safeHashes: [row.content_hash] }).kind === "unsent")) {
|
|
47587
|
+
return { state: clearRowHolds(state, relPath), action: "noop", textUnknown };
|
|
47085
47588
|
}
|
|
47086
|
-
const stashFailure =
|
|
47589
|
+
const stashFailure = keepUnsentLocalCopy({
|
|
47087
47590
|
workspaceSlug,
|
|
47088
47591
|
relPath,
|
|
47089
47592
|
abs,
|
|
47090
|
-
|
|
47091
|
-
rowHash: row.content_hash,
|
|
47092
|
-
ambient: suppressor !== undefined,
|
|
47593
|
+
safeHashes: [row.content_hash, ackAtWrite?.content_hash],
|
|
47093
47594
|
now,
|
|
47595
|
+
via: "pull",
|
|
47596
|
+
versionKey: row.content_hash,
|
|
47597
|
+
onBoot: suppressor === undefined,
|
|
47094
47598
|
output,
|
|
47095
47599
|
loggedStashNotices: args.loggedStashNotices
|
|
47096
47600
|
});
|
|
@@ -47100,7 +47604,18 @@ async function applyFileRow(args) {
|
|
|
47100
47604
|
args.loggedStashNotices?.add(key);
|
|
47101
47605
|
output?.log(fileStashFailedLine({ path: relPath, ...stashFailure }));
|
|
47102
47606
|
}
|
|
47103
|
-
return {
|
|
47607
|
+
return {
|
|
47608
|
+
state: holdRow(state, getFilePullHold(state, relPath), {
|
|
47609
|
+
row,
|
|
47610
|
+
relPath,
|
|
47611
|
+
kind: "stash_failed",
|
|
47612
|
+
abs,
|
|
47613
|
+
now,
|
|
47614
|
+
failure: stashFailure
|
|
47615
|
+
}),
|
|
47616
|
+
action: "skipped",
|
|
47617
|
+
stashFailed: true
|
|
47618
|
+
};
|
|
47104
47619
|
}
|
|
47105
47620
|
suppressor?.registerWrite(abs, { bodyHash: row.content_hash, frontmatterHash: "" });
|
|
47106
47621
|
writeFilesBytes(workspaceSlug, relPath, bytes);
|
|
@@ -47126,7 +47641,8 @@ async function applyFileRow(args) {
|
|
|
47126
47641
|
relPath,
|
|
47127
47642
|
bytesInMirror: true,
|
|
47128
47643
|
fetchImpl,
|
|
47129
|
-
output
|
|
47644
|
+
output,
|
|
47645
|
+
stillCurrent: stillAt(ackAtWrite)
|
|
47130
47646
|
});
|
|
47131
47647
|
}
|
|
47132
47648
|
let inode;
|
|
@@ -47135,7 +47651,7 @@ async function applyFileRow(args) {
|
|
|
47135
47651
|
} catch {
|
|
47136
47652
|
inode = undefined;
|
|
47137
47653
|
}
|
|
47138
|
-
const { state: next } = upsertFileEntry(state, {
|
|
47654
|
+
const { state: next } = upsertFileEntry(clearRowHolds(state, relPath), {
|
|
47139
47655
|
id: row.id,
|
|
47140
47656
|
path: relPath,
|
|
47141
47657
|
content_hash: row.content_hash,
|
|
@@ -47171,7 +47687,8 @@ async function applyPointerRow(args) {
|
|
|
47171
47687
|
relPath,
|
|
47172
47688
|
bytesInMirror,
|
|
47173
47689
|
fetchImpl: args.fetchImpl,
|
|
47174
|
-
output: args.output
|
|
47690
|
+
output: args.output,
|
|
47691
|
+
stillCurrent: args.stillCurrent
|
|
47175
47692
|
});
|
|
47176
47693
|
return {
|
|
47177
47694
|
state,
|
|
@@ -47183,53 +47700,87 @@ async function applyPointerRow(args) {
|
|
|
47183
47700
|
pointer: true
|
|
47184
47701
|
};
|
|
47185
47702
|
}
|
|
47186
|
-
function
|
|
47187
|
-
|
|
47188
|
-
|
|
47189
|
-
|
|
47190
|
-
|
|
47191
|
-
|
|
47192
|
-
|
|
47193
|
-
|
|
47194
|
-
|
|
47195
|
-
|
|
47196
|
-
|
|
47197
|
-
}
|
|
47198
|
-
if (!st.isFile())
|
|
47199
|
-
return { reason: "not_a_file" };
|
|
47200
|
-
let localBytes;
|
|
47201
|
-
try {
|
|
47202
|
-
localBytes = readFileSync28(abs);
|
|
47203
|
-
} catch (err) {
|
|
47204
|
-
return { reason: "unreadable", code: errCode(err) };
|
|
47703
|
+
function clearRowHolds(state, relPath) {
|
|
47704
|
+
return removeFilePullHold(removeFilePullHold(state, relPath, "stash_failed"), relPath, "integrity_failed");
|
|
47705
|
+
}
|
|
47706
|
+
function clearHoldsOfRow(state, relPath, rowId) {
|
|
47707
|
+
const hold = getFilePullHold(state, relPath);
|
|
47708
|
+
return hold && hold.row_id === rowId ? clearRowHolds(state, relPath) : state;
|
|
47709
|
+
}
|
|
47710
|
+
function dropAck(state, relPath, ack, snapshotEntry, commit2) {
|
|
47711
|
+
const next = removeFileEntry(state, relPath).state;
|
|
47712
|
+
if (commit2 && !sameFileVersion(snapshotEntry, ack)) {
|
|
47713
|
+
commit2(upsertFileEntry(state, ack).state, next);
|
|
47205
47714
|
}
|
|
47206
|
-
|
|
47207
|
-
|
|
47715
|
+
return next;
|
|
47716
|
+
}
|
|
47717
|
+
function holdRow(state, prior, args) {
|
|
47718
|
+
const { row, relPath, kind, now } = args;
|
|
47719
|
+
const sameVersion = prior?.kind === kind && prior.row_id === row.id && prior.row_updated_at === row.updated_at && prior.row_hash === row.content_hash;
|
|
47720
|
+
const attempts = sameVersion ? prior.attempts + 1 : 1;
|
|
47721
|
+
const at = now();
|
|
47722
|
+
return upsertFilePullHold(clearRowHolds(state, relPath), {
|
|
47723
|
+
target: "file",
|
|
47724
|
+
path: relPath,
|
|
47725
|
+
kind,
|
|
47726
|
+
row_id: row.id,
|
|
47727
|
+
row_updated_at: row.updated_at,
|
|
47728
|
+
row_hash: row.content_hash,
|
|
47729
|
+
...args.failure ? { reason: args.failure.reason } : {},
|
|
47730
|
+
...args.failure?.code ? { code: args.failure.code } : {},
|
|
47731
|
+
local: localFingerprint(args.abs) ?? null,
|
|
47732
|
+
attempts,
|
|
47733
|
+
...kind === "integrity_failed" ? { next_retry_at: new Date(at.getTime() + integrityBackoffMs(attempts)).toISOString() } : {}
|
|
47734
|
+
});
|
|
47735
|
+
}
|
|
47736
|
+
var INTEGRITY_BACKOFF_MS = 60000;
|
|
47737
|
+
var INTEGRITY_BACKOFF_CAP_MS = 3600000;
|
|
47738
|
+
function integrityBackoffMs(attempts) {
|
|
47739
|
+
return Math.min(INTEGRITY_BACKOFF_MS * 2 ** Math.max(0, attempts - 1), INTEGRITY_BACKOFF_CAP_MS);
|
|
47740
|
+
}
|
|
47741
|
+
function stillHeld2(args) {
|
|
47742
|
+
const { hold, row } = args;
|
|
47743
|
+
if (!hold || hold.row_id !== row.id || hold.row_updated_at !== row.updated_at || hold.row_hash !== row.content_hash) {
|
|
47208
47744
|
return null;
|
|
47209
|
-
let stashed;
|
|
47210
|
-
try {
|
|
47211
|
-
stashed = stashFileConflictOnce({
|
|
47212
|
-
workspaceSlug,
|
|
47213
|
-
relPath,
|
|
47214
|
-
timestamp: args.now(),
|
|
47215
|
-
bytes: localBytes,
|
|
47216
|
-
side: "local"
|
|
47217
|
-
});
|
|
47218
|
-
} catch (err) {
|
|
47219
|
-
return { reason: "stash_io", code: errCode(err) };
|
|
47220
47745
|
}
|
|
47221
|
-
|
|
47222
|
-
|
|
47746
|
+
if (hold.kind === "integrity_failed") {
|
|
47747
|
+
const due = hold.next_retry_at ? Date.parse(hold.next_retry_at) : Number.NaN;
|
|
47748
|
+
const wait = due - args.now().getTime();
|
|
47749
|
+
return Number.isFinite(wait) && wait > 0 ? { kind: "integrity_failed", retryInSeconds: Math.ceil(wait / 1000) } : null;
|
|
47750
|
+
}
|
|
47751
|
+
if (hold.kind !== "stash_failed")
|
|
47223
47752
|
return null;
|
|
47224
|
-
|
|
47225
|
-
|
|
47226
|
-
|
|
47227
|
-
|
|
47228
|
-
|
|
47753
|
+
const local = localFingerprint(args.abs);
|
|
47754
|
+
const unchanged = local !== undefined && (local === null ? hold.local === null : hold.local != null && hold.local.size === local.size && hold.local.mtime_ms === local.mtime_ms && hold.local.ino === local.ino);
|
|
47755
|
+
if (!unchanged)
|
|
47756
|
+
return null;
|
|
47757
|
+
const failure = probeKeep({
|
|
47758
|
+
workspaceSlug: args.workspaceSlug,
|
|
47759
|
+
relPath: args.relPath,
|
|
47760
|
+
abs: args.abs,
|
|
47761
|
+
safeHashes: [row.content_hash, args.ackHash]
|
|
47762
|
+
});
|
|
47763
|
+
return failure ? { kind: "stash_failed" } : null;
|
|
47229
47764
|
}
|
|
47230
|
-
function
|
|
47231
|
-
|
|
47232
|
-
|
|
47765
|
+
function pruneFilePullHolds(args) {
|
|
47766
|
+
let state = args.state;
|
|
47767
|
+
for (const hold of listFilePullHolds(state)) {
|
|
47768
|
+
if (hold.kind === "kept_after_delete") {
|
|
47769
|
+
let gone = getFileEntry(state, hold.path) !== undefined;
|
|
47770
|
+
if (!gone) {
|
|
47771
|
+
try {
|
|
47772
|
+
gone = localFingerprint(filesSubtreePath(args.workspaceSlug, hold.path)) === null;
|
|
47773
|
+
} catch {
|
|
47774
|
+
gone = false;
|
|
47775
|
+
}
|
|
47776
|
+
}
|
|
47777
|
+
if (gone)
|
|
47778
|
+
state = removeFilePullHold(state, hold.path, hold.kind);
|
|
47779
|
+
} else if ((hold.kind === "stash_failed" || hold.kind === "integrity_failed") && args.cursorPass && !args.deliveredPaths.has(hold.path)) {
|
|
47780
|
+
state = removeFilePullHold(state, hold.path, hold.kind);
|
|
47781
|
+
}
|
|
47782
|
+
}
|
|
47783
|
+
return state;
|
|
47233
47784
|
}
|
|
47234
47785
|
function textIsCurrent(existing, row, workspaceSlug, relPath) {
|
|
47235
47786
|
const signal = extractedTextSignal(row);
|
|
@@ -47249,7 +47800,7 @@ function textIsCurrent(existing, row, workspaceSlug, relPath) {
|
|
|
47249
47800
|
}
|
|
47250
47801
|
|
|
47251
47802
|
// src/lib/files/rejection-sync.ts
|
|
47252
|
-
import { readFileSync as
|
|
47803
|
+
import { readFileSync as readFileSync30, statSync as statSync16 } from "node:fs";
|
|
47253
47804
|
|
|
47254
47805
|
// src/lib/files/rejections-api.ts
|
|
47255
47806
|
var RejectionsFeedSchema = exports_external.object({
|
|
@@ -47382,7 +47933,7 @@ function settledDisposition(args) {
|
|
|
47382
47933
|
return null;
|
|
47383
47934
|
let bytes;
|
|
47384
47935
|
try {
|
|
47385
|
-
bytes =
|
|
47936
|
+
bytes = readFileSync30(absolute2);
|
|
47386
47937
|
} catch {
|
|
47387
47938
|
return null;
|
|
47388
47939
|
}
|
|
@@ -47447,6 +47998,49 @@ function rearmMissingReports(state) {
|
|
|
47447
47998
|
|
|
47448
47999
|
// src/commands/sync.ts
|
|
47449
48000
|
init_output();
|
|
48001
|
+
|
|
48002
|
+
// src/lib/pull-live-gate.ts
|
|
48003
|
+
import { lstatSync as lstatSync7, readFileSync as readFileSync31 } from "node:fs";
|
|
48004
|
+
function decideLivePull(args) {
|
|
48005
|
+
const { workspaceSlug, strategy, live, snapshotEntry, writeSlug } = args;
|
|
48006
|
+
if (strategy.preserveLocalBodyOnPull)
|
|
48007
|
+
return { kind: "write" };
|
|
48008
|
+
if (strategy.type === "artists")
|
|
48009
|
+
return { kind: "write" };
|
|
48010
|
+
const absolutePath = entityFilePath(workspaceSlug, strategy.type, writeSlug);
|
|
48011
|
+
const ack = snapshotEntry ? committedEntryIfNewer(workspaceSlug, strategy.type, snapshotEntry) ?? snapshotEntry : readCommittedEntry(workspaceSlug, strategy.type, live.id);
|
|
48012
|
+
if (!ack)
|
|
48013
|
+
return { kind: "write" };
|
|
48014
|
+
const older = isLaterVersion(ack, live);
|
|
48015
|
+
if (ack.slug !== writeSlug)
|
|
48016
|
+
return older ? { kind: "skip" } : { kind: "write" };
|
|
48017
|
+
const newer = !older && live.updated_at !== ack.updated_at;
|
|
48018
|
+
let raw = null;
|
|
48019
|
+
try {
|
|
48020
|
+
raw = lstatSync7(absolutePath).isFile() ? readFileSync31(absolutePath, "utf8") : null;
|
|
48021
|
+
} catch {
|
|
48022
|
+
raw = null;
|
|
48023
|
+
}
|
|
48024
|
+
if (raw === null)
|
|
48025
|
+
return older ? { kind: "skip" } : { kind: "write" };
|
|
48026
|
+
if (frontmatterParseError(raw) !== null) {
|
|
48027
|
+
return { kind: "defer", reason: "unreadable", absolutePath };
|
|
48028
|
+
}
|
|
48029
|
+
const disk = computeMirrorHashes(strategy, raw);
|
|
48030
|
+
const matchesAck = disk.bodyHash === ack.body_hash && disk.frontmatterHash === ack.frontmatter_hash;
|
|
48031
|
+
if (!newer) {
|
|
48032
|
+
return matchesAck ? { kind: "skip" } : { kind: "defer", reason: "local_edit", absolutePath };
|
|
48033
|
+
}
|
|
48034
|
+
if (matchesAck)
|
|
48035
|
+
return { kind: "write" };
|
|
48036
|
+
const incoming = computeMirrorHashes(strategy, renderMirrorWithBodyPreserve(workspaceSlug, strategy, live, writeSlug));
|
|
48037
|
+
if (disk.bodyHash === incoming.bodyHash && disk.frontmatterHash === incoming.frontmatterHash) {
|
|
48038
|
+
return { kind: "adopt", raw };
|
|
48039
|
+
}
|
|
48040
|
+
return { kind: "defer", reason: "local_edit", absolutePath };
|
|
48041
|
+
}
|
|
48042
|
+
|
|
48043
|
+
// src/commands/sync.ts
|
|
47450
48044
|
var memberRefreshFailedAt = new Map;
|
|
47451
48045
|
var MEMBER_REFRESH_RETRY_AFTER_MS = 60 * 60 * 1000;
|
|
47452
48046
|
function classifyApiError(err) {
|
|
@@ -47468,7 +48062,7 @@ function hasExistingContent(workspacePath) {
|
|
|
47468
48062
|
if (!e.isDirectory())
|
|
47469
48063
|
return true;
|
|
47470
48064
|
try {
|
|
47471
|
-
return readdirSync7(
|
|
48065
|
+
return readdirSync7(join19(workspacePath, e.name)).length > 0;
|
|
47472
48066
|
} catch {
|
|
47473
48067
|
return true;
|
|
47474
48068
|
}
|
|
@@ -47498,7 +48092,7 @@ function unlinkIfProvablyOurs(workspaceSlug, strategy, entry2, suppressor) {
|
|
|
47498
48092
|
}
|
|
47499
48093
|
let raw;
|
|
47500
48094
|
try {
|
|
47501
|
-
raw =
|
|
48095
|
+
raw = readFileSync32(target, "utf8");
|
|
47502
48096
|
} catch (err) {
|
|
47503
48097
|
if (err?.code === "ENOENT") {
|
|
47504
48098
|
return { didUnlink: false };
|
|
@@ -47516,7 +48110,7 @@ function unlinkIfProvablyOurs(workspaceSlug, strategy, entry2, suppressor) {
|
|
|
47516
48110
|
}
|
|
47517
48111
|
try {
|
|
47518
48112
|
suppressor?.registerDelete(target);
|
|
47519
|
-
|
|
48113
|
+
unlinkSync11(target);
|
|
47520
48114
|
return { didUnlink: true };
|
|
47521
48115
|
} catch (err) {
|
|
47522
48116
|
if (err?.code === "ENOENT") {
|
|
@@ -47623,7 +48217,7 @@ async function replayPermanentDocuments(args) {
|
|
|
47623
48217
|
for (const row of rows) {
|
|
47624
48218
|
if (row.deleted_at !== null && row.permanent_delete === true)
|
|
47625
48219
|
continue;
|
|
47626
|
-
const outcome = applyRow(args.workspaceSlug, args.strategy, row, state, args.output, args.suppressor, args.now, args.isFrozen, args.loggedTombstoneSkips, args.recentUnlinks);
|
|
48220
|
+
const outcome = applyRow(args.workspaceSlug, args.strategy, row, state, args.output, args.suppressor, args.now, args.isFrozen, args.loggedTombstoneSkips, args.recentUnlinks, args.deferral);
|
|
47627
48221
|
state = outcome.state;
|
|
47628
48222
|
if (outcome.action === "written")
|
|
47629
48223
|
counters.written++;
|
|
@@ -47760,6 +48354,10 @@ async function runSync(workspaceSlug, deps = {}) {
|
|
|
47760
48354
|
committed = state;
|
|
47761
48355
|
};
|
|
47762
48356
|
const counters = {};
|
|
48357
|
+
const deferral = {
|
|
48358
|
+
onPushDeferred: deps.onPushDeferred,
|
|
48359
|
+
logged: deps.loggedPullDeferrals
|
|
48360
|
+
};
|
|
47763
48361
|
let restoresDeferred = 0;
|
|
47764
48362
|
try {
|
|
47765
48363
|
for (const entityType of allEntityTypes()) {
|
|
@@ -47783,6 +48381,7 @@ async function runSync(workspaceSlug, deps = {}) {
|
|
|
47783
48381
|
isFrozen: deps.isFrozen,
|
|
47784
48382
|
loggedTombstoneSkips: deps.loggedTombstoneSkips,
|
|
47785
48383
|
recentUnlinks: deps.recentUnlinks,
|
|
48384
|
+
deferral,
|
|
47786
48385
|
commit: (before, after) => {
|
|
47787
48386
|
const next = commitState(before, after);
|
|
47788
48387
|
committed = next;
|
|
@@ -47868,7 +48467,7 @@ async function runSync(workspaceSlug, deps = {}) {
|
|
|
47868
48467
|
}
|
|
47869
48468
|
let outcome;
|
|
47870
48469
|
try {
|
|
47871
|
-
outcome = applyRow(slug, strategy, row, state, out, deps.suppressor, now, deps.isFrozen, deps.loggedTombstoneSkips, deps.recentUnlinks);
|
|
48470
|
+
outcome = applyRow(slug, strategy, row, state, out, deps.suppressor, now, deps.isFrozen, deps.loggedTombstoneSkips, deps.recentUnlinks, deferral);
|
|
47872
48471
|
} catch (err) {
|
|
47873
48472
|
if (!(err instanceof MirrorSymlinkError))
|
|
47874
48473
|
throw err;
|
|
@@ -47928,6 +48527,7 @@ async function runSync(workspaceSlug, deps = {}) {
|
|
|
47928
48527
|
isFrozen: deps.isFrozen,
|
|
47929
48528
|
loggedTombstoneSkips: deps.loggedTombstoneSkips,
|
|
47930
48529
|
recentUnlinks: deps.recentUnlinks,
|
|
48530
|
+
deferral,
|
|
47931
48531
|
commit: (before, after) => {
|
|
47932
48532
|
const next = commitState(before, after);
|
|
47933
48533
|
committed = next;
|
|
@@ -48221,7 +48821,7 @@ async function runSync(workspaceSlug, deps = {}) {
|
|
|
48221
48821
|
out.success(`Done in ${elapsed}s.`);
|
|
48222
48822
|
return { exitCode: 0, applied: totalWritten + totalMoved + totalDeleted, restoresDeferred };
|
|
48223
48823
|
}
|
|
48224
|
-
function applyRow(workspaceSlug, strategy, row, state, out, suppressor, now = () => new Date, isFrozen, loggedTombstoneSkips, recentUnlinks) {
|
|
48824
|
+
function applyRow(workspaceSlug, strategy, row, state, out, suppressor, now = () => new Date, isFrozen, loggedTombstoneSkips, recentUnlinks, deferral) {
|
|
48225
48825
|
if (isFrozen?.(strategy.type, row.id)) {
|
|
48226
48826
|
return { state, action: "skipped" };
|
|
48227
48827
|
}
|
|
@@ -48372,10 +48972,38 @@ function applyRow(workspaceSlug, strategy, row, state, out, suppressor, now = ()
|
|
|
48372
48972
|
return { state, action: "noop_overwrite" };
|
|
48373
48973
|
}
|
|
48374
48974
|
}
|
|
48975
|
+
let adoptRaw;
|
|
48976
|
+
if (suppressor && relocation.kind !== "moved") {
|
|
48977
|
+
const gate = decideLivePull({
|
|
48978
|
+
workspaceSlug,
|
|
48979
|
+
strategy,
|
|
48980
|
+
live,
|
|
48981
|
+
snapshotEntry: existingEntry,
|
|
48982
|
+
writeSlug
|
|
48983
|
+
});
|
|
48984
|
+
if (gate.kind === "skip")
|
|
48985
|
+
return { state, action: "noop_overwrite" };
|
|
48986
|
+
if (gate.kind === "defer") {
|
|
48987
|
+
const key = `${strategy.type}:${live.id}:${live.updated_at}`;
|
|
48988
|
+
if (!deferral?.logged?.has(key)) {
|
|
48989
|
+
deferral?.logged?.add(key);
|
|
48990
|
+
out.log(entityPullDeferredLine({
|
|
48991
|
+
entityType: strategy.type,
|
|
48992
|
+
slug: writeSlug,
|
|
48993
|
+
id: live.id,
|
|
48994
|
+
reason: gate.reason
|
|
48995
|
+
}));
|
|
48996
|
+
deferral?.onPushDeferred?.(gate.absolutePath);
|
|
48997
|
+
}
|
|
48998
|
+
return { state, action: "deferred" };
|
|
48999
|
+
}
|
|
49000
|
+
if (gate.kind === "adopt")
|
|
49001
|
+
adoptRaw = gate.raw;
|
|
49002
|
+
}
|
|
48375
49003
|
if (!suppressor && existingEntry && existingEntry.updated_at !== live.updated_at && relocation.kind !== "moved") {
|
|
48376
49004
|
let localRaw = null;
|
|
48377
49005
|
try {
|
|
48378
|
-
localRaw =
|
|
49006
|
+
localRaw = readFileSync32(entityFilePath(workspaceSlug, strategy.type, writeSlug), "utf8");
|
|
48379
49007
|
} catch {
|
|
48380
49008
|
localRaw = null;
|
|
48381
49009
|
}
|
|
@@ -48402,7 +49030,7 @@ function applyRow(workspaceSlug, strategy, row, state, out, suppressor, now = ()
|
|
|
48402
49030
|
const target = entityFilePath(workspaceSlug, strategy.type, writeSlug);
|
|
48403
49031
|
let localRaw = null;
|
|
48404
49032
|
try {
|
|
48405
|
-
localRaw =
|
|
49033
|
+
localRaw = lstatSync8(target).isFile() ? readFileSync32(target, "utf8") : null;
|
|
48406
49034
|
} catch {
|
|
48407
49035
|
localRaw = null;
|
|
48408
49036
|
}
|
|
@@ -48430,7 +49058,7 @@ function applyRow(workspaceSlug, strategy, row, state, out, suppressor, now = ()
|
|
|
48430
49058
|
}
|
|
48431
49059
|
}
|
|
48432
49060
|
}
|
|
48433
|
-
const content = writeMirrorWithBodyPreserve(workspaceSlug, strategy, live, suppressor, writeSlug);
|
|
49061
|
+
const content = adoptRaw ?? writeMirrorWithBodyPreserve(workspaceSlug, strategy, live, suppressor, writeSlug);
|
|
48434
49062
|
const hashes = computeMirrorHashes(strategy, content);
|
|
48435
49063
|
let inode;
|
|
48436
49064
|
try {
|
|
@@ -48467,7 +49095,7 @@ function applyRow(workspaceSlug, strategy, row, state, out, suppressor, now = ()
|
|
|
48467
49095
|
memory_hash: memoryHash,
|
|
48468
49096
|
memory_generated: memoryGenerated
|
|
48469
49097
|
});
|
|
48470
|
-
const action = relocation.kind === "moved" ? "moved" : "written";
|
|
49098
|
+
const action = adoptRaw !== undefined ? "noop_overwrite" : relocation.kind === "moved" ? "moved" : "written";
|
|
48471
49099
|
return { state: nextState, action };
|
|
48472
49100
|
}
|
|
48473
49101
|
var syncCommand = defineCommand({
|
|
@@ -48568,7 +49196,7 @@ function backfillStateInodes(workspaceSlug, state, statFn = (p) => statSync18(p)
|
|
|
48568
49196
|
return { state, changed: false };
|
|
48569
49197
|
return { state: { ...state, entries: nextEntries }, changed: true };
|
|
48570
49198
|
}
|
|
48571
|
-
function backfillBodyLengths(workspaceSlug, state, readFn = (p) =>
|
|
49199
|
+
function backfillBodyLengths(workspaceSlug, state, readFn = (p) => readFileSync33(p, "utf8")) {
|
|
48572
49200
|
let changed = false;
|
|
48573
49201
|
const nextEntries = {};
|
|
48574
49202
|
for (const [entityType, bucket] of Object.entries(state.entries)) {
|
|
@@ -48600,7 +49228,7 @@ function isOwnWriteEcho(suppressor, absolutePath, strategy, raw) {
|
|
|
48600
49228
|
}
|
|
48601
49229
|
function destinationIsUnreadable(absolutePath) {
|
|
48602
49230
|
try {
|
|
48603
|
-
return frontmatterParseError(
|
|
49231
|
+
return frontmatterParseError(readFileSync33(absolutePath, "utf8")) !== null;
|
|
48604
49232
|
} catch {
|
|
48605
49233
|
return false;
|
|
48606
49234
|
}
|
|
@@ -48721,7 +49349,7 @@ function makeWatchHandler(deps) {
|
|
|
48721
49349
|
spinner.update(`Watching ${workspaceSlug} · ${watchingSummary(getState())}`);
|
|
48722
49350
|
return;
|
|
48723
49351
|
}
|
|
48724
|
-
const bytes =
|
|
49352
|
+
const bytes = readFileSync33(absolutePath);
|
|
48725
49353
|
const hash2 = sha256Bytes(bytes);
|
|
48726
49354
|
if (suppressor?.shouldSuppressUpsert(absolutePath, {
|
|
48727
49355
|
bodyHash: hash2,
|
|
@@ -48785,7 +49413,7 @@ function makeWatchHandler(deps) {
|
|
|
48785
49413
|
if (suppressor) {
|
|
48786
49414
|
let raw = null;
|
|
48787
49415
|
try {
|
|
48788
|
-
raw =
|
|
49416
|
+
raw = readFileSync33(absolutePath, "utf8");
|
|
48789
49417
|
} catch {
|
|
48790
49418
|
raw = null;
|
|
48791
49419
|
}
|
|
@@ -48824,7 +49452,7 @@ function makeWatchHandler(deps) {
|
|
|
48824
49452
|
if (suppressor) {
|
|
48825
49453
|
let raw = null;
|
|
48826
49454
|
try {
|
|
48827
|
-
raw =
|
|
49455
|
+
raw = readFileSync33(absolutePath, "utf8");
|
|
48828
49456
|
} catch {
|
|
48829
49457
|
raw = null;
|
|
48830
49458
|
}
|
|
@@ -48863,7 +49491,7 @@ function makeWatchHandler(deps) {
|
|
|
48863
49491
|
if (suppressor) {
|
|
48864
49492
|
let raw = null;
|
|
48865
49493
|
try {
|
|
48866
|
-
raw =
|
|
49494
|
+
raw = readFileSync33(absolutePath, "utf8");
|
|
48867
49495
|
} catch {
|
|
48868
49496
|
raw = null;
|
|
48869
49497
|
}
|
|
@@ -48902,7 +49530,7 @@ function makeWatchHandler(deps) {
|
|
|
48902
49530
|
if (suppressor) {
|
|
48903
49531
|
let raw = null;
|
|
48904
49532
|
try {
|
|
48905
|
-
raw =
|
|
49533
|
+
raw = readFileSync33(absolutePath, "utf8");
|
|
48906
49534
|
} catch {
|
|
48907
49535
|
raw = null;
|
|
48908
49536
|
}
|
|
@@ -48941,7 +49569,7 @@ function makeWatchHandler(deps) {
|
|
|
48941
49569
|
if (suppressor) {
|
|
48942
49570
|
let raw = null;
|
|
48943
49571
|
try {
|
|
48944
|
-
raw =
|
|
49572
|
+
raw = readFileSync33(absolutePath, "utf8");
|
|
48945
49573
|
} catch {
|
|
48946
49574
|
raw = null;
|
|
48947
49575
|
}
|
|
@@ -48987,7 +49615,7 @@ function makeWatchHandler(deps) {
|
|
|
48987
49615
|
const strategy = getStrategy(classifiedFrom.entityType);
|
|
48988
49616
|
let toRaw = null;
|
|
48989
49617
|
try {
|
|
48990
|
-
toRaw =
|
|
49618
|
+
toRaw = readFileSync33(event.toPath, "utf8");
|
|
48991
49619
|
} catch {
|
|
48992
49620
|
toRaw = null;
|
|
48993
49621
|
}
|
|
@@ -49059,7 +49687,7 @@ function makeWatchHandler(deps) {
|
|
|
49059
49687
|
if (event.kind === "upsert") {
|
|
49060
49688
|
try {
|
|
49061
49689
|
ownEcho = suppressor?.shouldSuppressUpsert(absolutePath, {
|
|
49062
|
-
bodyHash: stableHash(
|
|
49690
|
+
bodyHash: stableHash(readFileSync33(absolutePath, "utf8")),
|
|
49063
49691
|
frontmatterHash: ""
|
|
49064
49692
|
}) === true;
|
|
49065
49693
|
} catch {
|
|
@@ -49114,7 +49742,7 @@ function makeWatchHandler(deps) {
|
|
|
49114
49742
|
if (strategy) {
|
|
49115
49743
|
let raw = null;
|
|
49116
49744
|
try {
|
|
49117
|
-
raw =
|
|
49745
|
+
raw = readFileSync33(absolutePath, "utf8");
|
|
49118
49746
|
} catch {
|
|
49119
49747
|
raw = null;
|
|
49120
49748
|
}
|
|
@@ -49561,6 +50189,7 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
49561
50189
|
}
|
|
49562
50190
|
const loggedTombstoneSkips = new Set;
|
|
49563
50191
|
const loggedStashNotices = new Set;
|
|
50192
|
+
const loggedPullDeferrals = new Set;
|
|
49564
50193
|
const initial = await runSync(workspaceSlug, {
|
|
49565
50194
|
fetchImpl,
|
|
49566
50195
|
keychain,
|
|
@@ -49826,7 +50455,11 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
49826
50455
|
isFrozen,
|
|
49827
50456
|
isFileFrozen,
|
|
49828
50457
|
loggedTombstoneSkips,
|
|
49829
|
-
loggedStashNotices
|
|
50458
|
+
loggedStashNotices,
|
|
50459
|
+
onPushDeferred: (absolutePath) => {
|
|
50460
|
+
watcher.nudge(absolutePath);
|
|
50461
|
+
},
|
|
50462
|
+
loggedPullDeferrals
|
|
49830
50463
|
}));
|
|
49831
50464
|
if (result.exitCode === 0) {
|
|
49832
50465
|
const applied = result.applied ?? 0;
|
|
@@ -50191,6 +50824,21 @@ function reportOutcome(spinner, slug, state, outcome) {
|
|
|
50191
50824
|
return;
|
|
50192
50825
|
}
|
|
50193
50826
|
}
|
|
50827
|
+
function fileConflictMessage(outcome) {
|
|
50828
|
+
const head = `Conflict on files/${outcome.relPath}${outcome.stashPath ? ` → ${outcome.stashPath}` : ""}`;
|
|
50829
|
+
switch (outcome.restore) {
|
|
50830
|
+
case "skipped":
|
|
50831
|
+
return `${head} (Plinth's version was not applied here; daemon.log says why)`;
|
|
50832
|
+
case "removed":
|
|
50833
|
+
return outcome.stashPath ? `${head} (deleted in Plinth, so removed here; your copy is stashed)` : `${head} (deleted in Plinth too, and gone here)`;
|
|
50834
|
+
case "no_bytes":
|
|
50835
|
+
return `${head} (Plinth holds no bytes for it; the local file was left as it is)`;
|
|
50836
|
+
case "restored":
|
|
50837
|
+
return outcome.stashPath ? `${head} (cloud-wins; local copy stashed)` : `${head} (cloud-wins; cloud copy restored)`;
|
|
50838
|
+
default:
|
|
50839
|
+
return `${head} (already in Plinth; your copy is stashed)`;
|
|
50840
|
+
}
|
|
50841
|
+
}
|
|
50194
50842
|
function reportFileOutcome(spinner, slug, state, outcome) {
|
|
50195
50843
|
const summary = watchingSummary(state);
|
|
50196
50844
|
switch (outcome.kind) {
|
|
@@ -50207,7 +50855,7 @@ function reportFileOutcome(spinner, slug, state, outcome) {
|
|
|
50207
50855
|
spinner.update(`Watching ${slug} · ${summary}`);
|
|
50208
50856
|
return;
|
|
50209
50857
|
case "conflict":
|
|
50210
|
-
spinner.warn(
|
|
50858
|
+
spinner.warn(fileConflictMessage(outcome));
|
|
50211
50859
|
spinner.update(`Watching ${slug} · ${summary}`);
|
|
50212
50860
|
return;
|
|
50213
50861
|
case "over_cap":
|
|
@@ -50347,7 +50995,7 @@ var startCommand = defineCommand({
|
|
|
50347
50995
|
});
|
|
50348
50996
|
|
|
50349
50997
|
// src/commands/status.ts
|
|
50350
|
-
import { statSync as statSync20 } from "node:fs";
|
|
50998
|
+
import { existsSync as existsSync12, statSync as statSync20 } from "node:fs";
|
|
50351
50999
|
|
|
50352
51000
|
// src/lib/context/context-walls.ts
|
|
50353
51001
|
function prepare(raw) {
|
|
@@ -50648,14 +51296,69 @@ function assessContextWalls(workspaceSlug, deps = {}) {
|
|
|
50648
51296
|
return { walls, played, asOf, unreadable };
|
|
50649
51297
|
}
|
|
50650
51298
|
|
|
51299
|
+
// src/commands/status-holds.ts
|
|
51300
|
+
function filePullHoldLines(args) {
|
|
51301
|
+
const details = [];
|
|
51302
|
+
for (const hold of args.holds) {
|
|
51303
|
+
const line = holdLine(hold, args);
|
|
51304
|
+
if (line !== null)
|
|
51305
|
+
details.push(line);
|
|
51306
|
+
}
|
|
51307
|
+
const n = details.length;
|
|
51308
|
+
if (n === 0)
|
|
51309
|
+
return { headline: "No files held.", details };
|
|
51310
|
+
return {
|
|
51311
|
+
headline: `${n} file${n === 1 ? "" : "s"} held - this folder and Plinth differ. Each line says why and what clears it.`,
|
|
51312
|
+
details
|
|
51313
|
+
};
|
|
51314
|
+
}
|
|
51315
|
+
function holdLine(hold, args) {
|
|
51316
|
+
const name = `files/${hold.path}`;
|
|
51317
|
+
if (hold.kind === "kept_after_delete") {
|
|
51318
|
+
if (!args.fileExists(hold.path) || args.isTracked(hold.path))
|
|
51319
|
+
return null;
|
|
51320
|
+
return `${name}: deleted in Plinth, but your copy here had changes that were never uploaded, so it was kept. ` + `It goes back into Plinth as a new file the next time the daemon starts; delete it here to accept the deletion.`;
|
|
51321
|
+
}
|
|
51322
|
+
if (hold.kind === "stash_failed") {
|
|
51323
|
+
return `${name}: Plinth has a newer version, but your copy here could not be kept first, so it was not replaced. ` + `${keepRemedy(hold, args.conflictsDir)} It updates on the next sync after that.`;
|
|
51324
|
+
}
|
|
51325
|
+
if (hold.kind === "integrity_failed") {
|
|
51326
|
+
return `${name}: Plinth's copy did not match its checksum when it was downloaded, so it was not written. ` + `${retryPhrase(hold, args.now)}`;
|
|
51327
|
+
}
|
|
51328
|
+
return `${name}: held by a newer version of Plinth (${hold.kind}); update Plinth on this Mac to see why.`;
|
|
51329
|
+
}
|
|
51330
|
+
function keepRemedy(hold, conflictsDir) {
|
|
51331
|
+
const code = hold.code ? ` (${hold.code})` : "";
|
|
51332
|
+
switch (hold.reason) {
|
|
51333
|
+
case "symlink":
|
|
51334
|
+
return "A folder on the way to it is a link; replace the link with a real folder.";
|
|
51335
|
+
case "not_a_file":
|
|
51336
|
+
return "Something other than a file is at that path; move it aside.";
|
|
51337
|
+
case "unreadable":
|
|
51338
|
+
return `Your copy could not be read${code}; check its permissions.`;
|
|
51339
|
+
case "stash_io":
|
|
51340
|
+
return `A recovery copy could not be written to ${conflictsDir}${code}; free some disk space or check that folder's permissions.`;
|
|
51341
|
+
default:
|
|
51342
|
+
return `Your copy could not be kept (${hold.reason ?? "no reason recorded"}${code}); update Plinth on this Mac to see why.`;
|
|
51343
|
+
}
|
|
51344
|
+
}
|
|
51345
|
+
function retryPhrase(hold, now) {
|
|
51346
|
+
const due = hold.next_retry_at ? Date.parse(hold.next_retry_at) : Number.NaN;
|
|
51347
|
+
const wait = due - now.getTime();
|
|
51348
|
+
if (!Number.isFinite(wait) || wait <= 0)
|
|
51349
|
+
return "It is tried again on the next sync.";
|
|
51350
|
+
const minutes = Math.max(1, Math.round(wait / 60000));
|
|
51351
|
+
return `It is tried again in about ${minutes} minute${minutes === 1 ? "" : "s"}, or at once if the file changes in Plinth.`;
|
|
51352
|
+
}
|
|
51353
|
+
|
|
50651
51354
|
// src/lib/build-stamp.ts
|
|
50652
51355
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
50653
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync12, readFileSync as
|
|
50654
|
-
import { dirname as dirname14, join as
|
|
51356
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync12, readFileSync as readFileSync34, writeFileSync as writeFileSync9 } from "node:fs";
|
|
51357
|
+
import { dirname as dirname14, join as join20 } from "node:path";
|
|
50655
51358
|
import { fileURLToPath } from "node:url";
|
|
50656
51359
|
var BUILD_STAMP_FILENAME = "build-stamp.json";
|
|
50657
51360
|
function stampPath(distDir) {
|
|
50658
|
-
return
|
|
51361
|
+
return join20(distDir, BUILD_STAMP_FILENAME);
|
|
50659
51362
|
}
|
|
50660
51363
|
function readHead(repoDir) {
|
|
50661
51364
|
try {
|
|
@@ -50674,7 +51377,7 @@ function readHead(repoDir) {
|
|
|
50674
51377
|
}
|
|
50675
51378
|
function readBuildStamp(distDir) {
|
|
50676
51379
|
try {
|
|
50677
|
-
const parsed = JSON.parse(
|
|
51380
|
+
const parsed = JSON.parse(readFileSync34(stampPath(distDir), "utf8"));
|
|
50678
51381
|
if (parsed && typeof parsed.sha === "string" && parsed.sha.length > 0 && typeof parsed.dirty === "boolean" && typeof parsed.built_at === "string") {
|
|
50679
51382
|
return { sha: parsed.sha, dirty: parsed.dirty, built_at: parsed.built_at };
|
|
50680
51383
|
}
|
|
@@ -50686,7 +51389,7 @@ function readBuildStamp(distDir) {
|
|
|
50686
51389
|
function findRepoRoot(fromDir) {
|
|
50687
51390
|
let dir = fromDir;
|
|
50688
51391
|
for (;; ) {
|
|
50689
|
-
if (existsSync11(
|
|
51392
|
+
if (existsSync11(join20(dir, ".git")))
|
|
50690
51393
|
return dir;
|
|
50691
51394
|
const parent = dirname14(dir);
|
|
50692
51395
|
if (parent === dir)
|
|
@@ -50735,12 +51438,12 @@ function checkBuildStaleness(deps = {}) {
|
|
|
50735
51438
|
}
|
|
50736
51439
|
|
|
50737
51440
|
// src/lib/memory-links.ts
|
|
50738
|
-
import { readFileSync as
|
|
51441
|
+
import { readFileSync as readFileSync35 } from "node:fs";
|
|
50739
51442
|
import { relative as relative15, sep as sep9 } from "node:path";
|
|
50740
51443
|
var MEMORY_FILE_RE = /^(workspace-memory\.md|user-memory\.md|artists\/[^/]+\/memory\.md|memory\/[^/]+\.md|memory\/private\/[^/]+\.md)$/i;
|
|
50741
51444
|
function scanMemoryLinks(deps) {
|
|
50742
51445
|
const listFiles = deps.listFiles ?? ((r) => walkSubtreeFiles(r, isVcsInternal));
|
|
50743
|
-
const readFile = deps.readFile ?? ((p) =>
|
|
51446
|
+
const readFile = deps.readFile ?? ((p) => readFileSync35(p, "utf8"));
|
|
50744
51447
|
let root;
|
|
50745
51448
|
let absPaths;
|
|
50746
51449
|
try {
|
|
@@ -50784,7 +51487,7 @@ function scanMemoryLinks(deps) {
|
|
|
50784
51487
|
init_output();
|
|
50785
51488
|
|
|
50786
51489
|
// src/lib/pending-writes.ts
|
|
50787
|
-
import { readFileSync as
|
|
51490
|
+
import { readFileSync as readFileSync36, statSync as statSync19 } from "node:fs";
|
|
50788
51491
|
import { relative as relative16 } from "node:path";
|
|
50789
51492
|
var EMPTY3 = { writes: [], count: 0 };
|
|
50790
51493
|
function reportedElsewhere(state) {
|
|
@@ -50831,7 +51534,7 @@ function scanPendingWrites(args) {
|
|
|
50831
51534
|
continue;
|
|
50832
51535
|
let hash2;
|
|
50833
51536
|
try {
|
|
50834
|
-
hash2 = sha256Bytes(
|
|
51537
|
+
hash2 = sha256Bytes(readFileSync36(absolutePath));
|
|
50835
51538
|
} catch {
|
|
50836
51539
|
continue;
|
|
50837
51540
|
}
|
|
@@ -50862,7 +51565,7 @@ function scanPendingWrites(args) {
|
|
|
50862
51565
|
continue;
|
|
50863
51566
|
let content;
|
|
50864
51567
|
try {
|
|
50865
|
-
content =
|
|
51568
|
+
content = readFileSync36(absolutePath, "utf8");
|
|
50866
51569
|
} catch {
|
|
50867
51570
|
continue;
|
|
50868
51571
|
}
|
|
@@ -51305,6 +52008,23 @@ async function runStatus(args, deps = {}) {
|
|
|
51305
52008
|
}
|
|
51306
52009
|
out.info("");
|
|
51307
52010
|
}
|
|
52011
|
+
const pullHolds = filePullHoldLines({
|
|
52012
|
+
holds: listFilePullHolds(localState),
|
|
52013
|
+
conflictsDir: conflictsRoot(slug),
|
|
52014
|
+
now,
|
|
52015
|
+
fileExists: (rel) => {
|
|
52016
|
+
try {
|
|
52017
|
+
return existsSync12(filesSubtreePath(slug, rel));
|
|
52018
|
+
} catch {
|
|
52019
|
+
return true;
|
|
52020
|
+
}
|
|
52021
|
+
},
|
|
52022
|
+
isTracked: (rel) => getFileEntry(localState, rel) !== undefined
|
|
52023
|
+
});
|
|
52024
|
+
out.info(row("Held", pullHolds.headline));
|
|
52025
|
+
for (const line of pullHolds.details)
|
|
52026
|
+
out.info(cont(line));
|
|
52027
|
+
out.info("");
|
|
51308
52028
|
const rejected = listRejected(localState);
|
|
51309
52029
|
const freshness = Object.values(localState.memory_sources?.unwritten ?? {}).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
51310
52030
|
const localRejectedPaths = new Set(rejected.filter((r) => r.target === "file").map((r) => r.path));
|
|
@@ -51771,7 +52491,7 @@ var versionCommand = defineCommand({
|
|
|
51771
52491
|
});
|
|
51772
52492
|
|
|
51773
52493
|
// src/commands/voice-gate.ts
|
|
51774
|
-
import { readFileSync as
|
|
52494
|
+
import { readFileSync as readFileSync39 } from "node:fs";
|
|
51775
52495
|
|
|
51776
52496
|
// src/lib/voice-gate/api.ts
|
|
51777
52497
|
var VoiceGateSurfaces = [
|
|
@@ -51873,15 +52593,15 @@ async function callVoiceGate(req) {
|
|
|
51873
52593
|
}
|
|
51874
52594
|
|
|
51875
52595
|
// src/lib/voice-gate/attempts.ts
|
|
51876
|
-
import { createHash as
|
|
52596
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
51877
52597
|
import {
|
|
51878
52598
|
mkdirSync as mkdirSync13,
|
|
51879
|
-
readFileSync as
|
|
51880
|
-
renameSync as
|
|
51881
|
-
unlinkSync as
|
|
51882
|
-
writeFileSync as
|
|
52599
|
+
readFileSync as readFileSync37,
|
|
52600
|
+
renameSync as renameSync10,
|
|
52601
|
+
unlinkSync as unlinkSync12,
|
|
52602
|
+
writeFileSync as writeFileSync10
|
|
51883
52603
|
} from "node:fs";
|
|
51884
|
-
import { join as
|
|
52604
|
+
import { join as join21 } from "node:path";
|
|
51885
52605
|
var MAX_REGENS = 2;
|
|
51886
52606
|
var ENTRY_TTL_MS = 24 * 60 * 60 * 1000;
|
|
51887
52607
|
var AttemptEntrySchema = exports_external.object({
|
|
@@ -51894,20 +52614,20 @@ var AttemptsFileSchema = exports_external.object({
|
|
|
51894
52614
|
entries: exports_external.record(exports_external.string(), AttemptEntrySchema)
|
|
51895
52615
|
});
|
|
51896
52616
|
function attemptsDir(workspaceSlug) {
|
|
51897
|
-
return
|
|
52617
|
+
return join21(plinthHome(), "_voice_gate", workspaceSlug);
|
|
51898
52618
|
}
|
|
51899
52619
|
function attemptsPath(workspaceSlug) {
|
|
51900
|
-
return
|
|
52620
|
+
return join21(attemptsDir(workspaceSlug), "attempts.json");
|
|
51901
52621
|
}
|
|
51902
52622
|
function attemptKey(identity) {
|
|
51903
|
-
return
|
|
52623
|
+
return createHash8("sha256").update(identity, "utf8").digest("hex").slice(0, 32);
|
|
51904
52624
|
}
|
|
51905
52625
|
function emptyFile() {
|
|
51906
52626
|
return { version: 1, entries: {} };
|
|
51907
52627
|
}
|
|
51908
52628
|
function load2(workspaceSlug) {
|
|
51909
52629
|
try {
|
|
51910
|
-
const raw =
|
|
52630
|
+
const raw = readFileSync37(attemptsPath(workspaceSlug), "utf8");
|
|
51911
52631
|
const parsed = AttemptsFileSchema.safeParse(JSON.parse(raw));
|
|
51912
52632
|
return parsed.success ? parsed.data : emptyFile();
|
|
51913
52633
|
} catch {
|
|
@@ -51928,13 +52648,13 @@ function save(workspaceSlug, file2) {
|
|
|
51928
52648
|
const target = attemptsPath(workspaceSlug);
|
|
51929
52649
|
const tmp = `${target}.${process.pid}.${tmpCounter2++}.tmp`;
|
|
51930
52650
|
mkdirSync13(dir, { recursive: true, mode: 448 });
|
|
51931
|
-
|
|
52651
|
+
writeFileSync10(tmp, `${JSON.stringify(file2, null, 2)}
|
|
51932
52652
|
`, { mode: 384 });
|
|
51933
52653
|
try {
|
|
51934
|
-
|
|
52654
|
+
renameSync10(tmp, target);
|
|
51935
52655
|
} catch (err) {
|
|
51936
52656
|
try {
|
|
51937
|
-
|
|
52657
|
+
unlinkSync12(tmp);
|
|
51938
52658
|
} catch {}
|
|
51939
52659
|
throw err;
|
|
51940
52660
|
}
|
|
@@ -52070,7 +52790,7 @@ function denyUngated(detail) {
|
|
|
52070
52790
|
}
|
|
52071
52791
|
|
|
52072
52792
|
// src/lib/voice-gate/payload.ts
|
|
52073
|
-
import { readFileSync as
|
|
52793
|
+
import { readFileSync as readFileSync38, statSync as statSync21 } from "node:fs";
|
|
52074
52794
|
import { homedir as homedir2 } from "node:os";
|
|
52075
52795
|
import { isAbsolute as isAbsolute4, resolve as resolve7 } from "node:path";
|
|
52076
52796
|
var MAX_BODY_BYTES = 256 * 1024;
|
|
@@ -52231,7 +52951,7 @@ function defaultReadBodyFile(path5) {
|
|
|
52231
52951
|
if (stat4.size > MAX_BODY_BYTES) {
|
|
52232
52952
|
throw new Error(`draft body is ${stat4.size} bytes, over the ${MAX_BODY_BYTES}-byte gate limit`);
|
|
52233
52953
|
}
|
|
52234
|
-
return
|
|
52954
|
+
return readFileSync38(path5, "utf8");
|
|
52235
52955
|
}
|
|
52236
52956
|
function parseRecipients(...raw) {
|
|
52237
52957
|
const all = raw.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0);
|
|
@@ -52591,7 +53311,7 @@ async function runVoiceGateDirect(args, deps = {}) {
|
|
|
52591
53311
|
let text;
|
|
52592
53312
|
if (args.file) {
|
|
52593
53313
|
try {
|
|
52594
|
-
text =
|
|
53314
|
+
text = readFileSync39(args.file, "utf8");
|
|
52595
53315
|
} catch (err) {
|
|
52596
53316
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
52597
53317
|
out.error(`Could not read ${args.file}: ${msg}`);
|