@plinth-music/cli 0.20.1 → 0.21.1
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 +74 -0
- package/README.md +2 -2
- package/dist/build-stamp.json +2 -2
- package/dist/cli.js +1124 -270
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7584,7 +7584,7 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
|
|
|
7584
7584
|
// package.json
|
|
7585
7585
|
var package_default = {
|
|
7586
7586
|
name: "@plinth-music/cli",
|
|
7587
|
-
version: "0.
|
|
7587
|
+
version: "0.21.1",
|
|
7588
7588
|
description: "Plinth capstone sync daemon and CLI",
|
|
7589
7589
|
type: "module",
|
|
7590
7590
|
private: false,
|
|
@@ -26125,6 +26125,27 @@ function isLaterVersion(a, b) {
|
|
|
26125
26125
|
return false;
|
|
26126
26126
|
return ta > tb;
|
|
26127
26127
|
}
|
|
26128
|
+
function committedEntryIfNewer(workspaceSlug, entityType, entry) {
|
|
26129
|
+
let raw;
|
|
26130
|
+
try {
|
|
26131
|
+
raw = readFileSync7(stateFilePath(workspaceSlug), "utf8");
|
|
26132
|
+
} catch {
|
|
26133
|
+
return;
|
|
26134
|
+
}
|
|
26135
|
+
let committed;
|
|
26136
|
+
try {
|
|
26137
|
+
const parsed = JSON.parse(raw);
|
|
26138
|
+
committed = parsed.entries?.[entityType]?.[entry.id];
|
|
26139
|
+
} catch {
|
|
26140
|
+
return;
|
|
26141
|
+
}
|
|
26142
|
+
if (committed === undefined)
|
|
26143
|
+
return;
|
|
26144
|
+
const valid = StateEntrySchema.safeParse(committed);
|
|
26145
|
+
if (!valid.success)
|
|
26146
|
+
return;
|
|
26147
|
+
return isLaterVersion(valid.data, entry) ? valid.data : undefined;
|
|
26148
|
+
}
|
|
26128
26149
|
function mergeKey(fresh, before, after) {
|
|
26129
26150
|
if (before === after)
|
|
26130
26151
|
return { value: fresh, overruled: false };
|
|
@@ -26200,8 +26221,7 @@ function mergeAnchor(fresh, before, after) {
|
|
|
26200
26221
|
return fresh;
|
|
26201
26222
|
return after;
|
|
26202
26223
|
}
|
|
26203
|
-
function
|
|
26204
|
-
const fresh = loadState(after.workspace_slug);
|
|
26224
|
+
function mergeWorkspaceState(fresh, before, after, wantCorrected = true) {
|
|
26205
26225
|
const entityTypes = new Set([
|
|
26206
26226
|
...Object.keys(fresh.entries),
|
|
26207
26227
|
...Object.keys(before.entries),
|
|
@@ -26242,10 +26262,11 @@ function commitState(before, after) {
|
|
|
26242
26262
|
last_trim_at: mergeScalar(fresh.last_trim_at, before.last_trim_at, after.last_trim_at),
|
|
26243
26263
|
memory_facts: mergeScalar(fresh.memory_facts, before.memory_facts, after.memory_facts)
|
|
26244
26264
|
};
|
|
26245
|
-
|
|
26265
|
+
if (!wantCorrected)
|
|
26266
|
+
return { merged, corrected: after };
|
|
26246
26267
|
const singletonOverruled = rules.overruled || memory.overruled || userMemory.overruled || userVoice.overruled;
|
|
26247
26268
|
if (overruledEntries.length === 0 && files.overruled.length === 0 && !singletonOverruled) {
|
|
26248
|
-
return after;
|
|
26269
|
+
return { merged, corrected: after };
|
|
26249
26270
|
}
|
|
26250
26271
|
let corrected = after;
|
|
26251
26272
|
if (rules.overruled)
|
|
@@ -26264,8 +26285,20 @@ function commitState(before, after) {
|
|
|
26264
26285
|
const committed = merged.files?.[path];
|
|
26265
26286
|
corrected = committed ? upsertFileEntry(corrected, committed).state : removeFileEntry(corrected, path).state;
|
|
26266
26287
|
}
|
|
26288
|
+
return { merged, corrected };
|
|
26289
|
+
}
|
|
26290
|
+
function commitState(before, after) {
|
|
26291
|
+
const { merged, corrected } = mergeWorkspaceState(loadState(after.workspace_slug), before, after);
|
|
26292
|
+
writeState(merged);
|
|
26267
26293
|
return corrected;
|
|
26268
26294
|
}
|
|
26295
|
+
function rebaseState(current, base2, next) {
|
|
26296
|
+
if (next === base2)
|
|
26297
|
+
return current;
|
|
26298
|
+
if (current === base2)
|
|
26299
|
+
return next;
|
|
26300
|
+
return mergeWorkspaceState(current, base2, next, false).merged;
|
|
26301
|
+
}
|
|
26269
26302
|
function upsertEntry(state, entityType, entry) {
|
|
26270
26303
|
const bucket = state.entries[entityType] ?? {};
|
|
26271
26304
|
const existing = bucket[entry.id];
|
|
@@ -30505,6 +30538,560 @@ function ensureGitignore(workspaceSlug) {
|
|
|
30505
30538
|
function backupStamp2(d) {
|
|
30506
30539
|
return d.toISOString().replace(/[-:.]/g, "").replace("Z", "");
|
|
30507
30540
|
}
|
|
30541
|
+
// src/lib/context/observation.ts
|
|
30542
|
+
import {
|
|
30543
|
+
closeSync as closeSync2,
|
|
30544
|
+
constants as FS,
|
|
30545
|
+
fstatSync,
|
|
30546
|
+
lstatSync as lstatSync3,
|
|
30547
|
+
openSync as openSync2,
|
|
30548
|
+
readSync,
|
|
30549
|
+
realpathSync as realpathSync2
|
|
30550
|
+
} from "node:fs";
|
|
30551
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
30552
|
+
import { basename as basename2, dirname as dirname6, join as join8, resolve as resolve3, sep as sep4 } from "node:path";
|
|
30553
|
+
var OBSERVATION_MAX_BYTES = 64 * 1024 * 1024;
|
|
30554
|
+
var OBSERVATION_MAX_LINES = 200000;
|
|
30555
|
+
var READ_CHUNK = 1024 * 1024;
|
|
30556
|
+
var PROVENANCE_MAX = 256;
|
|
30557
|
+
var UNSAFE_PROVENANCE = /[\p{Cc}\p{Cf}]/u;
|
|
30558
|
+
function safeProvenance(v) {
|
|
30559
|
+
if (typeof v !== "string" || v === "")
|
|
30560
|
+
return null;
|
|
30561
|
+
if (v.length > PROVENANCE_MAX)
|
|
30562
|
+
return null;
|
|
30563
|
+
if (UNSAFE_PROVENANCE.test(v))
|
|
30564
|
+
return null;
|
|
30565
|
+
return v;
|
|
30566
|
+
}
|
|
30567
|
+
var ATTACHMENT_LABELS = new Set([
|
|
30568
|
+
"skill_listing",
|
|
30569
|
+
"deferred_tools_delta",
|
|
30570
|
+
"mcp_instructions_delta",
|
|
30571
|
+
"command_permissions",
|
|
30572
|
+
"agent_listing_delta",
|
|
30573
|
+
"total_tokens_reminder",
|
|
30574
|
+
"auto_mode",
|
|
30575
|
+
"remote_session_change",
|
|
30576
|
+
"file_history_snapshot"
|
|
30577
|
+
]);
|
|
30578
|
+
function isFailure(x) {
|
|
30579
|
+
return x.status !== undefined;
|
|
30580
|
+
}
|
|
30581
|
+
function openSelected(selectedPath, hooks = {}) {
|
|
30582
|
+
const resolved = resolve3(selectedPath);
|
|
30583
|
+
let canonicalPath;
|
|
30584
|
+
try {
|
|
30585
|
+
canonicalPath = realpathSync2(resolved);
|
|
30586
|
+
} catch (err) {
|
|
30587
|
+
const code = err?.code;
|
|
30588
|
+
if (code === "ENOENT") {
|
|
30589
|
+
return { status: "absent", refusalReason: null, canonicalPath: null, indirection: false };
|
|
30590
|
+
}
|
|
30591
|
+
if (code === "ELOOP") {
|
|
30592
|
+
return {
|
|
30593
|
+
status: "refused",
|
|
30594
|
+
refusalReason: "path-unresolvable",
|
|
30595
|
+
canonicalPath: null,
|
|
30596
|
+
indirection: true
|
|
30597
|
+
};
|
|
30598
|
+
}
|
|
30599
|
+
return { status: "unreadable", refusalReason: null, canonicalPath: null, indirection: false };
|
|
30600
|
+
}
|
|
30601
|
+
const indirection = canonicalPath !== resolved;
|
|
30602
|
+
let before;
|
|
30603
|
+
try {
|
|
30604
|
+
before = lstatSync3(canonicalPath);
|
|
30605
|
+
} catch (err) {
|
|
30606
|
+
const code = err?.code;
|
|
30607
|
+
if (code === "ENOENT") {
|
|
30608
|
+
return { status: "absent", refusalReason: null, canonicalPath, indirection };
|
|
30609
|
+
}
|
|
30610
|
+
return { status: "unreadable", refusalReason: null, canonicalPath, indirection };
|
|
30611
|
+
}
|
|
30612
|
+
hooks.afterIdentityCheck?.();
|
|
30613
|
+
let fd;
|
|
30614
|
+
try {
|
|
30615
|
+
fd = openSync2(canonicalPath, FS.O_RDONLY | FS.O_NOFOLLOW | FS.O_NONBLOCK);
|
|
30616
|
+
} catch (err) {
|
|
30617
|
+
const code = err?.code;
|
|
30618
|
+
if (code === "ENOENT") {
|
|
30619
|
+
return { status: "absent", refusalReason: null, canonicalPath, indirection };
|
|
30620
|
+
}
|
|
30621
|
+
if (code === "ELOOP" || code === "EMLINK") {
|
|
30622
|
+
return { status: "refused", refusalReason: "target-changed", canonicalPath, indirection };
|
|
30623
|
+
}
|
|
30624
|
+
return { status: "unreadable", refusalReason: null, canonicalPath, indirection };
|
|
30625
|
+
}
|
|
30626
|
+
let after;
|
|
30627
|
+
try {
|
|
30628
|
+
after = fstatSync(fd);
|
|
30629
|
+
} catch {
|
|
30630
|
+
closeSync2(fd);
|
|
30631
|
+
return { status: "unreadable", refusalReason: null, canonicalPath, indirection };
|
|
30632
|
+
}
|
|
30633
|
+
if (!after.isFile()) {
|
|
30634
|
+
closeSync2(fd);
|
|
30635
|
+
return { status: "refused", refusalReason: "not-regular-file", canonicalPath, indirection };
|
|
30636
|
+
}
|
|
30637
|
+
if (after.dev !== before.dev || after.ino !== before.ino) {
|
|
30638
|
+
closeSync2(fd);
|
|
30639
|
+
return { status: "refused", refusalReason: "target-changed", canonicalPath, indirection };
|
|
30640
|
+
}
|
|
30641
|
+
return { fd, canonicalPath, indirection };
|
|
30642
|
+
}
|
|
30643
|
+
function readBounded(fd, maxBytes) {
|
|
30644
|
+
const hash2 = createHash2("sha256");
|
|
30645
|
+
const chunks = [];
|
|
30646
|
+
const buf = Buffer.allocUnsafe(READ_CHUNK);
|
|
30647
|
+
let bytesRead = 0;
|
|
30648
|
+
let truncated = false;
|
|
30649
|
+
for (;; ) {
|
|
30650
|
+
const want = Math.min(READ_CHUNK, maxBytes - bytesRead);
|
|
30651
|
+
if (want <= 0) {
|
|
30652
|
+
const probe2 = readSync(fd, buf, 0, 1, bytesRead);
|
|
30653
|
+
truncated = probe2 > 0;
|
|
30654
|
+
break;
|
|
30655
|
+
}
|
|
30656
|
+
const n = readSync(fd, buf, 0, want, bytesRead);
|
|
30657
|
+
if (n <= 0)
|
|
30658
|
+
break;
|
|
30659
|
+
const slice = Buffer.from(buf.subarray(0, n));
|
|
30660
|
+
hash2.update(slice);
|
|
30661
|
+
chunks.push(slice);
|
|
30662
|
+
bytesRead += n;
|
|
30663
|
+
}
|
|
30664
|
+
return {
|
|
30665
|
+
text: Buffer.concat(chunks).toString("utf8"),
|
|
30666
|
+
bytesRead,
|
|
30667
|
+
truncated,
|
|
30668
|
+
sha256: hash2.digest("hex")
|
|
30669
|
+
};
|
|
30670
|
+
}
|
|
30671
|
+
var USAGE_FIELDS = [
|
|
30672
|
+
["inputTokens", ["input_tokens"]],
|
|
30673
|
+
["cacheReadTokens", ["cache_read_input_tokens"]],
|
|
30674
|
+
["cacheCreationTokens", ["cache_creation_input_tokens"]],
|
|
30675
|
+
["outputTokens", ["output_tokens"]],
|
|
30676
|
+
["cacheCreation1hTokens", ["cache_creation", "ephemeral_1h_input_tokens"]],
|
|
30677
|
+
["cacheCreation5mTokens", ["cache_creation", "ephemeral_5m_input_tokens"]]
|
|
30678
|
+
];
|
|
30679
|
+
function dig(obj, path) {
|
|
30680
|
+
let cur = obj;
|
|
30681
|
+
for (const key of path) {
|
|
30682
|
+
if (cur === null || typeof cur !== "object")
|
|
30683
|
+
return;
|
|
30684
|
+
cur = cur[key];
|
|
30685
|
+
}
|
|
30686
|
+
return cur;
|
|
30687
|
+
}
|
|
30688
|
+
|
|
30689
|
+
class UsageAccumulator {
|
|
30690
|
+
byRequest = new Map;
|
|
30691
|
+
sums = new Map;
|
|
30692
|
+
present = new Map;
|
|
30693
|
+
requests = 0;
|
|
30694
|
+
countedRecords = 0;
|
|
30695
|
+
duplicateRecords = 0;
|
|
30696
|
+
conflictingDuplicates = 0;
|
|
30697
|
+
untaggedRecords = 0;
|
|
30698
|
+
recordsWithoutUsage = 0;
|
|
30699
|
+
unresolvableRecords = 0;
|
|
30700
|
+
add(record2) {
|
|
30701
|
+
if (record2["type"] !== "assistant")
|
|
30702
|
+
return;
|
|
30703
|
+
const usage = dig(record2, ["message", "usage"]);
|
|
30704
|
+
const hasUsage = usage !== null && typeof usage === "object";
|
|
30705
|
+
if (hasUsage)
|
|
30706
|
+
this.countedRecords += 1;
|
|
30707
|
+
else
|
|
30708
|
+
this.recordsWithoutUsage += 1;
|
|
30709
|
+
const requestId = record2["requestId"];
|
|
30710
|
+
if (typeof requestId !== "string" || requestId === "") {
|
|
30711
|
+
if (hasUsage)
|
|
30712
|
+
this.untaggedRecords += 1;
|
|
30713
|
+
else
|
|
30714
|
+
this.unresolvableRecords += 1;
|
|
30715
|
+
return;
|
|
30716
|
+
}
|
|
30717
|
+
if (!this.byRequest.has(requestId)) {
|
|
30718
|
+
this.byRequest.set(requestId, null);
|
|
30719
|
+
this.requests += 1;
|
|
30720
|
+
}
|
|
30721
|
+
if (!hasUsage)
|
|
30722
|
+
return;
|
|
30723
|
+
const fingerprint = JSON.stringify(USAGE_FIELDS.map(([, path]) => dig(usage, path) ?? null));
|
|
30724
|
+
const prior = this.byRequest.get(requestId) ?? null;
|
|
30725
|
+
if (prior !== null) {
|
|
30726
|
+
if (prior === fingerprint)
|
|
30727
|
+
this.duplicateRecords += 1;
|
|
30728
|
+
else
|
|
30729
|
+
this.conflictingDuplicates += 1;
|
|
30730
|
+
return;
|
|
30731
|
+
}
|
|
30732
|
+
this.byRequest.set(requestId, fingerprint);
|
|
30733
|
+
for (const [name, path] of USAGE_FIELDS) {
|
|
30734
|
+
const raw = dig(usage, path);
|
|
30735
|
+
if (typeof raw !== "number" || !Number.isFinite(raw))
|
|
30736
|
+
continue;
|
|
30737
|
+
this.present.set(name, (this.present.get(name) ?? 0) + 1);
|
|
30738
|
+
this.sums.set(name, (this.sums.get(name) ?? 0) + raw);
|
|
30739
|
+
}
|
|
30740
|
+
}
|
|
30741
|
+
unresolved() {
|
|
30742
|
+
let n = 0;
|
|
30743
|
+
for (const fp of this.byRequest.values())
|
|
30744
|
+
if (fp === null)
|
|
30745
|
+
n += 1;
|
|
30746
|
+
return n;
|
|
30747
|
+
}
|
|
30748
|
+
finish() {
|
|
30749
|
+
if (this.countedRecords === 0 && this.recordsWithoutUsage === 0)
|
|
30750
|
+
return null;
|
|
30751
|
+
const unresolvedRequests = this.unresolved();
|
|
30752
|
+
const incompleteFields = [];
|
|
30753
|
+
const value = (name) => {
|
|
30754
|
+
const present2 = this.present.get(name) ?? 0;
|
|
30755
|
+
if (present2 > 0 && present2 === this.requests)
|
|
30756
|
+
return this.sums.get(name) ?? 0;
|
|
30757
|
+
if (present2 > 0)
|
|
30758
|
+
incompleteFields.push(name);
|
|
30759
|
+
return null;
|
|
30760
|
+
};
|
|
30761
|
+
const fields = Object.fromEntries(USAGE_FIELDS.map(([name]) => [name, value(name)]));
|
|
30762
|
+
const anyFullyCovered = USAGE_FIELDS.some(([name]) => (this.present.get(name) ?? 0) > 0 && (this.present.get(name) ?? 0) === this.requests);
|
|
30763
|
+
return {
|
|
30764
|
+
requests: this.requests,
|
|
30765
|
+
countedRecords: this.countedRecords,
|
|
30766
|
+
duplicateRecords: this.duplicateRecords,
|
|
30767
|
+
conflictingDuplicates: this.conflictingDuplicates,
|
|
30768
|
+
untaggedRecords: this.untaggedRecords,
|
|
30769
|
+
unresolvedRequests,
|
|
30770
|
+
recordsWithoutUsage: this.recordsWithoutUsage,
|
|
30771
|
+
unresolvableRecords: this.unresolvableRecords,
|
|
30772
|
+
...fields,
|
|
30773
|
+
incompleteFields,
|
|
30774
|
+
incomplete: incompleteFields.length > 0 || this.conflictingDuplicates > 0 || this.untaggedRecords > 0 || unresolvedRequests > 0 || this.unresolvableRecords > 0 || this.requests > 0 && !anyFullyCovered
|
|
30775
|
+
};
|
|
30776
|
+
}
|
|
30777
|
+
}
|
|
30778
|
+
function hasToolResult(content) {
|
|
30779
|
+
if (!Array.isArray(content))
|
|
30780
|
+
return false;
|
|
30781
|
+
return content.some((b) => b !== null && typeof b === "object" && b["type"] === "tool_result");
|
|
30782
|
+
}
|
|
30783
|
+
function contentText(content) {
|
|
30784
|
+
if (typeof content === "string")
|
|
30785
|
+
return content;
|
|
30786
|
+
if (!Array.isArray(content))
|
|
30787
|
+
return "";
|
|
30788
|
+
return content.map((b) => b !== null && typeof b === "object" && typeof b["text"] === "string" ? b["text"] : "").join("");
|
|
30789
|
+
}
|
|
30790
|
+
function payloadBytes(payload) {
|
|
30791
|
+
if (payload === undefined)
|
|
30792
|
+
return 0;
|
|
30793
|
+
if (typeof payload === "string")
|
|
30794
|
+
return Buffer.byteLength(payload, "utf8");
|
|
30795
|
+
try {
|
|
30796
|
+
return Buffer.byteLength(JSON.stringify(payload) ?? "", "utf8");
|
|
30797
|
+
} catch {
|
|
30798
|
+
return 0;
|
|
30799
|
+
}
|
|
30800
|
+
}
|
|
30801
|
+
|
|
30802
|
+
class DeliveryAccumulator {
|
|
30803
|
+
rows = new Map;
|
|
30804
|
+
pendingCommand = false;
|
|
30805
|
+
bump(kind, label, bytes) {
|
|
30806
|
+
const key = `${kind} ${label}`;
|
|
30807
|
+
const row = this.rows.get(key) ?? { kind, label, occurrences: 0, recordedBytes: 0 };
|
|
30808
|
+
row.occurrences += 1;
|
|
30809
|
+
row.recordedBytes += bytes;
|
|
30810
|
+
this.rows.set(key, row);
|
|
30811
|
+
}
|
|
30812
|
+
add(record2) {
|
|
30813
|
+
const wasPending = this.pendingCommand;
|
|
30814
|
+
this.pendingCommand = false;
|
|
30815
|
+
const type = record2["type"];
|
|
30816
|
+
if (type === "attachment") {
|
|
30817
|
+
const att = record2["attachment"];
|
|
30818
|
+
const rawLabel = dig(att, ["type"]);
|
|
30819
|
+
const label = typeof rawLabel === "string" && ATTACHMENT_LABELS.has(rawLabel) ? rawLabel : "other";
|
|
30820
|
+
this.bump("harness-listing", label, payloadBytes(att));
|
|
30821
|
+
return;
|
|
30822
|
+
}
|
|
30823
|
+
if (type !== "user")
|
|
30824
|
+
return;
|
|
30825
|
+
const content = dig(record2, ["message", "content"]);
|
|
30826
|
+
const bytes = payloadBytes(content);
|
|
30827
|
+
const text = contentText(content);
|
|
30828
|
+
if (record2["isMeta"] === true) {
|
|
30829
|
+
if (wasPending)
|
|
30830
|
+
this.bump("command-body", "command-body", bytes);
|
|
30831
|
+
else
|
|
30832
|
+
this.bump("meta-injection", "meta-injection", bytes);
|
|
30833
|
+
return;
|
|
30834
|
+
}
|
|
30835
|
+
if (hasToolResult(content)) {
|
|
30836
|
+
this.bump("tool-result", "tool-result", bytes);
|
|
30837
|
+
return;
|
|
30838
|
+
}
|
|
30839
|
+
const opening = text.trimStart();
|
|
30840
|
+
this.pendingCommand = opening.startsWith("<command-message>") || opening.startsWith("<command-name>");
|
|
30841
|
+
this.bump("user-turn", "user-turn", bytes);
|
|
30842
|
+
}
|
|
30843
|
+
finish() {
|
|
30844
|
+
return [...this.rows.values()].sort((a, b) => b.recordedBytes - a.recordedBytes || a.label.localeCompare(b.label));
|
|
30845
|
+
}
|
|
30846
|
+
}
|
|
30847
|
+
|
|
30848
|
+
class BindingAccumulator {
|
|
30849
|
+
sessionIds = new Set;
|
|
30850
|
+
versions = new Set;
|
|
30851
|
+
cwds = new Set;
|
|
30852
|
+
branches = new Set;
|
|
30853
|
+
first = null;
|
|
30854
|
+
last = null;
|
|
30855
|
+
rejected = 0;
|
|
30856
|
+
add(record2) {
|
|
30857
|
+
const str2 = (k) => {
|
|
30858
|
+
const raw = record2[k];
|
|
30859
|
+
const safe = safeProvenance(raw);
|
|
30860
|
+
if (safe === null && typeof raw === "string" && raw !== "")
|
|
30861
|
+
this.rejected += 1;
|
|
30862
|
+
return safe;
|
|
30863
|
+
};
|
|
30864
|
+
const sid = str2("sessionId");
|
|
30865
|
+
if (sid !== null)
|
|
30866
|
+
this.sessionIds.add(sid);
|
|
30867
|
+
const ver = str2("version");
|
|
30868
|
+
if (ver !== null)
|
|
30869
|
+
this.versions.add(ver);
|
|
30870
|
+
const cwd = str2("cwd");
|
|
30871
|
+
if (cwd !== null)
|
|
30872
|
+
this.cwds.add(cwd);
|
|
30873
|
+
const branch = str2("gitBranch");
|
|
30874
|
+
if (branch !== null)
|
|
30875
|
+
this.branches.add(branch);
|
|
30876
|
+
const ts = str2("timestamp");
|
|
30877
|
+
if (ts !== null) {
|
|
30878
|
+
if (this.first === null || ts < this.first)
|
|
30879
|
+
this.first = ts;
|
|
30880
|
+
if (this.last === null || ts > this.last)
|
|
30881
|
+
this.last = ts;
|
|
30882
|
+
}
|
|
30883
|
+
}
|
|
30884
|
+
finish(workspaceMirrorDir) {
|
|
30885
|
+
const only = (s) => s.size === 1 ? [...s][0] : null;
|
|
30886
|
+
const cwd = only(this.cwds);
|
|
30887
|
+
const consistent = this.sessionIds.size === 1 && this.versions.size === 1 && this.cwds.size === 1;
|
|
30888
|
+
return {
|
|
30889
|
+
sessionId: only(this.sessionIds),
|
|
30890
|
+
runtimeVersion: only(this.versions),
|
|
30891
|
+
cwd,
|
|
30892
|
+
gitBranch: only(this.branches),
|
|
30893
|
+
firstTimestamp: this.first,
|
|
30894
|
+
lastTimestamp: this.last,
|
|
30895
|
+
distinctSessionIds: this.sessionIds.size,
|
|
30896
|
+
consistent,
|
|
30897
|
+
scope: deriveScope(cwd, workspaceMirrorDir),
|
|
30898
|
+
rejectedFields: this.rejected
|
|
30899
|
+
};
|
|
30900
|
+
}
|
|
30901
|
+
}
|
|
30902
|
+
function canonicalise(p) {
|
|
30903
|
+
const full = resolve3(p);
|
|
30904
|
+
let cur = full;
|
|
30905
|
+
const tail = [];
|
|
30906
|
+
for (;; ) {
|
|
30907
|
+
try {
|
|
30908
|
+
const real = realpathSync2(cur);
|
|
30909
|
+
return tail.length === 0 ? real : join8(real, ...tail.slice().reverse());
|
|
30910
|
+
} catch {
|
|
30911
|
+
const parent = dirname6(cur);
|
|
30912
|
+
if (parent === cur)
|
|
30913
|
+
return full;
|
|
30914
|
+
tail.push(basename2(cur));
|
|
30915
|
+
cur = parent;
|
|
30916
|
+
}
|
|
30917
|
+
}
|
|
30918
|
+
}
|
|
30919
|
+
function deriveScope(cwd, workspaceMirrorDir) {
|
|
30920
|
+
if (cwd === null || workspaceMirrorDir === undefined)
|
|
30921
|
+
return "unknown";
|
|
30922
|
+
const ws = canonicalise(workspaceMirrorDir);
|
|
30923
|
+
const here = canonicalise(cwd);
|
|
30924
|
+
if (here === ws)
|
|
30925
|
+
return "root";
|
|
30926
|
+
if (here.startsWith(ws.endsWith(sep4) ? ws : ws + sep4))
|
|
30927
|
+
return "below-root";
|
|
30928
|
+
return "outside";
|
|
30929
|
+
}
|
|
30930
|
+
function limitsFor(obs) {
|
|
30931
|
+
const limits = [
|
|
30932
|
+
{
|
|
30933
|
+
id: "observation-not-attributable",
|
|
30934
|
+
what: "which source contributed which tokens is not recoverable from this artefact: the standing context arrives as one aggregate in the first request's cache fields, with no per-source breakdown. The remainder is unattributable, not zero",
|
|
30935
|
+
owner: "not exposed by the measured artefact, runtime and this adapter; further effective-loading work stays with S2/E15 and the packaged half"
|
|
30936
|
+
},
|
|
30937
|
+
{
|
|
30938
|
+
id: "observation-interval",
|
|
30939
|
+
what: "this covers only the interval actually read from the selected file, identified by its byte boundary and digest. A live session keeps growing, so the same path measured later is a different interval",
|
|
30940
|
+
owner: "the reader who selected the file; re-run to extend it"
|
|
30941
|
+
},
|
|
30942
|
+
{
|
|
30943
|
+
id: "observation-inventory-not-contemporaneous",
|
|
30944
|
+
what: "the source inventory above is what is on disk NOW. It is not evidence that those files existed, or had those sizes, during the observed interval",
|
|
30945
|
+
owner: "E15, which compares arms rather than inventories"
|
|
30946
|
+
},
|
|
30947
|
+
{
|
|
30948
|
+
id: "observation-compaction",
|
|
30949
|
+
what: "resume, clear and compaction are not detected and were not demonstrated in any measured sample, so a compacted session may be reported as one continuous interval",
|
|
30950
|
+
owner: "a later S2/E15 lane with a purpose-built compacted session"
|
|
30951
|
+
},
|
|
30952
|
+
{
|
|
30953
|
+
id: "observation-delivery-cause",
|
|
30954
|
+
what: "a command body is labelled only when an invocation marker precedes it; an uncorrelated meta-injection's cause is not established. And a recorded delivery proves delivery happened, never that a body is not loaded by some other route",
|
|
30955
|
+
owner: "a later S2 lane, if per-route delivery proof is wanted"
|
|
30956
|
+
}
|
|
30957
|
+
];
|
|
30958
|
+
if (obs.binding.rejectedFields > 0) {
|
|
30959
|
+
limits.push({
|
|
30960
|
+
id: "observation-provenance-refused",
|
|
30961
|
+
what: "one or more session/runtime/directory values in the file carried control characters or exceeded the length cap and were refused rather than printed, so they read as unknown above",
|
|
30962
|
+
owner: "the reader who selected the file"
|
|
30963
|
+
});
|
|
30964
|
+
}
|
|
30965
|
+
if (obs.indirection) {
|
|
30966
|
+
limits.push({
|
|
30967
|
+
id: "observation-path-indirection",
|
|
30968
|
+
what: "the selected path resolved through a link; the canonical path above is what was read",
|
|
30969
|
+
owner: "the reader who selected the file"
|
|
30970
|
+
});
|
|
30971
|
+
}
|
|
30972
|
+
if (!obs.binding.consistent) {
|
|
30973
|
+
limits.push({
|
|
30974
|
+
id: "observation-binding-inconsistent",
|
|
30975
|
+
what: "session id, runtime version or working directory is missing or varies across records, so this observation is NOT attributed to the workspace inventory and is reported separately",
|
|
30976
|
+
owner: "select a single-session transcript to attribute it"
|
|
30977
|
+
});
|
|
30978
|
+
}
|
|
30979
|
+
if (obs.binding.scope === "below-root") {
|
|
30980
|
+
limits.push({
|
|
30981
|
+
id: "observation-scope-unsupported",
|
|
30982
|
+
what: "the observed session ran below the workspace root, and the inventory's scoped-load class is not resolved per session, so its scoped sources are not matched to this observation",
|
|
30983
|
+
owner: "a later S2 lane, if per-scope resolution is wanted"
|
|
30984
|
+
});
|
|
30985
|
+
}
|
|
30986
|
+
return limits;
|
|
30987
|
+
}
|
|
30988
|
+
function emptyBinding(workspaceMirrorDir) {
|
|
30989
|
+
return new BindingAccumulator().finish(workspaceMirrorDir);
|
|
30990
|
+
}
|
|
30991
|
+
function shell(selectedPath, status, refusalReason, canonicalPath, indirection, opts, extras = {}) {
|
|
30992
|
+
const base2 = {
|
|
30993
|
+
status,
|
|
30994
|
+
refusalReason,
|
|
30995
|
+
selectedPath,
|
|
30996
|
+
canonicalPath,
|
|
30997
|
+
indirection,
|
|
30998
|
+
bytesRead: extras.bytesRead ?? 0,
|
|
30999
|
+
sha256: extras.sha256 ?? null,
|
|
31000
|
+
lines: extras.lines ?? { total: 0, parsed: 0, malformed: 0, truncated: false, cutFinalLine: false },
|
|
31001
|
+
binding: emptyBinding(opts.workspaceMirrorDir),
|
|
31002
|
+
usage: null,
|
|
31003
|
+
deliveries: []
|
|
31004
|
+
};
|
|
31005
|
+
return { ...base2, limits: limitsFor(base2), complete: false };
|
|
31006
|
+
}
|
|
31007
|
+
function buildObservation(selectedPath, opts = {}) {
|
|
31008
|
+
const maxBytes = opts.maxBytes ?? OBSERVATION_MAX_BYTES;
|
|
31009
|
+
const maxLines = opts.maxLines ?? OBSERVATION_MAX_LINES;
|
|
31010
|
+
const opened = openSelected(selectedPath, opts.openHooks ?? {});
|
|
31011
|
+
if (isFailure(opened)) {
|
|
31012
|
+
return shell(selectedPath, opened.status, opened.refusalReason, opened.canonicalPath, opened.indirection, opts);
|
|
31013
|
+
}
|
|
31014
|
+
let read;
|
|
31015
|
+
try {
|
|
31016
|
+
read = readBounded(opened.fd, maxBytes);
|
|
31017
|
+
} catch {
|
|
31018
|
+
return shell(selectedPath, "unreadable", null, opened.canonicalPath, opened.indirection, opts);
|
|
31019
|
+
} finally {
|
|
31020
|
+
try {
|
|
31021
|
+
closeSync2(opened.fd);
|
|
31022
|
+
} catch {}
|
|
31023
|
+
}
|
|
31024
|
+
const pieces = read.text.split(`
|
|
31025
|
+
`);
|
|
31026
|
+
if (pieces.length > 0 && pieces[pieces.length - 1] === "")
|
|
31027
|
+
pieces.pop();
|
|
31028
|
+
let cutFinalLine = false;
|
|
31029
|
+
let truncated = read.truncated;
|
|
31030
|
+
if (read.truncated && pieces.length > 0 && !read.text.endsWith(`
|
|
31031
|
+
`)) {
|
|
31032
|
+
pieces.pop();
|
|
31033
|
+
cutFinalLine = true;
|
|
31034
|
+
}
|
|
31035
|
+
if (pieces.length > maxLines) {
|
|
31036
|
+
pieces.length = maxLines;
|
|
31037
|
+
truncated = true;
|
|
31038
|
+
}
|
|
31039
|
+
const nonBlank = pieces.filter((l) => l.trim() !== "");
|
|
31040
|
+
if (nonBlank.length === 0) {
|
|
31041
|
+
return shell(selectedPath, truncated ? "read" : "empty", null, opened.canonicalPath, opened.indirection, opts, {
|
|
31042
|
+
bytesRead: read.bytesRead,
|
|
31043
|
+
sha256: read.sha256,
|
|
31044
|
+
lines: { total: 0, parsed: 0, malformed: 0, truncated, cutFinalLine }
|
|
31045
|
+
});
|
|
31046
|
+
}
|
|
31047
|
+
const usage = new UsageAccumulator;
|
|
31048
|
+
const deliveries = new DeliveryAccumulator;
|
|
31049
|
+
const binding = new BindingAccumulator;
|
|
31050
|
+
let parsed = 0;
|
|
31051
|
+
let malformed = 0;
|
|
31052
|
+
for (const line of nonBlank) {
|
|
31053
|
+
let record2;
|
|
31054
|
+
try {
|
|
31055
|
+
record2 = JSON.parse(line);
|
|
31056
|
+
} catch {
|
|
31057
|
+
malformed += 1;
|
|
31058
|
+
continue;
|
|
31059
|
+
}
|
|
31060
|
+
if (record2 === null || typeof record2 !== "object" || Array.isArray(record2)) {
|
|
31061
|
+
malformed += 1;
|
|
31062
|
+
continue;
|
|
31063
|
+
}
|
|
31064
|
+
parsed += 1;
|
|
31065
|
+
const rec = record2;
|
|
31066
|
+
binding.add(rec);
|
|
31067
|
+
usage.add(rec);
|
|
31068
|
+
deliveries.add(rec);
|
|
31069
|
+
}
|
|
31070
|
+
const lines = {
|
|
31071
|
+
total: nonBlank.length,
|
|
31072
|
+
parsed,
|
|
31073
|
+
malformed,
|
|
31074
|
+
truncated,
|
|
31075
|
+
cutFinalLine
|
|
31076
|
+
};
|
|
31077
|
+
const status = parsed === 0 ? "malformed" : "read";
|
|
31078
|
+
const finishedUsage = status === "read" ? usage.finish() : null;
|
|
31079
|
+
const base2 = {
|
|
31080
|
+
status,
|
|
31081
|
+
refusalReason: null,
|
|
31082
|
+
selectedPath,
|
|
31083
|
+
canonicalPath: opened.canonicalPath,
|
|
31084
|
+
indirection: opened.indirection,
|
|
31085
|
+
bytesRead: read.bytesRead,
|
|
31086
|
+
sha256: read.sha256,
|
|
31087
|
+
lines,
|
|
31088
|
+
binding: binding.finish(opts.workspaceMirrorDir),
|
|
31089
|
+
usage: finishedUsage,
|
|
31090
|
+
deliveries: status === "read" ? deliveries.finish() : []
|
|
31091
|
+
};
|
|
31092
|
+
const complete = status === "read" && malformed === 0 && !truncated && !cutFinalLine && (finishedUsage === null || !finishedUsage.incomplete);
|
|
31093
|
+
return { ...base2, limits: limitsFor(base2), complete };
|
|
31094
|
+
}
|
|
30508
31095
|
|
|
30509
31096
|
// src/lib/context/context-account.ts
|
|
30510
31097
|
function localRow(workspaceSlug) {
|
|
@@ -30709,13 +31296,13 @@ function collectExternalSources() {
|
|
|
30709
31296
|
var STANDING_LIMITS = [
|
|
30710
31297
|
{
|
|
30711
31298
|
id: "observed-loading",
|
|
30712
|
-
what: "what a session ACTUALLY loaded is not observed. Everything above is bytes on disk and the hook's own rendered output",
|
|
30713
|
-
owner: "
|
|
31299
|
+
what: "what a session ACTUALLY loaded is not observed here. Everything above is bytes on disk and the hook's own rendered output",
|
|
31300
|
+
owner: "pass `--observed <transcript.jsonl>` to measure one selected session's recorded delivery. Per-source attribution stays unavailable even then"
|
|
30714
31301
|
},
|
|
30715
31302
|
{
|
|
30716
31303
|
id: "usage",
|
|
30717
|
-
what: "tokens, cached and billed usage are not measured
|
|
30718
|
-
owner: "
|
|
31304
|
+
what: "tokens, cached and billed usage are not measured here",
|
|
31305
|
+
owner: "pass `--observed <transcript.jsonl>` for one session's recorded per-request usage; the packaged/desktop half of effective-context diagnostics is still open"
|
|
30719
31306
|
},
|
|
30720
31307
|
{
|
|
30721
31308
|
id: "user-level-config",
|
|
@@ -30767,7 +31354,11 @@ function buildContextAccount(workspaceSlug, deps = {}) {
|
|
|
30767
31354
|
collectExternalSources()
|
|
30768
31355
|
];
|
|
30769
31356
|
const sources = parts.flatMap((p) => p.sources);
|
|
30770
|
-
const limits = [
|
|
31357
|
+
const limits = [
|
|
31358
|
+
...parts.flatMap((p) => p.limits),
|
|
31359
|
+
...STANDING_LIMITS,
|
|
31360
|
+
...deps.observation?.limits ?? []
|
|
31361
|
+
];
|
|
30771
31362
|
const refusedFeedingHook = sources.filter((s) => s.status === "refused" && (s.route === "hook-injection" || s.path === VOICE_FILENAME));
|
|
30772
31363
|
if (refusedFeedingHook.length > 0) {
|
|
30773
31364
|
limits.unshift({
|
|
@@ -30805,6 +31396,7 @@ function buildContextAccount(workspaceSlug, deps = {}) {
|
|
|
30805
31396
|
observedLoadedBytes: null,
|
|
30806
31397
|
estimatedTokens: null,
|
|
30807
31398
|
conditionalStanding,
|
|
31399
|
+
observation: deps.observation ?? null,
|
|
30808
31400
|
limits
|
|
30809
31401
|
};
|
|
30810
31402
|
}
|
|
@@ -31059,13 +31651,92 @@ function renderAccount(out, account) {
|
|
|
31059
31651
|
out.info(` · ${a}`);
|
|
31060
31652
|
out.info("");
|
|
31061
31653
|
}
|
|
31654
|
+
if (account.observation !== null)
|
|
31655
|
+
renderObservation(out, account.observation);
|
|
31062
31656
|
out.info("Not measured here");
|
|
31063
|
-
|
|
31657
|
+
if (account.observation === null) {
|
|
31658
|
+
out.info(" Observed loading: not observed. Tokens and billed usage: not measured.");
|
|
31659
|
+
} else {
|
|
31660
|
+
out.info(" Observed above is ONE selected session's recorded delivery and usage. No token is attributed to any file.");
|
|
31661
|
+
}
|
|
31064
31662
|
for (const l of account.limits) {
|
|
31065
31663
|
out.info(` · ${l.what}`);
|
|
31066
31664
|
out.info(` owed to: ${l.owner}`);
|
|
31067
31665
|
}
|
|
31068
31666
|
}
|
|
31667
|
+
var REFUSAL_PROSE = {
|
|
31668
|
+
"not-regular-file": "not a regular file",
|
|
31669
|
+
"target-changed": "the file opened was not the file checked — refused rather than measured",
|
|
31670
|
+
"path-unresolvable": "the path could not be resolved"
|
|
31671
|
+
};
|
|
31672
|
+
var STATUS_PROSE = {
|
|
31673
|
+
absent: "not there",
|
|
31674
|
+
unreadable: "could not be read",
|
|
31675
|
+
empty: "no records",
|
|
31676
|
+
malformed: "records present, none parsed — NOT a measured zero"
|
|
31677
|
+
};
|
|
31678
|
+
function renderObservation(out, obs) {
|
|
31679
|
+
const head = obs.complete ? "Observed" : "Observed (PARTIAL)";
|
|
31680
|
+
if (obs.status !== "read") {
|
|
31681
|
+
const why = REFUSAL_PROSE[obs.refusalReason ?? ""] ?? STATUS_PROSE[obs.status] ?? obs.status;
|
|
31682
|
+
out.info(`${head} ${obs.selectedPath}`);
|
|
31683
|
+
out.info(` ${why}. Nothing was measured, and that is not a zero.`);
|
|
31684
|
+
if (obs.bytesRead > 0 || obs.lines.total > 0) {
|
|
31685
|
+
out.info(` ${fmt(obs.bytesRead)} bytes read · ${fmt(obs.lines.total)} records seen` + (obs.lines.malformed > 0 ? ` · ${fmt(obs.lines.malformed)} malformed` : "") + (obs.lines.truncated ? " · stopped at the read bound" : "") + (obs.sha256 !== null ? ` · sha256 ${obs.sha256.slice(0, 16)}` : ""));
|
|
31686
|
+
}
|
|
31687
|
+
out.info("");
|
|
31688
|
+
return;
|
|
31689
|
+
}
|
|
31690
|
+
const b = obs.binding;
|
|
31691
|
+
out.info(`${head} ${fmt(obs.bytesRead)} bytes read from the selected transcript`);
|
|
31692
|
+
if (obs.canonicalPath !== null && obs.indirection) {
|
|
31693
|
+
out.info(` resolved through a link to ${obs.canonicalPath}`);
|
|
31694
|
+
}
|
|
31695
|
+
out.info(` session ${b.sessionId ?? "unknown"} · runtime ${b.runtimeVersion ?? "unknown"} · scope ${b.scope}`);
|
|
31696
|
+
out.info(` interval ${b.firstTimestamp ?? "unknown"} .. ${b.lastTimestamp ?? "unknown"} · sha256 ${obs.sha256?.slice(0, 16) ?? "unknown"}`);
|
|
31697
|
+
out.info(` ${fmt(obs.lines.parsed)} of ${fmt(obs.lines.total)} records parsed` + (obs.lines.malformed > 0 ? ` · ${fmt(obs.lines.malformed)} malformed` : "") + (obs.lines.truncated ? " · stopped at the read bound" : "") + (obs.lines.cutFinalLine ? " · final record severed by the bound" : ""));
|
|
31698
|
+
if (b.rejectedFields > 0) {
|
|
31699
|
+
out.info(` ${fmt(b.rejectedFields)} provenance value(s) in the file were refused as unsafe or over-long and read as unknown above.`);
|
|
31700
|
+
}
|
|
31701
|
+
if (!b.consistent) {
|
|
31702
|
+
out.info(" NOT attributed to this workspace: session, runtime or directory is missing or varies across records.");
|
|
31703
|
+
}
|
|
31704
|
+
out.info("");
|
|
31705
|
+
const u = obs.usage;
|
|
31706
|
+
if (u === null) {
|
|
31707
|
+
out.info(" Usage — no record carried a usage object. Unknown, not zero.");
|
|
31708
|
+
} else {
|
|
31709
|
+
const n = (v) => v === null ? "unknown" : fmt(v);
|
|
31710
|
+
out.info(` Usage over ${fmt(u.requests)} requests, from ${fmt(u.countedRecords)} usage-bearing records` + (u.duplicateRecords > 0 ? ` (${fmt(u.duplicateRecords)} repeated the same usage)` : ""));
|
|
31711
|
+
out.info(` fresh input ${n(u.inputTokens)} tokens`);
|
|
31712
|
+
out.info(` cache read ${n(u.cacheReadTokens)} tokens`);
|
|
31713
|
+
out.info(` cache written ${n(u.cacheCreationTokens)} tokens`);
|
|
31714
|
+
out.info(` output ${n(u.outputTokens)} tokens`);
|
|
31715
|
+
if (u.conflictingDuplicates > 0) {
|
|
31716
|
+
out.info(` ⚠ ${fmt(u.conflictingDuplicates)} request(s) reported conflicting usage; the figures above are incomplete`);
|
|
31717
|
+
}
|
|
31718
|
+
if (u.untaggedRecords > 0) {
|
|
31719
|
+
out.info(` ⚠ ${fmt(u.untaggedRecords)} record(s) carried usage with no request id; excluded, because they cannot be shown independent`);
|
|
31720
|
+
}
|
|
31721
|
+
if (u.unresolvedRequests > 0) {
|
|
31722
|
+
out.info(` ⚠ ${fmt(u.unresolvedRequests)} of ${fmt(u.requests)} request(s) carried no usage record, so what they cost is unknown — not zero`);
|
|
31723
|
+
}
|
|
31724
|
+
if (u.unresolvableRecords > 0) {
|
|
31725
|
+
out.info(` ⚠ ${fmt(u.unresolvableRecords)} record(s) had neither usage nor a request id; nothing can name or resolve them`);
|
|
31726
|
+
}
|
|
31727
|
+
if (u.incompleteFields.length > 0) {
|
|
31728
|
+
out.info(` ⚠ not every request reported: ${u.incompleteFields.join(", ")}`);
|
|
31729
|
+
}
|
|
31730
|
+
}
|
|
31731
|
+
out.info("");
|
|
31732
|
+
if (obs.deliveries.length > 0) {
|
|
31733
|
+
out.info(" Recorded deliveries — payload bytes as stored, not injected or billed bytes");
|
|
31734
|
+
for (const d of obs.deliveries) {
|
|
31735
|
+
out.info(` ${d.kind.padEnd(16)} ${d.label.padEnd(24)} ${String(d.occurrences).padStart(4)}x ${fmt(d.recordedBytes)} bytes`);
|
|
31736
|
+
}
|
|
31737
|
+
out.info("");
|
|
31738
|
+
}
|
|
31739
|
+
}
|
|
31069
31740
|
async function runContext(args, deps = {}) {
|
|
31070
31741
|
const out = new Output({ sink: deps.sink, noColor: deps.noColor });
|
|
31071
31742
|
const config2 = (deps.loadConfigFn ?? loadConfig)();
|
|
@@ -31079,7 +31750,20 @@ async function runContext(args, deps = {}) {
|
|
|
31079
31750
|
out.error(`Workspace "${slug}" not found. Run \`plinth login --workspace ${slug}\` first.`);
|
|
31080
31751
|
return { exitCode: 1 };
|
|
31081
31752
|
}
|
|
31753
|
+
let observation = null;
|
|
31754
|
+
if (args.observed !== undefined && args.observed !== "") {
|
|
31755
|
+
let mirrorDir;
|
|
31756
|
+
try {
|
|
31757
|
+
mirrorDir = workspaceMirrorPath(slug);
|
|
31758
|
+
} catch {
|
|
31759
|
+
mirrorDir = undefined;
|
|
31760
|
+
}
|
|
31761
|
+
observation = (deps.buildObservationFn ?? buildObservation)(args.observed, {
|
|
31762
|
+
workspaceMirrorDir: mirrorDir
|
|
31763
|
+
});
|
|
31764
|
+
}
|
|
31082
31765
|
const account = buildContextAccount(slug, {
|
|
31766
|
+
observation,
|
|
31083
31767
|
hook: deps.hook ?? (() => defaultHook(slug)),
|
|
31084
31768
|
listMemberCommands: deps.listMemberCommands,
|
|
31085
31769
|
readStoreModes: deps.readStoreModes ?? readMemoryStoreModesNoWrite
|
|
@@ -31110,6 +31794,11 @@ var contextCommand = defineCommand({
|
|
|
31110
31794
|
description: "Emit the account as JSON",
|
|
31111
31795
|
required: false
|
|
31112
31796
|
},
|
|
31797
|
+
observed: {
|
|
31798
|
+
type: "string",
|
|
31799
|
+
description: "Path to ONE session transcript (.jsonl) to measure recorded delivery and usage from. Explicit: nothing is discovered or crawled, and no token is attributed to any file",
|
|
31800
|
+
required: false
|
|
31801
|
+
},
|
|
31113
31802
|
"no-color": {
|
|
31114
31803
|
type: "boolean",
|
|
31115
31804
|
description: "Disable ANSI colour output",
|
|
@@ -31117,7 +31806,7 @@ var contextCommand = defineCommand({
|
|
|
31117
31806
|
}
|
|
31118
31807
|
},
|
|
31119
31808
|
async run({ args }) {
|
|
31120
|
-
const result = await runContext({ workspace: args.workspace, json: args.json === true }, { noColor: noColorFromArgs(args) });
|
|
31809
|
+
const result = await runContext({ workspace: args.workspace, json: args.json === true, observed: args.observed }, { noColor: noColorFromArgs(args) });
|
|
31121
31810
|
if (result.exitCode !== 0)
|
|
31122
31811
|
process.exit(result.exitCode);
|
|
31123
31812
|
}
|
|
@@ -31484,14 +32173,14 @@ var loginCommand = defineCommand({
|
|
|
31484
32173
|
// src/lib/memory-gate/log.ts
|
|
31485
32174
|
import {
|
|
31486
32175
|
appendFileSync,
|
|
31487
|
-
closeSync as
|
|
32176
|
+
closeSync as closeSync3,
|
|
31488
32177
|
mkdirSync as mkdirSync8,
|
|
31489
|
-
openSync as
|
|
32178
|
+
openSync as openSync3,
|
|
31490
32179
|
readFileSync as readFileSync12,
|
|
31491
|
-
readSync,
|
|
32180
|
+
readSync as readSync2,
|
|
31492
32181
|
statSync as statSync3
|
|
31493
32182
|
} from "node:fs";
|
|
31494
|
-
import { join as
|
|
32183
|
+
import { join as join10 } from "node:path";
|
|
31495
32184
|
|
|
31496
32185
|
// src/lib/sessions/registry.ts
|
|
31497
32186
|
import {
|
|
@@ -31503,7 +32192,7 @@ import {
|
|
|
31503
32192
|
unlinkSync as unlinkSync6,
|
|
31504
32193
|
writeFileSync as writeFileSync6
|
|
31505
32194
|
} from "node:fs";
|
|
31506
|
-
import { basename as
|
|
32195
|
+
import { basename as basename3, dirname as dirname7, join as join9 } from "node:path";
|
|
31507
32196
|
var SessionRecordSchema = exports_external.object({
|
|
31508
32197
|
version: exports_external.literal(1),
|
|
31509
32198
|
session_id: exports_external.string().min(1),
|
|
@@ -31524,13 +32213,13 @@ function sessionsDir(workspaceSlug) {
|
|
|
31524
32213
|
if (!isSafeSegment(workspaceSlug)) {
|
|
31525
32214
|
throw new Error(`Refusing unsafe workspace slug: ${workspaceSlug}`);
|
|
31526
32215
|
}
|
|
31527
|
-
return
|
|
32216
|
+
return join9(plinthHome(), workspaceSlug, "sessions");
|
|
31528
32217
|
}
|
|
31529
32218
|
function sessionRecordPath(workspaceSlug, sessionId) {
|
|
31530
32219
|
if (!isSafeSegment(sessionId)) {
|
|
31531
32220
|
throw new Error(`Refusing unsafe session id: ${sessionId}`);
|
|
31532
32221
|
}
|
|
31533
|
-
return
|
|
32222
|
+
return join9(sessionsDir(workspaceSlug), `${sessionId}.json`);
|
|
31534
32223
|
}
|
|
31535
32224
|
function registerSession(input) {
|
|
31536
32225
|
const startTime = (input.startTimeFn ?? readPidStartTime)(input.pid);
|
|
@@ -31553,7 +32242,7 @@ function registerSession(input) {
|
|
|
31553
32242
|
}
|
|
31554
32243
|
var tmpCounter = 0;
|
|
31555
32244
|
function writeRecordAtomic(target, record2) {
|
|
31556
|
-
const dir =
|
|
32245
|
+
const dir = dirname7(target);
|
|
31557
32246
|
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
31558
32247
|
const tmp = `${target}.${process.pid}.${tmpCounter++}.tmp`;
|
|
31559
32248
|
sweepDeadTmpFiles(dir, target);
|
|
@@ -31569,7 +32258,7 @@ function writeRecordAtomic(target, record2) {
|
|
|
31569
32258
|
}
|
|
31570
32259
|
}
|
|
31571
32260
|
function sweepDeadTmpFiles(dir, target) {
|
|
31572
|
-
const prefix = `${
|
|
32261
|
+
const prefix = `${basename3(target)}.`;
|
|
31573
32262
|
let names;
|
|
31574
32263
|
try {
|
|
31575
32264
|
names = readdirSync5(dir);
|
|
@@ -31586,7 +32275,7 @@ function sweepDeadTmpFiles(dir, target) {
|
|
|
31586
32275
|
if (pid === process.pid || isPidAlive(pid))
|
|
31587
32276
|
continue;
|
|
31588
32277
|
try {
|
|
31589
|
-
unlinkSync6(
|
|
32278
|
+
unlinkSync6(join9(dir, name));
|
|
31590
32279
|
} catch {}
|
|
31591
32280
|
}
|
|
31592
32281
|
}
|
|
@@ -31651,7 +32340,7 @@ function listSessions(workspaceSlug, deps = {}) {
|
|
|
31651
32340
|
for (const name of names) {
|
|
31652
32341
|
if (!name.endsWith(".json"))
|
|
31653
32342
|
continue;
|
|
31654
|
-
const path3 =
|
|
32343
|
+
const path3 = join9(sessionsDir(workspaceSlug), name);
|
|
31655
32344
|
const record2 = readSessionRecord(path3);
|
|
31656
32345
|
if (!record2) {
|
|
31657
32346
|
continue;
|
|
@@ -31710,10 +32399,10 @@ function memoryGateDir(workspaceSlug) {
|
|
|
31710
32399
|
if (!isSafeSegment(workspaceSlug)) {
|
|
31711
32400
|
throw new Error(`Refusing unsafe workspace slug: ${workspaceSlug}`);
|
|
31712
32401
|
}
|
|
31713
|
-
return
|
|
32402
|
+
return join10(plinthHome(), "_memory_gate", workspaceSlug);
|
|
31714
32403
|
}
|
|
31715
32404
|
function memoryGateLogPath(workspaceSlug) {
|
|
31716
|
-
return
|
|
32405
|
+
return join10(memoryGateDir(workspaceSlug), "log.jsonl");
|
|
31717
32406
|
}
|
|
31718
32407
|
function buildDecisionRecord(input) {
|
|
31719
32408
|
const final = input.decision === "accept" ? input.proposed : input.decision === "reject" ? null : input.final ?? null;
|
|
@@ -31746,13 +32435,13 @@ function endsMidLine(path3) {
|
|
|
31746
32435
|
}
|
|
31747
32436
|
if (size === 0)
|
|
31748
32437
|
return false;
|
|
31749
|
-
const fd =
|
|
32438
|
+
const fd = openSync3(path3, "r");
|
|
31750
32439
|
try {
|
|
31751
32440
|
const last = Buffer.alloc(1);
|
|
31752
|
-
|
|
32441
|
+
readSync2(fd, last, 0, 1, size - 1);
|
|
31753
32442
|
return last[0] !== 10;
|
|
31754
32443
|
} finally {
|
|
31755
|
-
|
|
32444
|
+
closeSync3(fd);
|
|
31756
32445
|
}
|
|
31757
32446
|
}
|
|
31758
32447
|
function appendMemoryGateRecord(workspaceSlug, record2) {
|
|
@@ -33017,10 +33706,10 @@ import { existsSync as existsSync9, readFileSync as readFileSync26, statSync as
|
|
|
33017
33706
|
|
|
33018
33707
|
// src/lib/daemon-log.ts
|
|
33019
33708
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9, renameSync as renameSync7, statSync as statSync4 } from "node:fs";
|
|
33020
|
-
import { dirname as
|
|
33709
|
+
import { dirname as dirname8, join as join11 } from "node:path";
|
|
33021
33710
|
var DEFAULT_DAEMON_LOG_MAX_BYTES = 5000000;
|
|
33022
33711
|
function daemonLogPath() {
|
|
33023
|
-
return
|
|
33712
|
+
return join11(plinthHome(), "daemon.log");
|
|
33024
33713
|
}
|
|
33025
33714
|
function daemonLogMaxBytes() {
|
|
33026
33715
|
const raw = process.env.PLINTH_DAEMON_LOG_MAX_BYTES;
|
|
@@ -33042,7 +33731,7 @@ class DaemonLog {
|
|
|
33042
33731
|
this.now = opts.now ?? (() => new Date);
|
|
33043
33732
|
this.maxBytes = opts.maxBytes ?? daemonLogMaxBytes();
|
|
33044
33733
|
try {
|
|
33045
|
-
mkdirSync9(
|
|
33734
|
+
mkdirSync9(dirname8(this.path), { recursive: true });
|
|
33046
33735
|
} catch {}
|
|
33047
33736
|
try {
|
|
33048
33737
|
this.bytesWritten = statSync4(this.path).size;
|
|
@@ -33081,7 +33770,7 @@ class DaemonLog {
|
|
|
33081
33770
|
// src/lib/destructive-gate.ts
|
|
33082
33771
|
var import_gray_matter7 = __toESM(require_gray_matter(), 1);
|
|
33083
33772
|
import { readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
|
|
33084
|
-
import { basename as
|
|
33773
|
+
import { basename as basename4 } from "node:path";
|
|
33085
33774
|
var GUTTING_RATIO_DEFAULT = 0.5;
|
|
33086
33775
|
var GUTTING_MIN_PRIOR_BYTES = 200;
|
|
33087
33776
|
function envFloat(name, def) {
|
|
@@ -33146,7 +33835,7 @@ function classifyDestructive(event, entityType, state, deps = {}) {
|
|
|
33146
33835
|
return null;
|
|
33147
33836
|
}
|
|
33148
33837
|
const liveInode = statInode(event.absolutePath);
|
|
33149
|
-
const slug =
|
|
33838
|
+
const slug = basename4(event.absolutePath, ".md");
|
|
33150
33839
|
const entry2 = (liveInode !== undefined ? getEntryByInode(state, entityType, liveInode) : undefined) ?? getEntryBySlug(state, entityType, slug);
|
|
33151
33840
|
if (!entry2)
|
|
33152
33841
|
return null;
|
|
@@ -33261,18 +33950,18 @@ import { readFileSync as readFileSync18 } from "node:fs";
|
|
|
33261
33950
|
|
|
33262
33951
|
// src/lib/conflicts.ts
|
|
33263
33952
|
import { mkdirSync as mkdirSync10, readdirSync as readdirSync6, statSync as statSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
33264
|
-
import { dirname as
|
|
33953
|
+
import { dirname as dirname9, join as join12 } from "node:path";
|
|
33265
33954
|
function conflictsRoot(workspaceSlug) {
|
|
33266
|
-
return
|
|
33955
|
+
return join12(plinthHome(), "_conflicts", workspaceSlug);
|
|
33267
33956
|
}
|
|
33268
33957
|
function conflictFilePath(args) {
|
|
33269
33958
|
const { workspaceSlug, entityType, entitySlug, timestamp, side } = args;
|
|
33270
33959
|
const iso = timestamp.toISOString().replace(/[:.]/g, "-");
|
|
33271
|
-
return
|
|
33960
|
+
return join12(conflictsRoot(workspaceSlug), `${entityType}.${entitySlug}.${iso}.${side}.md`);
|
|
33272
33961
|
}
|
|
33273
33962
|
function stashConflict(args) {
|
|
33274
33963
|
const target = conflictFilePath(args);
|
|
33275
|
-
mkdirSync10(
|
|
33964
|
+
mkdirSync10(dirname9(target), { recursive: true, mode: 448 });
|
|
33276
33965
|
writeFileSync7(target, args.contents, { mode: 384 });
|
|
33277
33966
|
return target;
|
|
33278
33967
|
}
|
|
@@ -33280,11 +33969,11 @@ function fileConflictPath(args) {
|
|
|
33280
33969
|
const { workspaceSlug, relPath, timestamp, side } = args;
|
|
33281
33970
|
const iso = timestamp.toISOString().replace(/[:.]/g, "-");
|
|
33282
33971
|
const safe = relPath.replace(/[/\\]/g, "__");
|
|
33283
|
-
return
|
|
33972
|
+
return join12(conflictsRoot(workspaceSlug), `files.${safe}.${iso}.${side}`);
|
|
33284
33973
|
}
|
|
33285
33974
|
function stashFileConflict(args) {
|
|
33286
33975
|
const target = fileConflictPath(args);
|
|
33287
|
-
mkdirSync10(
|
|
33976
|
+
mkdirSync10(dirname9(target), { recursive: true, mode: 448 });
|
|
33288
33977
|
writeFileSync7(target, args.bytes, { mode: 384 });
|
|
33289
33978
|
return target;
|
|
33290
33979
|
}
|
|
@@ -33317,7 +34006,7 @@ function listConflictStashes(workspaceSlug) {
|
|
|
33317
34006
|
}
|
|
33318
34007
|
const out = [];
|
|
33319
34008
|
for (const file2 of names) {
|
|
33320
|
-
const absPath =
|
|
34009
|
+
const absPath = join12(dir, file2);
|
|
33321
34010
|
let st;
|
|
33322
34011
|
try {
|
|
33323
34012
|
st = statSync6(absPath);
|
|
@@ -33346,9 +34035,9 @@ function listConflictStashes(workspaceSlug) {
|
|
|
33346
34035
|
import { readFileSync as readFileSync15, statSync as statSync7 } from "node:fs";
|
|
33347
34036
|
|
|
33348
34037
|
// src/lib/files/content-hash.ts
|
|
33349
|
-
import { createHash as
|
|
34038
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
33350
34039
|
function sha256Bytes(bytes) {
|
|
33351
|
-
return
|
|
34040
|
+
return createHash3("sha256").update(bytes).digest("hex");
|
|
33352
34041
|
}
|
|
33353
34042
|
|
|
33354
34043
|
// src/lib/files/mime.ts
|
|
@@ -33668,8 +34357,8 @@ class PerIdMutex {
|
|
|
33668
34357
|
|
|
33669
34358
|
// src/lib/push.ts
|
|
33670
34359
|
var import_gray_matter10 = __toESM(require_gray_matter(), 1);
|
|
33671
|
-
import { readFileSync as readFileSync17, statSync as statSync8 } from "node:fs";
|
|
33672
|
-
import { basename as
|
|
34360
|
+
import { existsSync as existsSync4, readFileSync as readFileSync17, statSync as statSync8 } from "node:fs";
|
|
34361
|
+
import { basename as basename5, relative as relative5 } from "node:path";
|
|
33673
34362
|
|
|
33674
34363
|
// src/lib/frontmatter-id.ts
|
|
33675
34364
|
var import_gray_matter8 = __toESM(require_gray_matter(), 1);
|
|
@@ -33856,6 +34545,10 @@ function writeMirrorWithBodyPreserve(workspaceSlug, strategy, doc2, suppressor,
|
|
|
33856
34545
|
}
|
|
33857
34546
|
|
|
33858
34547
|
// src/lib/push.ts
|
|
34548
|
+
var DELETE_DECLINED_PATH_EXISTS = "the file is on disk again — a newer write owns this row";
|
|
34549
|
+
function deleteDeclinedSentence(relPath) {
|
|
34550
|
+
return `Not deleted: ${relPath} is on disk again, so the row was left alone. If you meant to delete it, remove the file and it will be retried.`;
|
|
34551
|
+
}
|
|
33859
34552
|
function normaliseWireBody(body) {
|
|
33860
34553
|
if (body === undefined)
|
|
33861
34554
|
return;
|
|
@@ -34150,7 +34843,7 @@ async function pushFile(args) {
|
|
|
34150
34843
|
const rel = relative5(workspaceMirrorPath(workspaceSlug), absolutePath);
|
|
34151
34844
|
parsedSlug = strategy.parseMirrorPath(rel)?.slug ?? null;
|
|
34152
34845
|
} catch {}
|
|
34153
|
-
const slugFromPath = parsedSlug ??
|
|
34846
|
+
const slugFromPath = parsedSlug ?? basename5(absolutePath, ".md");
|
|
34154
34847
|
const slug = identity === "path" ? slugFromPath : slugFromFm ?? slugFromPath;
|
|
34155
34848
|
if (!slug) {
|
|
34156
34849
|
return {
|
|
@@ -34233,15 +34926,16 @@ async function reconcileContextDocConflict(args) {
|
|
|
34233
34926
|
now,
|
|
34234
34927
|
output
|
|
34235
34928
|
} = args;
|
|
34929
|
+
const base2 = args.state;
|
|
34236
34930
|
let state = args.state;
|
|
34237
34931
|
const existingSlug = existing.slug;
|
|
34238
34932
|
const existingId = existing.id;
|
|
34239
34933
|
let fileSlug;
|
|
34240
34934
|
try {
|
|
34241
34935
|
const rel = relative5(workspaceMirrorPath(workspaceSlug), absolutePath);
|
|
34242
|
-
fileSlug = strategy.parseMirrorPath(rel)?.slug ??
|
|
34936
|
+
fileSlug = strategy.parseMirrorPath(rel)?.slug ?? basename5(absolutePath, ".md");
|
|
34243
34937
|
} catch {
|
|
34244
|
-
fileSlug =
|
|
34938
|
+
fileSlug = basename5(absolutePath, ".md");
|
|
34245
34939
|
}
|
|
34246
34940
|
if (fileSlug !== existingSlug) {
|
|
34247
34941
|
const stashPath = stashConflict({
|
|
@@ -34282,7 +34976,8 @@ async function reconcileContextDocConflict(args) {
|
|
|
34282
34976
|
entitySlug: fileSlug,
|
|
34283
34977
|
stashPath
|
|
34284
34978
|
},
|
|
34285
|
-
state: nextState
|
|
34979
|
+
state: nextState,
|
|
34980
|
+
base: base2
|
|
34286
34981
|
};
|
|
34287
34982
|
}
|
|
34288
34983
|
const stashPreLandCloudCopy = () => stashConflict({
|
|
@@ -34344,7 +35039,8 @@ async function reconcileContextDocConflict(args) {
|
|
|
34344
35039
|
entitySlug: updated.slug,
|
|
34345
35040
|
stashPath: preLandStash
|
|
34346
35041
|
},
|
|
34347
|
-
state
|
|
35042
|
+
state,
|
|
35043
|
+
base: base2
|
|
34348
35044
|
};
|
|
34349
35045
|
}
|
|
34350
35046
|
if (patchResult.code === 404) {
|
|
@@ -34402,7 +35098,8 @@ async function reconcileContextDocConflict(args) {
|
|
|
34402
35098
|
entitySlug: fileSlug,
|
|
34403
35099
|
stashPath
|
|
34404
35100
|
},
|
|
34405
|
-
state
|
|
35101
|
+
state,
|
|
35102
|
+
base: base2
|
|
34406
35103
|
};
|
|
34407
35104
|
}
|
|
34408
35105
|
if (patchResult.reason === "context_doc_conflict") {
|
|
@@ -34430,10 +35127,11 @@ async function runPushUpsert(args) {
|
|
|
34430
35127
|
const hashBody = hashableBody(strategy, bodyMarkdown);
|
|
34431
35128
|
const newBodyHash = stableHash(hashBody);
|
|
34432
35129
|
const newFrontmatterHash = stableHash({ ...userFm, ...lifted.topLevel });
|
|
34433
|
-
const
|
|
35130
|
+
const snapshotEntry = getEntry(args.state, strategy.type, args.stateEntry.id) ?? args.stateEntry;
|
|
35131
|
+
const liveEntry = committedEntryIfNewer(args.workspaceSlug, strategy.type, snapshotEntry) ?? snapshotEntry;
|
|
34434
35132
|
const bodyChanged = newBodyHash !== liveEntry.body_hash;
|
|
34435
35133
|
const frontmatterChanged = newFrontmatterHash !== liveEntry.frontmatter_hash;
|
|
34436
|
-
let workingState = args.state;
|
|
35134
|
+
let workingState = liveEntry === snapshotEntry ? args.state : upsertEntry(args.state, strategy.type, liveEntry).state;
|
|
34437
35135
|
if (fm["id"] === undefined && workingState.frontmatter_id_backfill_version !== undefined) {
|
|
34438
35136
|
const cleared = clearFrontmatterIdBackfillMarker(workingState);
|
|
34439
35137
|
if (cleared.changed) {
|
|
@@ -34442,6 +35140,69 @@ async function runPushUpsert(args) {
|
|
|
34442
35140
|
} catch {}
|
|
34443
35141
|
}
|
|
34444
35142
|
}
|
|
35143
|
+
const wireProjection = (md) => {
|
|
35144
|
+
try {
|
|
35145
|
+
const p = import_gray_matter10.default(md);
|
|
35146
|
+
const f = p.data ?? {};
|
|
35147
|
+
const l = splitUserFrontmatter(strategy, f);
|
|
35148
|
+
const u = userFrontmatter(strategy, l.frontmatter);
|
|
35149
|
+
return stableHash({
|
|
35150
|
+
body: strategy.bodyToWire(p.content),
|
|
35151
|
+
frontmatter: { ...u, ...l.topLevel }
|
|
35152
|
+
});
|
|
35153
|
+
} catch {
|
|
35154
|
+
return null;
|
|
35155
|
+
}
|
|
35156
|
+
};
|
|
35157
|
+
if (liveEntry !== snapshotEntry) {
|
|
35158
|
+
let onDisk = null;
|
|
35159
|
+
try {
|
|
35160
|
+
onDisk = readFileSync17(args.absolutePath, "utf8");
|
|
35161
|
+
} catch {
|
|
35162
|
+
onDisk = null;
|
|
35163
|
+
}
|
|
35164
|
+
if (onDisk !== null && onDisk !== args.raw) {
|
|
35165
|
+
let disk = null;
|
|
35166
|
+
try {
|
|
35167
|
+
disk = computeMirrorHashes(strategy, onDisk);
|
|
35168
|
+
} catch {
|
|
35169
|
+
disk = null;
|
|
35170
|
+
}
|
|
35171
|
+
if (disk !== null && disk.bodyHash === liveEntry.body_hash && disk.frontmatterHash === liveEntry.frontmatter_hash) {
|
|
35172
|
+
const ours = wireProjection(args.raw);
|
|
35173
|
+
const theirs = wireProjection(onDisk);
|
|
35174
|
+
if (ours !== null && theirs !== null && ours === theirs) {
|
|
35175
|
+
return {
|
|
35176
|
+
outcome: {
|
|
35177
|
+
kind: "noop",
|
|
35178
|
+
entityType: strategy.type,
|
|
35179
|
+
entitySlug: liveEntry.slug
|
|
35180
|
+
},
|
|
35181
|
+
state: workingState,
|
|
35182
|
+
base: workingState
|
|
35183
|
+
};
|
|
35184
|
+
}
|
|
35185
|
+
const stashPath2 = stashConflict({
|
|
35186
|
+
workspaceSlug: args.workspaceSlug,
|
|
35187
|
+
entityType: strategy.type,
|
|
35188
|
+
entitySlug: liveEntry.slug,
|
|
35189
|
+
timestamp: args.now(),
|
|
35190
|
+
contents: args.raw,
|
|
35191
|
+
side: "local"
|
|
35192
|
+
});
|
|
35193
|
+
return {
|
|
35194
|
+
outcome: {
|
|
35195
|
+
kind: "conflict",
|
|
35196
|
+
entityType: strategy.type,
|
|
35197
|
+
entitySlug: liveEntry.slug,
|
|
35198
|
+
stashPath: stashPath2
|
|
35199
|
+
},
|
|
35200
|
+
state: workingState,
|
|
35201
|
+
base: workingState
|
|
35202
|
+
};
|
|
35203
|
+
}
|
|
35204
|
+
}
|
|
35205
|
+
}
|
|
34445
35206
|
if (!bodyChanged && !frontmatterChanged) {
|
|
34446
35207
|
return {
|
|
34447
35208
|
outcome: {
|
|
@@ -34449,7 +35210,8 @@ async function runPushUpsert(args) {
|
|
|
34449
35210
|
entityType: strategy.type,
|
|
34450
35211
|
entitySlug: liveEntry.slug
|
|
34451
35212
|
},
|
|
34452
|
-
state: workingState
|
|
35213
|
+
state: workingState,
|
|
35214
|
+
base: workingState
|
|
34453
35215
|
};
|
|
34454
35216
|
}
|
|
34455
35217
|
const wireBodyFields = bodyChanged ? strategy.bodyToWire(bodyMarkdown) : undefined;
|
|
@@ -34468,7 +35230,8 @@ async function runPushUpsert(args) {
|
|
|
34468
35230
|
entityType: strategy.type,
|
|
34469
35231
|
entitySlug: liveEntry.slug
|
|
34470
35232
|
},
|
|
34471
|
-
state: persistEntry(workingState, strategy.type, updated)
|
|
35233
|
+
state: persistEntry(workingState, strategy.type, updated),
|
|
35234
|
+
base: workingState
|
|
34472
35235
|
};
|
|
34473
35236
|
}
|
|
34474
35237
|
const mergedTopLevel = split || wireBodyFields?.topLevel ? {
|
|
@@ -34504,7 +35267,8 @@ async function runPushUpsert(args) {
|
|
|
34504
35267
|
fields,
|
|
34505
35268
|
pushed: false
|
|
34506
35269
|
},
|
|
34507
|
-
state: nextState2
|
|
35270
|
+
state: nextState2,
|
|
35271
|
+
base: workingState
|
|
34508
35272
|
};
|
|
34509
35273
|
}
|
|
34510
35274
|
let patchResult = await patchEntity({
|
|
@@ -34577,7 +35341,8 @@ async function runPushUpsert(args) {
|
|
|
34577
35341
|
fields,
|
|
34578
35342
|
pushed: true
|
|
34579
35343
|
},
|
|
34580
|
-
state: nextState2
|
|
35344
|
+
state: nextState2,
|
|
35345
|
+
base: workingState
|
|
34581
35346
|
};
|
|
34582
35347
|
}
|
|
34583
35348
|
return {
|
|
@@ -34586,7 +35351,8 @@ async function runPushUpsert(args) {
|
|
|
34586
35351
|
entityType: strategy.type,
|
|
34587
35352
|
entitySlug: updated.slug
|
|
34588
35353
|
},
|
|
34589
|
-
state: nextState2
|
|
35354
|
+
state: nextState2,
|
|
35355
|
+
base: workingState
|
|
34590
35356
|
};
|
|
34591
35357
|
}
|
|
34592
35358
|
if (patchResult.code === 404) {
|
|
@@ -34596,7 +35362,8 @@ async function runPushUpsert(args) {
|
|
|
34596
35362
|
entityType: strategy.type,
|
|
34597
35363
|
entitySlug: liveEntry.slug
|
|
34598
35364
|
},
|
|
34599
|
-
state: workingState
|
|
35365
|
+
state: workingState,
|
|
35366
|
+
base: workingState
|
|
34600
35367
|
};
|
|
34601
35368
|
}
|
|
34602
35369
|
if (patchResult.reason === "slug_conflict") {
|
|
@@ -34666,7 +35433,8 @@ async function runPushUpsert(args) {
|
|
|
34666
35433
|
entitySlug: liveEntry.slug,
|
|
34667
35434
|
stashPath
|
|
34668
35435
|
},
|
|
34669
|
-
state: nextState
|
|
35436
|
+
state: nextState,
|
|
35437
|
+
base: workingState
|
|
34670
35438
|
};
|
|
34671
35439
|
});
|
|
34672
35440
|
}
|
|
@@ -34685,6 +35453,7 @@ async function reconcileTaskConflict(args) {
|
|
|
34685
35453
|
now,
|
|
34686
35454
|
output
|
|
34687
35455
|
} = args;
|
|
35456
|
+
const base2 = args.state;
|
|
34688
35457
|
let state = args.state;
|
|
34689
35458
|
const stashLocalSide = () => stashConflict({
|
|
34690
35459
|
workspaceSlug,
|
|
@@ -34717,7 +35486,8 @@ async function reconcileTaskConflict(args) {
|
|
|
34717
35486
|
entitySlug: liveEntry.slug,
|
|
34718
35487
|
stashPath: stashPath2
|
|
34719
35488
|
},
|
|
34720
|
-
state
|
|
35489
|
+
state,
|
|
35490
|
+
base: base2
|
|
34721
35491
|
};
|
|
34722
35492
|
}
|
|
34723
35493
|
let cloudRow = null;
|
|
@@ -34735,7 +35505,8 @@ async function reconcileTaskConflict(args) {
|
|
|
34735
35505
|
entityType: strategy.type,
|
|
34736
35506
|
entitySlug: liveEntry.slug
|
|
34737
35507
|
},
|
|
34738
|
-
state
|
|
35508
|
+
state,
|
|
35509
|
+
base: base2
|
|
34739
35510
|
};
|
|
34740
35511
|
}
|
|
34741
35512
|
const stashCloudSide = () => stashConflict({
|
|
@@ -34775,7 +35546,8 @@ async function reconcileTaskConflict(args) {
|
|
|
34775
35546
|
entitySlug: patchResult.doc.slug,
|
|
34776
35547
|
stashPath: stashPath2
|
|
34777
35548
|
},
|
|
34778
|
-
state
|
|
35549
|
+
state,
|
|
35550
|
+
base: base2
|
|
34779
35551
|
};
|
|
34780
35552
|
}
|
|
34781
35553
|
if (patchResult.code === 404) {
|
|
@@ -34786,7 +35558,8 @@ async function reconcileTaskConflict(args) {
|
|
|
34786
35558
|
entityType: strategy.type,
|
|
34787
35559
|
entitySlug: liveEntry.slug
|
|
34788
35560
|
},
|
|
34789
|
-
state
|
|
35561
|
+
state,
|
|
35562
|
+
base: base2
|
|
34790
35563
|
};
|
|
34791
35564
|
}
|
|
34792
35565
|
const stashPath = stashLocalSide();
|
|
@@ -34809,7 +35582,8 @@ async function reconcileTaskConflict(args) {
|
|
|
34809
35582
|
entitySlug: liveEntry.slug,
|
|
34810
35583
|
stashPath
|
|
34811
35584
|
},
|
|
34812
|
-
state
|
|
35585
|
+
state,
|
|
35586
|
+
base: base2
|
|
34813
35587
|
};
|
|
34814
35588
|
}
|
|
34815
35589
|
function writeCreatedIdBack(args) {
|
|
@@ -34920,7 +35694,8 @@ async function runPushCreate(args) {
|
|
|
34920
35694
|
fields,
|
|
34921
35695
|
pushed: true
|
|
34922
35696
|
},
|
|
34923
|
-
state: nextState2
|
|
35697
|
+
state: nextState2,
|
|
35698
|
+
base: args.state
|
|
34924
35699
|
};
|
|
34925
35700
|
}
|
|
34926
35701
|
return {
|
|
@@ -34929,7 +35704,8 @@ async function runPushCreate(args) {
|
|
|
34929
35704
|
entityType: strategy.type,
|
|
34930
35705
|
entitySlug: doc2.slug
|
|
34931
35706
|
},
|
|
34932
|
-
state: nextState2
|
|
35707
|
+
state: nextState2,
|
|
35708
|
+
base: args.state
|
|
34933
35709
|
};
|
|
34934
35710
|
}
|
|
34935
35711
|
if (createResult.reason === "context_doc_conflict") {
|
|
@@ -34978,7 +35754,8 @@ async function runPushCreate(args) {
|
|
|
34978
35754
|
entitySlug: args.slug,
|
|
34979
35755
|
stashPath
|
|
34980
35756
|
},
|
|
34981
|
-
state: nextState
|
|
35757
|
+
state: nextState,
|
|
35758
|
+
base: args.state
|
|
34982
35759
|
};
|
|
34983
35760
|
});
|
|
34984
35761
|
}
|
|
@@ -34997,7 +35774,7 @@ async function runPushDelete(args) {
|
|
|
34997
35774
|
const rel = relative5(workspaceMirrorPath(args.workspaceSlug), args.absolutePath);
|
|
34998
35775
|
parsedSlug = strategy.parseMirrorPath(rel)?.slug ?? null;
|
|
34999
35776
|
} catch {}
|
|
35000
|
-
const lookupSlug = parsedSlug ??
|
|
35777
|
+
const lookupSlug = parsedSlug ?? basename5(args.absolutePath, ".md");
|
|
35001
35778
|
const entry2 = getEntryBySlug(args.state, strategy.type, lookupSlug);
|
|
35002
35779
|
if (entry2) {
|
|
35003
35780
|
id = entry2.id;
|
|
@@ -35012,12 +35789,31 @@ async function runPushDelete(args) {
|
|
|
35012
35789
|
absolutePath: args.absolutePath,
|
|
35013
35790
|
reason: "untracked file — no cloud row to delete"
|
|
35014
35791
|
},
|
|
35015
|
-
state: args.state
|
|
35792
|
+
state: args.state,
|
|
35793
|
+
base: args.state
|
|
35016
35794
|
};
|
|
35017
35795
|
}
|
|
35018
35796
|
return args.mutex.run(id, async () => {
|
|
35019
|
-
const
|
|
35797
|
+
const snapshot = getEntry(args.state, strategy.type, id) ?? null;
|
|
35798
|
+
const live = snapshot ? committedEntryIfNewer(args.workspaceSlug, strategy.type, snapshot) ?? snapshot : null;
|
|
35799
|
+
if (existsSync4(args.absolutePath)) {
|
|
35800
|
+
args.output?.log(entityDeleteSuppressedLine({
|
|
35801
|
+
entityType: strategy.type,
|
|
35802
|
+
slug,
|
|
35803
|
+
reason: "path_exists"
|
|
35804
|
+
}));
|
|
35805
|
+
return {
|
|
35806
|
+
outcome: {
|
|
35807
|
+
kind: "ignored",
|
|
35808
|
+
absolutePath: args.absolutePath,
|
|
35809
|
+
reason: DELETE_DECLINED_PATH_EXISTS
|
|
35810
|
+
},
|
|
35811
|
+
state: args.state,
|
|
35812
|
+
base: args.state
|
|
35813
|
+
};
|
|
35814
|
+
}
|
|
35020
35815
|
const effectiveExpected = live?.updated_at ?? expectedUpdatedAt;
|
|
35816
|
+
const workingState = live && live !== snapshot ? upsertEntry(args.state, strategy.type, live).state : args.state;
|
|
35021
35817
|
const result = await deleteEntity({
|
|
35022
35818
|
strategy,
|
|
35023
35819
|
baseUrl: args.baseUrl,
|
|
@@ -35027,7 +35823,7 @@ async function runPushDelete(args) {
|
|
|
35027
35823
|
fetchImpl: args.fetchImpl
|
|
35028
35824
|
});
|
|
35029
35825
|
if (result.ok) {
|
|
35030
|
-
const nextState2 = dropEntry(
|
|
35826
|
+
const nextState2 = dropEntry(workingState, strategy.type, id);
|
|
35031
35827
|
args.output?.log(`[DELETE entityType=${strategy.type} slug=${slug} id=${id}]`);
|
|
35032
35828
|
return {
|
|
35033
35829
|
outcome: {
|
|
@@ -35035,11 +35831,12 @@ async function runPushDelete(args) {
|
|
|
35035
35831
|
entityType: strategy.type,
|
|
35036
35832
|
entitySlug: slug
|
|
35037
35833
|
},
|
|
35038
|
-
state: nextState2
|
|
35834
|
+
state: nextState2,
|
|
35835
|
+
base: workingState
|
|
35039
35836
|
};
|
|
35040
35837
|
}
|
|
35041
35838
|
if (result.code === 404) {
|
|
35042
|
-
const nextState2 = dropEntry(
|
|
35839
|
+
const nextState2 = dropEntry(workingState, strategy.type, id);
|
|
35043
35840
|
args.output?.log(`[DELETE entityType=${strategy.type} slug=${slug} id=${id} note=already_absent]`);
|
|
35044
35841
|
return {
|
|
35045
35842
|
outcome: {
|
|
@@ -35047,7 +35844,8 @@ async function runPushDelete(args) {
|
|
|
35047
35844
|
entityType: strategy.type,
|
|
35048
35845
|
entitySlug: slug
|
|
35049
35846
|
},
|
|
35050
|
-
state: nextState2
|
|
35847
|
+
state: nextState2,
|
|
35848
|
+
base: workingState
|
|
35051
35849
|
};
|
|
35052
35850
|
}
|
|
35053
35851
|
const nextState = await pullAndWriteMirror({
|
|
@@ -35069,7 +35867,8 @@ async function runPushDelete(args) {
|
|
|
35069
35867
|
entitySlug: slug,
|
|
35070
35868
|
stashPath: ""
|
|
35071
35869
|
},
|
|
35072
|
-
state: nextState
|
|
35870
|
+
state: nextState,
|
|
35871
|
+
base: workingState
|
|
35073
35872
|
};
|
|
35074
35873
|
});
|
|
35075
35874
|
}
|
|
@@ -35096,13 +35895,15 @@ async function pushRename(args) {
|
|
|
35096
35895
|
let toSlug;
|
|
35097
35896
|
try {
|
|
35098
35897
|
const rel = relative5(workspaceMirrorPath(workspaceSlug), toPath);
|
|
35099
|
-
toSlug = strategy.parseMirrorPath(rel)?.slug ??
|
|
35898
|
+
toSlug = strategy.parseMirrorPath(rel)?.slug ?? basename5(toPath, ".md");
|
|
35100
35899
|
} catch {
|
|
35101
|
-
toSlug =
|
|
35900
|
+
toSlug = basename5(toPath, ".md");
|
|
35102
35901
|
}
|
|
35103
35902
|
return mutex.run(id, async () => {
|
|
35104
|
-
const
|
|
35903
|
+
const snapshot = getEntry(state, entityType, id);
|
|
35904
|
+
const live = snapshot ? committedEntryIfNewer(workspaceSlug, entityType, snapshot) ?? snapshot : undefined;
|
|
35105
35905
|
const effectiveExpected = live?.updated_at ?? expectedUpdatedAt;
|
|
35906
|
+
const workingState = live && live !== snapshot ? upsertEntry(state, entityType, live).state : state;
|
|
35106
35907
|
let patchResult = await patchEntity({
|
|
35107
35908
|
strategy,
|
|
35108
35909
|
baseUrl,
|
|
@@ -35162,7 +35963,7 @@ async function pushRename(args) {
|
|
|
35162
35963
|
updated.body_len = renameHashBody.length;
|
|
35163
35964
|
} catch {}
|
|
35164
35965
|
}
|
|
35165
|
-
const nextState2 = persistEntry(
|
|
35966
|
+
const nextState2 = persistEntry(workingState, entityType, updated);
|
|
35166
35967
|
output?.log(`[RENAME entityType=${entityType} fromSlug=${fromSlug} toSlug=${doc2.slug} id=${doc2.id}]`);
|
|
35167
35968
|
return {
|
|
35168
35969
|
outcome: {
|
|
@@ -35171,13 +35972,15 @@ async function pushRename(args) {
|
|
|
35171
35972
|
fromSlug,
|
|
35172
35973
|
toSlug: doc2.slug
|
|
35173
35974
|
},
|
|
35174
|
-
state: nextState2
|
|
35975
|
+
state: nextState2,
|
|
35976
|
+
base: workingState
|
|
35175
35977
|
};
|
|
35176
35978
|
}
|
|
35177
35979
|
if (patchResult.code === 404) {
|
|
35178
35980
|
return {
|
|
35179
35981
|
outcome: { kind: "not_found", entityType, entitySlug: fromSlug },
|
|
35180
|
-
state
|
|
35982
|
+
state: workingState,
|
|
35983
|
+
base: workingState
|
|
35181
35984
|
};
|
|
35182
35985
|
}
|
|
35183
35986
|
if (patchResult.reason === "slug_conflict") {
|
|
@@ -35199,7 +36002,7 @@ async function pushRename(args) {
|
|
|
35199
36002
|
baseUrl,
|
|
35200
36003
|
pat,
|
|
35201
36004
|
id: patchResult.existing.id,
|
|
35202
|
-
state,
|
|
36005
|
+
state: workingState,
|
|
35203
36006
|
fetchImpl,
|
|
35204
36007
|
echo,
|
|
35205
36008
|
output,
|
|
@@ -35225,7 +36028,8 @@ async function pushRename(args) {
|
|
|
35225
36028
|
toSlug,
|
|
35226
36029
|
stashPath: stashPath2
|
|
35227
36030
|
},
|
|
35228
|
-
state: finalState
|
|
36031
|
+
state: finalState,
|
|
36032
|
+
base: workingState
|
|
35229
36033
|
};
|
|
35230
36034
|
}
|
|
35231
36035
|
let contents = "";
|
|
@@ -35246,7 +36050,7 @@ async function pushRename(args) {
|
|
|
35246
36050
|
baseUrl,
|
|
35247
36051
|
pat,
|
|
35248
36052
|
id,
|
|
35249
|
-
state,
|
|
36053
|
+
state: workingState,
|
|
35250
36054
|
fetchImpl,
|
|
35251
36055
|
echo,
|
|
35252
36056
|
output,
|
|
@@ -35259,7 +36063,8 @@ async function pushRename(args) {
|
|
|
35259
36063
|
entitySlug: fromSlug,
|
|
35260
36064
|
stashPath
|
|
35261
36065
|
},
|
|
35262
|
-
state: nextState
|
|
36066
|
+
state: nextState,
|
|
36067
|
+
base: workingState
|
|
35263
36068
|
};
|
|
35264
36069
|
});
|
|
35265
36070
|
}
|
|
@@ -35295,13 +36100,14 @@ async function applyBatch(batch, deps, mutex) {
|
|
|
35295
36100
|
await applyFileOp(op, deps);
|
|
35296
36101
|
continue;
|
|
35297
36102
|
}
|
|
36103
|
+
const base2 = deps.getState();
|
|
35298
36104
|
const result = await pushFile({
|
|
35299
36105
|
workspaceSlug: deps.workspaceSlug,
|
|
35300
36106
|
baseUrl: deps.baseUrl,
|
|
35301
36107
|
pat: deps.pat,
|
|
35302
36108
|
absolutePath: opPath(deps.workspaceSlug, op),
|
|
35303
36109
|
entityType: op.entityType,
|
|
35304
|
-
state:
|
|
36110
|
+
state: base2,
|
|
35305
36111
|
exists: op.kind === "overwrite",
|
|
35306
36112
|
unlinkContext: op.kind === "delete" ? { id: op.id, slug: op.slug, updated_at: op.updated_at } : null,
|
|
35307
36113
|
deps: {
|
|
@@ -35312,7 +36118,10 @@ async function applyBatch(batch, deps, mutex) {
|
|
|
35312
36118
|
echo: deps.echo
|
|
35313
36119
|
}
|
|
35314
36120
|
});
|
|
35315
|
-
deps.
|
|
36121
|
+
deps.applyState(result.base ?? base2, result.state);
|
|
36122
|
+
if (result.outcome.kind === "ignored" && result.outcome.reason === DELETE_DECLINED_PATH_EXISTS) {
|
|
36123
|
+
deps.output?.warn(deleteDeclinedSentence(opPath(deps.workspaceSlug, op)));
|
|
36124
|
+
}
|
|
35316
36125
|
} catch (err) {
|
|
35317
36126
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
35318
36127
|
deps.output?.warn(`Apply failed for ${opLabel(op)}: ${msg} (left for the next pull to reconcile).`);
|
|
@@ -35322,27 +36131,29 @@ async function applyBatch(batch, deps, mutex) {
|
|
|
35322
36131
|
async function applyFileOp(op, deps) {
|
|
35323
36132
|
const fileDeps = { fetchImpl: deps.fetchImpl, now: deps.now, output: deps.output };
|
|
35324
36133
|
if (op.kind === "delete") {
|
|
36134
|
+
const base3 = deps.getState();
|
|
35325
36135
|
const result2 = await pushFilesDelete({
|
|
35326
36136
|
workspaceSlug: deps.workspaceSlug,
|
|
35327
36137
|
baseUrl: deps.baseUrl,
|
|
35328
36138
|
pat: deps.pat,
|
|
35329
36139
|
relPath: op.path,
|
|
35330
|
-
state:
|
|
36140
|
+
state: base3,
|
|
35331
36141
|
deps: fileDeps
|
|
35332
36142
|
});
|
|
35333
|
-
deps.
|
|
36143
|
+
deps.applyState(base3, result2.state);
|
|
35334
36144
|
return;
|
|
35335
36145
|
}
|
|
36146
|
+
const base2 = deps.getState();
|
|
35336
36147
|
const result = await pushFilesUpsert({
|
|
35337
36148
|
workspaceSlug: deps.workspaceSlug,
|
|
35338
36149
|
baseUrl: deps.baseUrl,
|
|
35339
36150
|
pat: deps.pat,
|
|
35340
36151
|
absolutePath: filesSubtreePath(deps.workspaceSlug, op.path),
|
|
35341
36152
|
relPath: op.path,
|
|
35342
|
-
state:
|
|
36153
|
+
state: base2,
|
|
35343
36154
|
deps: fileDeps
|
|
35344
36155
|
});
|
|
35345
|
-
deps.
|
|
36156
|
+
deps.applyState(base2, result.state);
|
|
35346
36157
|
}
|
|
35347
36158
|
async function discardBatchOps(batch, deps) {
|
|
35348
36159
|
for (const op of batch.ops) {
|
|
@@ -35371,19 +36182,20 @@ async function discardBatchOps(batch, deps) {
|
|
|
35371
36182
|
});
|
|
35372
36183
|
}
|
|
35373
36184
|
}
|
|
36185
|
+
const base2 = deps.getState();
|
|
35374
36186
|
const next = await pullAndWriteMirror({
|
|
35375
36187
|
strategy,
|
|
35376
36188
|
workspaceSlug: deps.workspaceSlug,
|
|
35377
36189
|
baseUrl: deps.baseUrl,
|
|
35378
36190
|
pat: deps.pat,
|
|
35379
36191
|
id: op.id,
|
|
35380
|
-
state:
|
|
36192
|
+
state: base2,
|
|
35381
36193
|
fetchImpl: deps.fetchImpl,
|
|
35382
36194
|
echo: deps.echo,
|
|
35383
36195
|
output: deps.output,
|
|
35384
36196
|
now: deps.now
|
|
35385
36197
|
});
|
|
35386
|
-
deps.
|
|
36198
|
+
deps.applyState(base2, next);
|
|
35387
36199
|
} catch (err) {
|
|
35388
36200
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
35389
36201
|
deps.output?.warn(`Discard restore failed for ${opLabel(op)}: ${msg} (the next pull will reconcile).`);
|
|
@@ -35407,17 +36219,18 @@ async function discardFileOp(op, deps) {
|
|
|
35407
36219
|
side: "local"
|
|
35408
36220
|
});
|
|
35409
36221
|
}
|
|
36222
|
+
const base2 = deps.getState();
|
|
35410
36223
|
const next = await restoreFileFromCloud({
|
|
35411
36224
|
workspaceSlug: deps.workspaceSlug,
|
|
35412
36225
|
baseUrl: deps.baseUrl,
|
|
35413
36226
|
pat: deps.pat,
|
|
35414
36227
|
id: op.id,
|
|
35415
36228
|
relPath: op.path,
|
|
35416
|
-
state:
|
|
36229
|
+
state: base2,
|
|
35417
36230
|
fetchImpl: deps.fetchImpl,
|
|
35418
36231
|
output: deps.output
|
|
35419
36232
|
});
|
|
35420
|
-
deps.
|
|
36233
|
+
deps.applyState(base2, next);
|
|
35421
36234
|
}
|
|
35422
36235
|
function opPath(workspaceSlug, op) {
|
|
35423
36236
|
try {
|
|
@@ -35430,7 +36243,7 @@ function opPath(workspaceSlug, op) {
|
|
|
35430
36243
|
}
|
|
35431
36244
|
|
|
35432
36245
|
// src/lib/files/classify.ts
|
|
35433
|
-
import { relative as relative6, sep as
|
|
36246
|
+
import { relative as relative6, sep as sep5 } from "node:path";
|
|
35434
36247
|
var FILES_SUBTREE = "files";
|
|
35435
36248
|
function classifyFilesPath(args) {
|
|
35436
36249
|
let root;
|
|
@@ -35440,20 +36253,20 @@ function classifyFilesPath(args) {
|
|
|
35440
36253
|
return null;
|
|
35441
36254
|
}
|
|
35442
36255
|
const rel = relative6(root, args.absolutePath);
|
|
35443
|
-
if (rel.startsWith("..") || rel.startsWith(
|
|
36256
|
+
if (rel.startsWith("..") || rel.startsWith(sep5))
|
|
35444
36257
|
return null;
|
|
35445
|
-
const prefix = FILES_SUBTREE +
|
|
36258
|
+
const prefix = FILES_SUBTREE + sep5;
|
|
35446
36259
|
if (!rel.startsWith(prefix))
|
|
35447
36260
|
return null;
|
|
35448
36261
|
const sub = rel.slice(prefix.length);
|
|
35449
36262
|
if (sub.length === 0)
|
|
35450
36263
|
return null;
|
|
35451
|
-
const posix =
|
|
36264
|
+
const posix = sep5 === "/" ? sub : sub.split(sep5).join("/");
|
|
35452
36265
|
return validateFilesRelPath(posix) ? { relPath: posix } : null;
|
|
35453
36266
|
}
|
|
35454
36267
|
|
|
35455
36268
|
// src/lib/memory-facts/sync.ts
|
|
35456
|
-
import { isAbsolute as isAbsolute2, relative as relative7, sep as
|
|
36269
|
+
import { isAbsolute as isAbsolute2, relative as relative7, sep as sep6 } from "node:path";
|
|
35457
36270
|
|
|
35458
36271
|
// src/lib/memory-facts/api.ts
|
|
35459
36272
|
var FactCommonSchema = exports_external.object({
|
|
@@ -35774,7 +36587,7 @@ function classifyMemoryFactPath(args) {
|
|
|
35774
36587
|
const rel = relative7(root, args.absolutePath);
|
|
35775
36588
|
if (rel === "" || rel.startsWith("..") || isAbsolute2(rel))
|
|
35776
36589
|
return null;
|
|
35777
|
-
const mirrorRel = `${MEMORY_FACTS_DIR}/${rel.split(
|
|
36590
|
+
const mirrorRel = `${MEMORY_FACTS_DIR}/${rel.split(sep6).join("/")}`;
|
|
35778
36591
|
return { relPath: mirrorRel, fact: parseMemoryFactRelPath(mirrorRel) };
|
|
35779
36592
|
}
|
|
35780
36593
|
function classifyMemoryFactEvent(args) {
|
|
@@ -35933,7 +36746,7 @@ class FileDestructiveGate {
|
|
|
35933
36746
|
import { readFileSync as readFileSync19, statSync as statSync10 } from "node:fs";
|
|
35934
36747
|
|
|
35935
36748
|
// src/lib/unsynced.ts
|
|
35936
|
-
import { join as
|
|
36749
|
+
import { join as join13, relative as relative8 } from "node:path";
|
|
35937
36750
|
var RECORDABLE_REASONS = new Set([
|
|
35938
36751
|
"rate_limit",
|
|
35939
36752
|
"server",
|
|
@@ -35978,7 +36791,7 @@ function recordFileUnsynced(args) {
|
|
|
35978
36791
|
});
|
|
35979
36792
|
}
|
|
35980
36793
|
async function drainUnsyncedOps(args) {
|
|
35981
|
-
const { workspaceSlug, baseUrl, pat, getState,
|
|
36794
|
+
const { workspaceSlug, baseUrl, pat, getState, applyState, fetchImpl, mutex, output, echo } = args;
|
|
35982
36795
|
const now = args.now ?? (() => new Date);
|
|
35983
36796
|
const ops = listUnsynced(getState());
|
|
35984
36797
|
let attempted = 0;
|
|
@@ -35989,51 +36802,59 @@ async function drainUnsyncedOps(args) {
|
|
|
35989
36802
|
const before = getState();
|
|
35990
36803
|
const { state: next, changed } = removeUnsynced(before, target, path5);
|
|
35991
36804
|
if (changed)
|
|
35992
|
-
|
|
36805
|
+
applyState(before, commitState(before, next));
|
|
35993
36806
|
};
|
|
35994
36807
|
for (const op of ops) {
|
|
35995
36808
|
attempted += 1;
|
|
35996
36809
|
try {
|
|
35997
36810
|
if (op.target === "entity") {
|
|
35998
|
-
const absolutePath =
|
|
36811
|
+
const absolutePath = join13(workspaceMirrorPath(workspaceSlug), op.path);
|
|
35999
36812
|
const classified = classifyMirrorPath({ workspaceSlug, absolutePath });
|
|
36000
36813
|
if (!classified) {
|
|
36001
36814
|
clear("entity", op.path);
|
|
36002
36815
|
resolved += 1;
|
|
36003
36816
|
continue;
|
|
36004
36817
|
}
|
|
36818
|
+
const base2 = getState();
|
|
36005
36819
|
const result = await pushFile({
|
|
36006
36820
|
workspaceSlug,
|
|
36007
36821
|
baseUrl,
|
|
36008
36822
|
pat,
|
|
36009
36823
|
absolutePath,
|
|
36010
36824
|
entityType: classified.entityType,
|
|
36011
|
-
state:
|
|
36825
|
+
state: base2,
|
|
36012
36826
|
exists: op.op === "upsert",
|
|
36013
36827
|
unlinkContext: null,
|
|
36014
36828
|
deps: { fetchImpl, now, mutex, output, echo }
|
|
36015
36829
|
});
|
|
36016
|
-
|
|
36830
|
+
applyState(result.base ?? base2, result.state);
|
|
36831
|
+
if (result.outcome.kind === "ignored" && result.outcome.reason === DELETE_DECLINED_PATH_EXISTS) {
|
|
36832
|
+
output?.warn(deleteDeclinedSentence(op.path));
|
|
36833
|
+
}
|
|
36017
36834
|
} else {
|
|
36018
36835
|
const absolutePath = filesSubtreePath(workspaceSlug, op.path);
|
|
36019
|
-
|
|
36020
|
-
|
|
36021
|
-
|
|
36022
|
-
|
|
36023
|
-
|
|
36024
|
-
|
|
36025
|
-
|
|
36026
|
-
|
|
36027
|
-
|
|
36028
|
-
|
|
36029
|
-
|
|
36030
|
-
|
|
36031
|
-
|
|
36032
|
-
|
|
36033
|
-
|
|
36034
|
-
|
|
36836
|
+
let base2;
|
|
36837
|
+
const doFile = () => {
|
|
36838
|
+
base2 = getState();
|
|
36839
|
+
return op.op === "delete" ? pushFilesDelete({
|
|
36840
|
+
workspaceSlug,
|
|
36841
|
+
baseUrl,
|
|
36842
|
+
pat,
|
|
36843
|
+
relPath: op.path,
|
|
36844
|
+
state: base2,
|
|
36845
|
+
deps: { fetchImpl, now, output }
|
|
36846
|
+
}) : pushFilesUpsert({
|
|
36847
|
+
workspaceSlug,
|
|
36848
|
+
baseUrl,
|
|
36849
|
+
pat,
|
|
36850
|
+
absolutePath,
|
|
36851
|
+
relPath: op.path,
|
|
36852
|
+
state: base2,
|
|
36853
|
+
deps: { fetchImpl, now, output }
|
|
36854
|
+
});
|
|
36855
|
+
};
|
|
36035
36856
|
const result = mutex ? await mutex.run(`files:${op.path}`, doFile) : await doFile();
|
|
36036
|
-
|
|
36857
|
+
applyState(base2, result.state);
|
|
36037
36858
|
}
|
|
36038
36859
|
clear(op.target, op.path);
|
|
36039
36860
|
resolved += 1;
|
|
@@ -36053,7 +36874,7 @@ async function drainUnsyncedOps(args) {
|
|
|
36053
36874
|
message: cat.serverMessage,
|
|
36054
36875
|
rejected_at: now().toISOString()
|
|
36055
36876
|
});
|
|
36056
|
-
|
|
36877
|
+
applyState(beforeReject, commitState(beforeReject, next2));
|
|
36057
36878
|
continue;
|
|
36058
36879
|
}
|
|
36059
36880
|
stillFailing += 1;
|
|
@@ -36066,7 +36887,7 @@ async function drainUnsyncedOps(args) {
|
|
|
36066
36887
|
last_reason: cat.reason
|
|
36067
36888
|
});
|
|
36068
36889
|
if (changed)
|
|
36069
|
-
|
|
36890
|
+
applyState(beforeRetry, commitState(beforeRetry, next));
|
|
36070
36891
|
}
|
|
36071
36892
|
}
|
|
36072
36893
|
return { attempted, resolved, stillFailing, rejected };
|
|
@@ -36240,10 +37061,10 @@ class ReaddirpStream extends Readable {
|
|
|
36240
37061
|
}
|
|
36241
37062
|
async _formatEntry(dirent, path5) {
|
|
36242
37063
|
let entry2;
|
|
36243
|
-
const
|
|
37064
|
+
const basename6 = this._isDirent ? dirent.name : dirent;
|
|
36244
37065
|
try {
|
|
36245
|
-
const fullPath = presolve(pjoin(path5,
|
|
36246
|
-
entry2 = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
37066
|
+
const fullPath = presolve(pjoin(path5, basename6));
|
|
37067
|
+
entry2 = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
|
|
36247
37068
|
entry2[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
36248
37069
|
} catch (err) {
|
|
36249
37070
|
this._onError(err);
|
|
@@ -36770,9 +37591,9 @@ class NodeFsHandler {
|
|
|
36770
37591
|
_watchWithNodeFs(path5, listener) {
|
|
36771
37592
|
const opts = this.fsw.options;
|
|
36772
37593
|
const directory = sp.dirname(path5);
|
|
36773
|
-
const
|
|
37594
|
+
const basename7 = sp.basename(path5);
|
|
36774
37595
|
const parent = this.fsw._getWatchedDir(directory);
|
|
36775
|
-
parent.add(
|
|
37596
|
+
parent.add(basename7);
|
|
36776
37597
|
const absolutePath = sp.resolve(path5);
|
|
36777
37598
|
const options2 = {
|
|
36778
37599
|
persistent: opts.persistent
|
|
@@ -36782,7 +37603,7 @@ class NodeFsHandler {
|
|
|
36782
37603
|
let closer;
|
|
36783
37604
|
if (opts.usePolling) {
|
|
36784
37605
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
36785
|
-
options2.interval = enableBin && isBinaryPath(
|
|
37606
|
+
options2.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
|
|
36786
37607
|
closer = setFsWatchFileListener(path5, absolutePath, options2, {
|
|
36787
37608
|
listener,
|
|
36788
37609
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -36800,11 +37621,11 @@ class NodeFsHandler {
|
|
|
36800
37621
|
if (this.fsw.closed) {
|
|
36801
37622
|
return;
|
|
36802
37623
|
}
|
|
36803
|
-
const
|
|
36804
|
-
const
|
|
36805
|
-
const parent = this.fsw._getWatchedDir(
|
|
37624
|
+
const dirname11 = sp.dirname(file2);
|
|
37625
|
+
const basename7 = sp.basename(file2);
|
|
37626
|
+
const parent = this.fsw._getWatchedDir(dirname11);
|
|
36806
37627
|
let prevStats = stats;
|
|
36807
|
-
if (parent.has(
|
|
37628
|
+
if (parent.has(basename7))
|
|
36808
37629
|
return;
|
|
36809
37630
|
const listener = async (path5, newStats) => {
|
|
36810
37631
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
|
|
@@ -36829,9 +37650,9 @@ class NodeFsHandler {
|
|
|
36829
37650
|
prevStats = newStats2;
|
|
36830
37651
|
}
|
|
36831
37652
|
} catch (error52) {
|
|
36832
|
-
this.fsw._remove(
|
|
37653
|
+
this.fsw._remove(dirname11, basename7);
|
|
36833
37654
|
}
|
|
36834
|
-
} else if (parent.has(
|
|
37655
|
+
} else if (parent.has(basename7)) {
|
|
36835
37656
|
const at = newStats.atimeMs;
|
|
36836
37657
|
const mt = newStats.mtimeMs;
|
|
36837
37658
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -36918,7 +37739,7 @@ class NodeFsHandler {
|
|
|
36918
37739
|
this._addToNodeFs(path5, initialAdd, wh, depth + 1);
|
|
36919
37740
|
}
|
|
36920
37741
|
}).on(EV.ERROR, this._boundHandleError);
|
|
36921
|
-
return new Promise((
|
|
37742
|
+
return new Promise((resolve5, reject) => {
|
|
36922
37743
|
if (!stream)
|
|
36923
37744
|
return reject();
|
|
36924
37745
|
stream.once(STR_END, () => {
|
|
@@ -36927,7 +37748,7 @@ class NodeFsHandler {
|
|
|
36927
37748
|
return;
|
|
36928
37749
|
}
|
|
36929
37750
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
36930
|
-
|
|
37751
|
+
resolve5(undefined);
|
|
36931
37752
|
previous.getChildren().filter((item) => {
|
|
36932
37753
|
return item !== directory && !current.has(item);
|
|
36933
37754
|
}).forEach((item) => {
|
|
@@ -37686,7 +38507,7 @@ var chokidar_default = { watch, FSWatcher };
|
|
|
37686
38507
|
|
|
37687
38508
|
// src/lib/watcher.ts
|
|
37688
38509
|
import { statSync as statSync9 } from "node:fs";
|
|
37689
|
-
import { sep as
|
|
38510
|
+
import { sep as sep7 } from "node:path";
|
|
37690
38511
|
|
|
37691
38512
|
// src/lib/rename-classifier.ts
|
|
37692
38513
|
class RenameClassifier {
|
|
@@ -37817,7 +38638,7 @@ function watcherIgnoreMatcher(workspaceRoot) {
|
|
|
37817
38638
|
return (absolutePath) => patterns.some((re) => re.test(absolutePath));
|
|
37818
38639
|
}
|
|
37819
38640
|
function escSep() {
|
|
37820
|
-
return
|
|
38641
|
+
return sep7 === "/" ? "/" : "\\\\";
|
|
37821
38642
|
}
|
|
37822
38643
|
|
|
37823
38644
|
class DebounceMap {
|
|
@@ -38345,7 +39166,9 @@ async function pushArtistMemory(args) {
|
|
|
38345
39166
|
}
|
|
38346
39167
|
const localHash = stableHash(local);
|
|
38347
39168
|
const runPatch = async () => {
|
|
38348
|
-
|
|
39169
|
+
const snapshotEntry = getEntryBySlug(state, ENTITY_TYPE, slug);
|
|
39170
|
+
const fresher = args.mutex && snapshotEntry ? committedEntryIfNewer(workspaceSlug, ENTITY_TYPE, snapshotEntry) : undefined;
|
|
39171
|
+
let base2 = fresher ? upsertEntry(state, ENTITY_TYPE, fresher).state : state;
|
|
38349
39172
|
const entry2 = getEntryBySlug(base2, ENTITY_TYPE, slug);
|
|
38350
39173
|
if (!entry2)
|
|
38351
39174
|
return { state: base2, outcome: { kind: "ignored", slug } };
|
|
@@ -41474,7 +42297,7 @@ class Semaphore {
|
|
|
41474
42297
|
this.active += 1;
|
|
41475
42298
|
return;
|
|
41476
42299
|
}
|
|
41477
|
-
await new Promise((
|
|
42300
|
+
await new Promise((resolve6) => this.waiters.push(resolve6));
|
|
41478
42301
|
}
|
|
41479
42302
|
release() {
|
|
41480
42303
|
const next = this.waiters.shift();
|
|
@@ -41541,11 +42364,11 @@ import {
|
|
|
41541
42364
|
statSync as statSync16,
|
|
41542
42365
|
unlinkSync as unlinkSync8
|
|
41543
42366
|
} from "node:fs";
|
|
41544
|
-
import { join as
|
|
42367
|
+
import { join as join17 } from "node:path";
|
|
41545
42368
|
|
|
41546
42369
|
// src/lib/conflicts-view.ts
|
|
41547
42370
|
import { existsSync as existsSync6 } from "node:fs";
|
|
41548
|
-
import { join as
|
|
42371
|
+
import { join as join16 } from "node:path";
|
|
41549
42372
|
var NO_GENERATED_MEMORY = {
|
|
41550
42373
|
artistMemoryGenerated: () => false,
|
|
41551
42374
|
workspaceMemoryGenerated: () => false,
|
|
@@ -41591,7 +42414,7 @@ function resolveConflictRows(workspaceSlug, stashes, generatedMemory = NO_GENERA
|
|
|
41591
42414
|
if (validateFilesRelPath(rel) === null) {
|
|
41592
42415
|
return { kind: "unsafe-path", stash };
|
|
41593
42416
|
}
|
|
41594
|
-
const abs =
|
|
42417
|
+
const abs = join16(root, "files", rel);
|
|
41595
42418
|
if (hasSymlinkedComponent(workspaceSlug, abs)) {
|
|
41596
42419
|
return { kind: "unsafe-path", stash };
|
|
41597
42420
|
}
|
|
@@ -41724,7 +42547,7 @@ function refreshConflictsView(workspaceSlug) {
|
|
|
41724
42547
|
import { existsSync as existsSync7, readFileSync as readFileSync23, statSync as statSync14 } from "node:fs";
|
|
41725
42548
|
|
|
41726
42549
|
// src/lib/files/extracted-text.ts
|
|
41727
|
-
import { createHash as
|
|
42550
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
41728
42551
|
var DERIVED_TEXT_VERSION = 2;
|
|
41729
42552
|
function yamlString3(value) {
|
|
41730
42553
|
return JSON.stringify(value);
|
|
@@ -41798,7 +42621,7 @@ ${doc2.text}
|
|
|
41798
42621
|
`;
|
|
41799
42622
|
}
|
|
41800
42623
|
function extractedTextHash(rendered) {
|
|
41801
|
-
return
|
|
42624
|
+
return createHash4("sha256").update(rendered, "utf8").digest("hex");
|
|
41802
42625
|
}
|
|
41803
42626
|
|
|
41804
42627
|
// src/lib/files/pull.ts
|
|
@@ -42417,7 +43240,7 @@ function hasExistingContent(workspacePath) {
|
|
|
42417
43240
|
if (!e.isDirectory())
|
|
42418
43241
|
return true;
|
|
42419
43242
|
try {
|
|
42420
|
-
return readdirSync7(
|
|
43243
|
+
return readdirSync7(join17(workspacePath, e.name)).length > 0;
|
|
42421
43244
|
} catch {
|
|
42422
43245
|
return true;
|
|
42423
43246
|
}
|
|
@@ -43221,7 +44044,7 @@ function makeWatchHandler(deps) {
|
|
|
43221
44044
|
spinner,
|
|
43222
44045
|
mutex,
|
|
43223
44046
|
getState,
|
|
43224
|
-
|
|
44047
|
+
applyState,
|
|
43225
44048
|
fetchImpl,
|
|
43226
44049
|
now,
|
|
43227
44050
|
output,
|
|
@@ -43231,6 +44054,10 @@ function makeWatchHandler(deps) {
|
|
|
43231
44054
|
fileGate,
|
|
43232
44055
|
isFileFrozen
|
|
43233
44056
|
} = deps;
|
|
44057
|
+
const applyFromCurrent = (update) => {
|
|
44058
|
+
const base2 = getState();
|
|
44059
|
+
applyState(base2, update(base2));
|
|
44060
|
+
};
|
|
43234
44061
|
const handleFilesEvent = async (relPath, absolutePath, exists) => {
|
|
43235
44062
|
try {
|
|
43236
44063
|
if (isFileFrozen?.(relPath))
|
|
@@ -43240,8 +44067,8 @@ function makeWatchHandler(deps) {
|
|
|
43240
44067
|
} catch (err) {
|
|
43241
44068
|
if (!(err instanceof MirrorSymlinkError))
|
|
43242
44069
|
throw err;
|
|
43243
|
-
|
|
43244
|
-
state:
|
|
44070
|
+
applyFromCurrent((base2) => recordFileSymlinkRefused({
|
|
44071
|
+
state: base2,
|
|
43245
44072
|
relPath,
|
|
43246
44073
|
op: exists ? "upsert" : "delete",
|
|
43247
44074
|
now: now ?? (() => new Date)
|
|
@@ -43267,15 +44094,16 @@ function makeWatchHandler(deps) {
|
|
|
43267
44094
|
}
|
|
43268
44095
|
}
|
|
43269
44096
|
await mutex.run(`files:${relPath}`, async () => {
|
|
44097
|
+
const base2 = getState();
|
|
43270
44098
|
const result = await pushFilesDelete({
|
|
43271
44099
|
workspaceSlug,
|
|
43272
44100
|
baseUrl,
|
|
43273
44101
|
pat,
|
|
43274
44102
|
relPath,
|
|
43275
|
-
state:
|
|
44103
|
+
state: base2,
|
|
43276
44104
|
deps: { fetchImpl, now, output, suppressor }
|
|
43277
44105
|
});
|
|
43278
|
-
|
|
44106
|
+
applyState(base2, clearReportedSafeFileRejected(result.state, relPath));
|
|
43279
44107
|
reportFileOutcome(spinner, workspaceSlug, getState(), result.outcome);
|
|
43280
44108
|
});
|
|
43281
44109
|
return;
|
|
@@ -43300,8 +44128,8 @@ function makeWatchHandler(deps) {
|
|
|
43300
44128
|
max: MAX_WORKSPACE_FILE_BYTES,
|
|
43301
44129
|
source: "client"
|
|
43302
44130
|
}));
|
|
43303
|
-
|
|
43304
|
-
state:
|
|
44131
|
+
applyFromCurrent((base2) => recordFileOverCap({
|
|
44132
|
+
state: base2,
|
|
43305
44133
|
relPath,
|
|
43306
44134
|
size: st.size,
|
|
43307
44135
|
max: MAX_WORKSPACE_FILE_BYTES,
|
|
@@ -43329,17 +44157,18 @@ function makeWatchHandler(deps) {
|
|
|
43329
44157
|
}
|
|
43330
44158
|
}
|
|
43331
44159
|
await mutex.run(`files:${relPath}`, async () => {
|
|
44160
|
+
const base2 = getState();
|
|
43332
44161
|
const result = await pushFilesUpsert({
|
|
43333
44162
|
workspaceSlug,
|
|
43334
44163
|
baseUrl,
|
|
43335
44164
|
pat,
|
|
43336
44165
|
absolutePath,
|
|
43337
44166
|
relPath,
|
|
43338
|
-
state:
|
|
44167
|
+
state: base2,
|
|
43339
44168
|
prepared: { bytes, hash: hash2, size: st.size, inode: st.ino },
|
|
43340
44169
|
deps: { fetchImpl, now, output, suppressor }
|
|
43341
44170
|
});
|
|
43342
|
-
|
|
44171
|
+
applyState(base2, result.outcome.kind === "over_cap" ? result.state : clearFileRejected(result.state, relPath));
|
|
43343
44172
|
reportFileOutcome(spinner, workspaceSlug, getState(), result.outcome);
|
|
43344
44173
|
});
|
|
43345
44174
|
} catch (err) {
|
|
@@ -43347,16 +44176,16 @@ function makeWatchHandler(deps) {
|
|
|
43347
44176
|
output?.log(fileFailedLine({ path: relPath, ...categorizeError(err) }));
|
|
43348
44177
|
spinner.warn(`File push failed for files/${relPath}: ${msg}`);
|
|
43349
44178
|
if (isRecordableFailure(err)) {
|
|
43350
|
-
|
|
43351
|
-
state:
|
|
44179
|
+
applyFromCurrent((base2) => recordFileUnsynced({
|
|
44180
|
+
state: base2,
|
|
43352
44181
|
relPath,
|
|
43353
44182
|
op: exists ? "upsert" : "delete",
|
|
43354
44183
|
err,
|
|
43355
44184
|
now: now ?? (() => new Date)
|
|
43356
44185
|
}));
|
|
43357
44186
|
} else if (isPermanentReject(err)) {
|
|
43358
|
-
|
|
43359
|
-
state:
|
|
44187
|
+
applyFromCurrent((base2) => recordFileRejected({
|
|
44188
|
+
state: base2,
|
|
43360
44189
|
relPath,
|
|
43361
44190
|
op: exists ? "upsert" : "delete",
|
|
43362
44191
|
err,
|
|
@@ -43384,17 +44213,18 @@ function makeWatchHandler(deps) {
|
|
|
43384
44213
|
}
|
|
43385
44214
|
try {
|
|
43386
44215
|
await mutex.run(WORKSPACE_RULES_MUTEX_KEY, async () => {
|
|
44216
|
+
const base2 = getState();
|
|
43387
44217
|
const result = await pushWorkspaceRules({
|
|
43388
44218
|
workspaceSlug,
|
|
43389
44219
|
baseUrl,
|
|
43390
44220
|
pat,
|
|
43391
|
-
state:
|
|
44221
|
+
state: base2,
|
|
43392
44222
|
fetchImpl,
|
|
43393
44223
|
now,
|
|
43394
44224
|
output,
|
|
43395
44225
|
suppressor
|
|
43396
44226
|
});
|
|
43397
|
-
|
|
44227
|
+
applyState(base2, result.state);
|
|
43398
44228
|
reportWorkspaceRulesOutcome(spinner, workspaceSlug, result.state, result.outcome);
|
|
43399
44229
|
});
|
|
43400
44230
|
} catch (err) {
|
|
@@ -43422,17 +44252,18 @@ function makeWatchHandler(deps) {
|
|
|
43422
44252
|
}
|
|
43423
44253
|
try {
|
|
43424
44254
|
await mutex.run(WORKSPACE_MEMORY_MUTEX_KEY, async () => {
|
|
44255
|
+
const base2 = getState();
|
|
43425
44256
|
const result = await pushWorkspaceMemory({
|
|
43426
44257
|
workspaceSlug,
|
|
43427
44258
|
baseUrl,
|
|
43428
44259
|
pat,
|
|
43429
|
-
state:
|
|
44260
|
+
state: base2,
|
|
43430
44261
|
fetchImpl,
|
|
43431
44262
|
now,
|
|
43432
44263
|
output,
|
|
43433
44264
|
suppressor
|
|
43434
44265
|
});
|
|
43435
|
-
|
|
44266
|
+
applyState(base2, result.state);
|
|
43436
44267
|
reportWorkspaceMemoryOutcome(spinner, workspaceSlug, result.state, result.outcome);
|
|
43437
44268
|
});
|
|
43438
44269
|
} catch (err) {
|
|
@@ -43460,17 +44291,18 @@ function makeWatchHandler(deps) {
|
|
|
43460
44291
|
}
|
|
43461
44292
|
try {
|
|
43462
44293
|
await mutex.run(USER_MEMORY_MUTEX_KEY, async () => {
|
|
44294
|
+
const base2 = getState();
|
|
43463
44295
|
const result = await pushUserMemory({
|
|
43464
44296
|
workspaceSlug,
|
|
43465
44297
|
baseUrl,
|
|
43466
44298
|
pat,
|
|
43467
|
-
state:
|
|
44299
|
+
state: base2,
|
|
43468
44300
|
fetchImpl,
|
|
43469
44301
|
now,
|
|
43470
44302
|
output,
|
|
43471
44303
|
suppressor
|
|
43472
44304
|
});
|
|
43473
|
-
|
|
44305
|
+
applyState(base2, result.state);
|
|
43474
44306
|
reportUserMemoryOutcome(spinner, workspaceSlug, result.state, result.outcome);
|
|
43475
44307
|
});
|
|
43476
44308
|
} catch (err) {
|
|
@@ -43498,17 +44330,18 @@ function makeWatchHandler(deps) {
|
|
|
43498
44330
|
}
|
|
43499
44331
|
try {
|
|
43500
44332
|
await mutex.run(VOICE_MUTEX_KEY, async () => {
|
|
44333
|
+
const base2 = getState();
|
|
43501
44334
|
const result = await pushVoice({
|
|
43502
44335
|
workspaceSlug,
|
|
43503
44336
|
baseUrl,
|
|
43504
44337
|
pat,
|
|
43505
|
-
state:
|
|
44338
|
+
state: base2,
|
|
43506
44339
|
fetchImpl,
|
|
43507
44340
|
now,
|
|
43508
44341
|
output,
|
|
43509
44342
|
suppressor
|
|
43510
44343
|
});
|
|
43511
|
-
|
|
44344
|
+
applyState(base2, result.state);
|
|
43512
44345
|
reportVoiceOutcome(spinner, workspaceSlug, result.state, result.outcome);
|
|
43513
44346
|
});
|
|
43514
44347
|
} catch (err) {
|
|
@@ -43535,19 +44368,20 @@ function makeWatchHandler(deps) {
|
|
|
43535
44368
|
}
|
|
43536
44369
|
}
|
|
43537
44370
|
try {
|
|
44371
|
+
const base2 = getState();
|
|
43538
44372
|
const result = await pushArtistMemory({
|
|
43539
44373
|
workspaceSlug,
|
|
43540
44374
|
baseUrl,
|
|
43541
44375
|
pat,
|
|
43542
44376
|
slug,
|
|
43543
|
-
state:
|
|
44377
|
+
state: base2,
|
|
43544
44378
|
fetchImpl,
|
|
43545
44379
|
now,
|
|
43546
44380
|
output,
|
|
43547
44381
|
suppressor,
|
|
43548
44382
|
mutex
|
|
43549
44383
|
});
|
|
43550
|
-
|
|
44384
|
+
applyState(base2, result.state);
|
|
43551
44385
|
reportArtistMemoryOutcome(spinner, workspaceSlug, result.state, result.outcome);
|
|
43552
44386
|
} catch (err) {
|
|
43553
44387
|
const msg = err instanceof Error ? err.message : "unknown push error";
|
|
@@ -43580,6 +44414,7 @@ function makeWatchHandler(deps) {
|
|
|
43580
44414
|
await handle({ kind: "upsert", absolutePath: event.toPath });
|
|
43581
44415
|
return;
|
|
43582
44416
|
}
|
|
44417
|
+
const base3 = getState();
|
|
43583
44418
|
const result2 = await pushRename({
|
|
43584
44419
|
workspaceSlug,
|
|
43585
44420
|
baseUrl,
|
|
@@ -43590,7 +44425,7 @@ function makeWatchHandler(deps) {
|
|
|
43590
44425
|
id: event.id,
|
|
43591
44426
|
expectedUpdatedAt: event.expectedUpdatedAt,
|
|
43592
44427
|
fromSlug: event.fromSlug,
|
|
43593
|
-
state:
|
|
44428
|
+
state: base3,
|
|
43594
44429
|
deps: {
|
|
43595
44430
|
fetchImpl,
|
|
43596
44431
|
now,
|
|
@@ -43599,7 +44434,7 @@ function makeWatchHandler(deps) {
|
|
|
43599
44434
|
echo: suppressor ? { watcher: "live", suppressor } : undefined
|
|
43600
44435
|
}
|
|
43601
44436
|
});
|
|
43602
|
-
|
|
44437
|
+
applyState(base3, result2.state);
|
|
43603
44438
|
reportOutcome(spinner, workspaceSlug, result2.state, result2.outcome);
|
|
43604
44439
|
return;
|
|
43605
44440
|
}
|
|
@@ -43658,8 +44493,8 @@ function makeWatchHandler(deps) {
|
|
|
43658
44493
|
if (!classified) {
|
|
43659
44494
|
const key = entitySymlinkRefusalKey({ workspaceSlug, absolutePath });
|
|
43660
44495
|
if (key) {
|
|
43661
|
-
|
|
43662
|
-
state:
|
|
44496
|
+
applyFromCurrent((base3) => recordEntitySymlinkRefused({
|
|
44497
|
+
state: base3,
|
|
43663
44498
|
mirrorRelativePath: key,
|
|
43664
44499
|
op: event.kind === "unlink" ? "delete" : "upsert",
|
|
43665
44500
|
now: now ?? (() => new Date)
|
|
@@ -43716,13 +44551,14 @@ function makeWatchHandler(deps) {
|
|
|
43716
44551
|
return;
|
|
43717
44552
|
}
|
|
43718
44553
|
}
|
|
44554
|
+
const base2 = getState();
|
|
43719
44555
|
const result = await pushFile({
|
|
43720
44556
|
workspaceSlug,
|
|
43721
44557
|
baseUrl,
|
|
43722
44558
|
pat,
|
|
43723
44559
|
absolutePath,
|
|
43724
44560
|
entityType: classified.entityType,
|
|
43725
|
-
state:
|
|
44561
|
+
state: base2,
|
|
43726
44562
|
exists: event.kind === "upsert",
|
|
43727
44563
|
unlinkContext: event.kind === "unlink" && event.stateEntry ? {
|
|
43728
44564
|
id: event.stateEntry.id,
|
|
@@ -43738,7 +44574,7 @@ function makeWatchHandler(deps) {
|
|
|
43738
44574
|
echo: suppressor ? { watcher: "live", suppressor } : undefined
|
|
43739
44575
|
}
|
|
43740
44576
|
});
|
|
43741
|
-
|
|
44577
|
+
applyState(result.base ?? base2, clearEntityRejected(result.state, workspaceSlug, absolutePath));
|
|
43742
44578
|
reportOutcome(spinner, workspaceSlug, getState(), result.outcome);
|
|
43743
44579
|
} catch (err) {
|
|
43744
44580
|
const msg = err instanceof Error ? err.message : "unknown push error";
|
|
@@ -43754,8 +44590,8 @@ function makeWatchHandler(deps) {
|
|
|
43754
44590
|
}));
|
|
43755
44591
|
}
|
|
43756
44592
|
if (classified && event.kind !== "renamed" && isRecordableFailure(err)) {
|
|
43757
|
-
|
|
43758
|
-
state:
|
|
44593
|
+
applyFromCurrent((base2) => recordEntityUnsynced({
|
|
44594
|
+
state: base2,
|
|
43759
44595
|
workspaceSlug,
|
|
43760
44596
|
absolutePath: path5,
|
|
43761
44597
|
op: event.kind === "unlink" ? "delete" : "upsert",
|
|
@@ -43763,8 +44599,8 @@ function makeWatchHandler(deps) {
|
|
|
43763
44599
|
now: now ?? (() => new Date)
|
|
43764
44600
|
}));
|
|
43765
44601
|
} else if (classified && event.kind !== "renamed" && isPermanentReject(err)) {
|
|
43766
|
-
|
|
43767
|
-
state:
|
|
44602
|
+
applyFromCurrent((base2) => recordEntityRejected({
|
|
44603
|
+
state: base2,
|
|
43768
44604
|
workspaceSlug,
|
|
43769
44605
|
absolutePath: path5,
|
|
43770
44606
|
op: event.kind === "unlink" ? "delete" : "upsert",
|
|
@@ -43783,6 +44619,7 @@ function discardSink() {
|
|
|
43783
44619
|
}
|
|
43784
44620
|
async function flushHeldEntityOp(deps) {
|
|
43785
44621
|
const now = deps.now ?? (() => new Date);
|
|
44622
|
+
const base2 = deps.getState();
|
|
43786
44623
|
try {
|
|
43787
44624
|
const result = await pushFile({
|
|
43788
44625
|
workspaceSlug: deps.workspaceSlug,
|
|
@@ -43790,7 +44627,7 @@ async function flushHeldEntityOp(deps) {
|
|
|
43790
44627
|
pat: deps.pat,
|
|
43791
44628
|
absolutePath: deps.op.absolutePath,
|
|
43792
44629
|
entityType: deps.op.entityType,
|
|
43793
|
-
state:
|
|
44630
|
+
state: base2,
|
|
43794
44631
|
exists: deps.op.kind === "overwrite",
|
|
43795
44632
|
unlinkContext: deps.op.kind === "delete" ? { id: deps.op.id, slug: deps.op.slug, updated_at: deps.op.updatedAt } : null,
|
|
43796
44633
|
deps: {
|
|
@@ -43801,7 +44638,10 @@ async function flushHeldEntityOp(deps) {
|
|
|
43801
44638
|
echo: deps.echo
|
|
43802
44639
|
}
|
|
43803
44640
|
});
|
|
43804
|
-
deps.
|
|
44641
|
+
deps.applyState(result.base ?? base2, result.state);
|
|
44642
|
+
if (result.outcome.kind === "ignored" && result.outcome.reason === DELETE_DECLINED_PATH_EXISTS) {
|
|
44643
|
+
deps.spinner.warn(deleteDeclinedSentence(deps.op.absolutePath));
|
|
44644
|
+
}
|
|
43805
44645
|
reportOutcome(deps.spinner, deps.workspaceSlug, deps.getState(), result.outcome);
|
|
43806
44646
|
} catch (err) {
|
|
43807
44647
|
const msg = err instanceof Error ? err.message : "unknown push error";
|
|
@@ -43813,8 +44653,8 @@ async function flushHeldEntityOp(deps) {
|
|
|
43813
44653
|
}));
|
|
43814
44654
|
deps.spinner.warn(`Push failed for ${deps.op.entityType}/${deps.op.slug}: ${msg}`);
|
|
43815
44655
|
if (isRecordableFailure(err)) {
|
|
43816
|
-
deps.
|
|
43817
|
-
state:
|
|
44656
|
+
deps.applyState(base2, recordEntityUnsynced({
|
|
44657
|
+
state: base2,
|
|
43818
44658
|
workspaceSlug: deps.workspaceSlug,
|
|
43819
44659
|
absolutePath: deps.op.absolutePath,
|
|
43820
44660
|
op: deps.op.kind === "delete" ? "delete" : "upsert",
|
|
@@ -43832,14 +44672,16 @@ async function flushHeldFileOpImpl(deps) {
|
|
|
43832
44672
|
output: deps.output,
|
|
43833
44673
|
suppressor: deps.suppressor
|
|
43834
44674
|
};
|
|
44675
|
+
let base2;
|
|
43835
44676
|
const run = async () => {
|
|
44677
|
+
base2 = deps.getState();
|
|
43836
44678
|
try {
|
|
43837
44679
|
const result = deps.op.kind === "delete" ? await pushFilesDelete({
|
|
43838
44680
|
workspaceSlug: deps.workspaceSlug,
|
|
43839
44681
|
baseUrl: deps.baseUrl,
|
|
43840
44682
|
pat: deps.pat,
|
|
43841
44683
|
relPath: deps.op.relPath,
|
|
43842
|
-
state:
|
|
44684
|
+
state: base2,
|
|
43843
44685
|
deps: fileDeps
|
|
43844
44686
|
}) : await pushFilesUpsert({
|
|
43845
44687
|
workspaceSlug: deps.workspaceSlug,
|
|
@@ -43847,18 +44689,18 @@ async function flushHeldFileOpImpl(deps) {
|
|
|
43847
44689
|
pat: deps.pat,
|
|
43848
44690
|
absolutePath: deps.op.absolutePath,
|
|
43849
44691
|
relPath: deps.op.relPath,
|
|
43850
|
-
state:
|
|
44692
|
+
state: base2,
|
|
43851
44693
|
deps: fileDeps
|
|
43852
44694
|
});
|
|
43853
|
-
deps.
|
|
44695
|
+
deps.applyState(base2, result.state);
|
|
43854
44696
|
reportFileOutcome(deps.spinner, deps.workspaceSlug, deps.getState(), result.outcome);
|
|
43855
44697
|
} catch (err) {
|
|
43856
44698
|
const msg = err instanceof Error ? err.message : "unknown push error";
|
|
43857
44699
|
deps.output?.log(fileFailedLine({ path: deps.op.relPath, ...categorizeError(err) }));
|
|
43858
44700
|
deps.spinner.warn(`File push failed for files/${deps.op.relPath}: ${msg}`);
|
|
43859
44701
|
if (isRecordableFailure(err)) {
|
|
43860
|
-
deps.
|
|
43861
|
-
state:
|
|
44702
|
+
deps.applyState(base2, recordFileUnsynced({
|
|
44703
|
+
state: base2,
|
|
43862
44704
|
relPath: deps.op.relPath,
|
|
43863
44705
|
op: deps.op.kind === "delete" ? "delete" : "upsert",
|
|
43864
44706
|
err,
|
|
@@ -44153,8 +44995,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44153
44995
|
baseUrl: entry2.api_base_url,
|
|
44154
44996
|
pat,
|
|
44155
44997
|
getState: () => state,
|
|
44156
|
-
|
|
44157
|
-
state =
|
|
44998
|
+
applyState: (base2, next) => {
|
|
44999
|
+
state = rebaseState(state, base2, next);
|
|
44158
45000
|
},
|
|
44159
45001
|
fetchImpl,
|
|
44160
45002
|
now: deps.now,
|
|
@@ -44171,8 +45013,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44171
45013
|
baseUrl: entry2.api_base_url,
|
|
44172
45014
|
pat,
|
|
44173
45015
|
getState: () => state,
|
|
44174
|
-
|
|
44175
|
-
state =
|
|
45016
|
+
applyState: (base2, next) => {
|
|
45017
|
+
state = rebaseState(state, base2, next);
|
|
44176
45018
|
},
|
|
44177
45019
|
fetchImpl,
|
|
44178
45020
|
now: deps.now,
|
|
@@ -44210,8 +45052,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44210
45052
|
pat,
|
|
44211
45053
|
op,
|
|
44212
45054
|
getState: () => state,
|
|
44213
|
-
|
|
44214
|
-
state =
|
|
45055
|
+
applyState: (base2, next) => {
|
|
45056
|
+
state = rebaseState(state, base2, next);
|
|
44215
45057
|
},
|
|
44216
45058
|
mutex,
|
|
44217
45059
|
fetchImpl,
|
|
@@ -44274,8 +45116,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44274
45116
|
pat,
|
|
44275
45117
|
op,
|
|
44276
45118
|
getState: () => state,
|
|
44277
|
-
|
|
44278
|
-
state =
|
|
45119
|
+
applyState: (base2, next) => {
|
|
45120
|
+
state = rebaseState(state, base2, next);
|
|
44279
45121
|
},
|
|
44280
45122
|
mutex,
|
|
44281
45123
|
fetchImpl,
|
|
@@ -44331,8 +45173,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44331
45173
|
spinner,
|
|
44332
45174
|
mutex,
|
|
44333
45175
|
getState: () => state,
|
|
44334
|
-
|
|
44335
|
-
state =
|
|
45176
|
+
applyState: (base2, next) => {
|
|
45177
|
+
state = rebaseState(state, base2, next);
|
|
44336
45178
|
},
|
|
44337
45179
|
fetchImpl,
|
|
44338
45180
|
now: deps.now,
|
|
@@ -44397,8 +45239,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44397
45239
|
baseUrl: entry2.api_base_url,
|
|
44398
45240
|
pat,
|
|
44399
45241
|
getState: () => state,
|
|
44400
|
-
|
|
44401
|
-
state =
|
|
45242
|
+
applyState: (base2, next) => {
|
|
45243
|
+
state = rebaseState(state, base2, next);
|
|
44402
45244
|
},
|
|
44403
45245
|
fetchImpl,
|
|
44404
45246
|
now: deps.now,
|
|
@@ -44415,8 +45257,8 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44415
45257
|
baseUrl: entry2.api_base_url,
|
|
44416
45258
|
pat,
|
|
44417
45259
|
getState: () => state,
|
|
44418
|
-
|
|
44419
|
-
state =
|
|
45260
|
+
applyState: (base2, next) => {
|
|
45261
|
+
state = rebaseState(state, base2, next);
|
|
44420
45262
|
},
|
|
44421
45263
|
fetchImpl,
|
|
44422
45264
|
now: deps.now,
|
|
@@ -44431,58 +45273,70 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44431
45273
|
spinner.warn(`${redriven.rejected} write${redriven.rejected === 1 ? "" : "s"} permanently rejected — see \`plinth status\``);
|
|
44432
45274
|
spinner.update(`Watching ${slug} · ${watchingSummary(state)}`);
|
|
44433
45275
|
}
|
|
44434
|
-
|
|
44435
|
-
|
|
44436
|
-
|
|
44437
|
-
|
|
44438
|
-
|
|
44439
|
-
|
|
44440
|
-
|
|
44441
|
-
|
|
44442
|
-
|
|
44443
|
-
|
|
44444
|
-
|
|
44445
|
-
|
|
45276
|
+
let rulesBase = state;
|
|
45277
|
+
const rulesRecon = await mutex.run(WORKSPACE_RULES_MUTEX_KEY, () => {
|
|
45278
|
+
rulesBase = state;
|
|
45279
|
+
return reconcileWorkspaceRules({
|
|
45280
|
+
workspaceSlug: slug,
|
|
45281
|
+
baseUrl: entry2.api_base_url,
|
|
45282
|
+
pat,
|
|
45283
|
+
state: rulesBase,
|
|
45284
|
+
fetchImpl,
|
|
45285
|
+
now: deps.now,
|
|
45286
|
+
output: out,
|
|
45287
|
+
suppressor
|
|
45288
|
+
});
|
|
45289
|
+
});
|
|
45290
|
+
state = rebaseState(state, rulesBase, rulesRecon.state);
|
|
44446
45291
|
reportSingletonReconcile(spinner, slug, state, WORKSPACE_RULES_FILENAME, rulesRecon.outcome);
|
|
44447
|
-
|
|
44448
|
-
|
|
44449
|
-
|
|
44450
|
-
|
|
44451
|
-
|
|
44452
|
-
|
|
44453
|
-
|
|
44454
|
-
|
|
44455
|
-
|
|
44456
|
-
|
|
44457
|
-
|
|
44458
|
-
|
|
45292
|
+
let memoryBase = state;
|
|
45293
|
+
const memoryRecon = await mutex.run(WORKSPACE_MEMORY_MUTEX_KEY, () => {
|
|
45294
|
+
memoryBase = state;
|
|
45295
|
+
return reconcileWorkspaceMemory({
|
|
45296
|
+
workspaceSlug: slug,
|
|
45297
|
+
baseUrl: entry2.api_base_url,
|
|
45298
|
+
pat,
|
|
45299
|
+
state: memoryBase,
|
|
45300
|
+
fetchImpl,
|
|
45301
|
+
now: deps.now,
|
|
45302
|
+
output: out,
|
|
45303
|
+
suppressor
|
|
45304
|
+
});
|
|
45305
|
+
});
|
|
45306
|
+
state = rebaseState(state, memoryBase, memoryRecon.state);
|
|
44459
45307
|
reportSingletonReconcile(spinner, slug, state, WORKSPACE_MEMORY_FILENAME, memoryRecon.outcome);
|
|
44460
|
-
|
|
44461
|
-
|
|
44462
|
-
|
|
44463
|
-
|
|
44464
|
-
state,
|
|
44465
|
-
fetchImpl,
|
|
44466
|
-
now: deps.now,
|
|
44467
|
-
output: out,
|
|
44468
|
-
suppressor
|
|
44469
|
-
}));
|
|
44470
|
-
if (userMemoryRecon.state !== state)
|
|
44471
|
-
state = userMemoryRecon.state;
|
|
44472
|
-
reportSingletonReconcile(spinner, slug, state, USER_MEMORY_FILENAME, userMemoryRecon.outcome);
|
|
44473
|
-
try {
|
|
44474
|
-
const voiceRecon = await mutex.run(VOICE_MUTEX_KEY, () => reconcileVoice({
|
|
45308
|
+
let userMemoryBase = state;
|
|
45309
|
+
const userMemoryRecon = await mutex.run(USER_MEMORY_MUTEX_KEY, () => {
|
|
45310
|
+
userMemoryBase = state;
|
|
45311
|
+
return reconcileUserMemory({
|
|
44475
45312
|
workspaceSlug: slug,
|
|
44476
45313
|
baseUrl: entry2.api_base_url,
|
|
44477
45314
|
pat,
|
|
44478
|
-
state,
|
|
45315
|
+
state: userMemoryBase,
|
|
44479
45316
|
fetchImpl,
|
|
44480
45317
|
now: deps.now,
|
|
44481
45318
|
output: out,
|
|
44482
45319
|
suppressor
|
|
44483
|
-
})
|
|
44484
|
-
|
|
44485
|
-
|
|
45320
|
+
});
|
|
45321
|
+
});
|
|
45322
|
+
state = rebaseState(state, userMemoryBase, userMemoryRecon.state);
|
|
45323
|
+
reportSingletonReconcile(spinner, slug, state, USER_MEMORY_FILENAME, userMemoryRecon.outcome);
|
|
45324
|
+
try {
|
|
45325
|
+
let voiceBase = state;
|
|
45326
|
+
const voiceRecon = await mutex.run(VOICE_MUTEX_KEY, () => {
|
|
45327
|
+
voiceBase = state;
|
|
45328
|
+
return reconcileVoice({
|
|
45329
|
+
workspaceSlug: slug,
|
|
45330
|
+
baseUrl: entry2.api_base_url,
|
|
45331
|
+
pat,
|
|
45332
|
+
state: voiceBase,
|
|
45333
|
+
fetchImpl,
|
|
45334
|
+
now: deps.now,
|
|
45335
|
+
output: out,
|
|
45336
|
+
suppressor
|
|
45337
|
+
});
|
|
45338
|
+
});
|
|
45339
|
+
state = rebaseState(state, voiceBase, voiceRecon.state);
|
|
44486
45340
|
reportSingletonReconcile(spinner, slug, state, VOICE_FILENAME, voiceRecon.outcome);
|
|
44487
45341
|
const reconWarning = voiceMalformedWarning(state, slug);
|
|
44488
45342
|
if (reconWarning)
|
|
@@ -44490,19 +45344,19 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44490
45344
|
} catch (err) {
|
|
44491
45345
|
out.log(`[VOICE_RECONCILE_FAILED ${categorizeError(err).reason}]`);
|
|
44492
45346
|
}
|
|
45347
|
+
const memBase = state;
|
|
44493
45348
|
const memRecon = await reconcileArtistMemory({
|
|
44494
45349
|
workspaceSlug: slug,
|
|
44495
45350
|
baseUrl: entry2.api_base_url,
|
|
44496
45351
|
pat,
|
|
44497
|
-
state,
|
|
45352
|
+
state: memBase,
|
|
44498
45353
|
fetchImpl,
|
|
44499
45354
|
now: deps.now,
|
|
44500
45355
|
output: out,
|
|
44501
45356
|
suppressor,
|
|
44502
45357
|
mutex
|
|
44503
45358
|
});
|
|
44504
|
-
|
|
44505
|
-
state = memRecon.state;
|
|
45359
|
+
state = rebaseState(state, memBase, memRecon.state);
|
|
44506
45360
|
if (memRecon.pushed > 0) {
|
|
44507
45361
|
spinner.success(`↑ synced ${memRecon.pushed} memory file${memRecon.pushed === 1 ? "" : "s"}`);
|
|
44508
45362
|
spinner.update(`Watching ${slug} · ${watchingSummary(state)}`);
|
|
@@ -44596,12 +45450,12 @@ async function runStart(workspaceSlug, deps = {}) {
|
|
|
44596
45450
|
try {
|
|
44597
45451
|
await Promise.race([
|
|
44598
45452
|
watcher.drainPending().then(() => Promise.all([gate.drain(), fileGate.drain()])),
|
|
44599
|
-
new Promise((
|
|
45453
|
+
new Promise((resolve6) => setTimeout(() => resolve6(-1), SHUTDOWN_DRAIN_TIMEOUT_MS))
|
|
44600
45454
|
]);
|
|
44601
45455
|
} finally {
|
|
44602
45456
|
await Promise.race([
|
|
44603
45457
|
watcher.stop(),
|
|
44604
|
-
new Promise((
|
|
45458
|
+
new Promise((resolve6) => setTimeout(resolve6, SHUTDOWN_DRAIN_TIMEOUT_MS))
|
|
44605
45459
|
]);
|
|
44606
45460
|
clearDaemonPid(slug);
|
|
44607
45461
|
releaseDaemonLock(lockHandle);
|
|
@@ -45157,11 +46011,11 @@ function assessContextWalls(workspaceSlug, deps = {}) {
|
|
|
45157
46011
|
// src/lib/build-stamp.ts
|
|
45158
46012
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
45159
46013
|
import { existsSync as existsSync10, mkdirSync as mkdirSync11, readFileSync as readFileSync27, writeFileSync as writeFileSync8 } from "node:fs";
|
|
45160
|
-
import { dirname as
|
|
46014
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
45161
46015
|
import { fileURLToPath } from "node:url";
|
|
45162
46016
|
var BUILD_STAMP_FILENAME = "build-stamp.json";
|
|
45163
46017
|
function stampPath(distDir) {
|
|
45164
|
-
return
|
|
46018
|
+
return join18(distDir, BUILD_STAMP_FILENAME);
|
|
45165
46019
|
}
|
|
45166
46020
|
function readHead(repoDir) {
|
|
45167
46021
|
try {
|
|
@@ -45192,9 +46046,9 @@ function readBuildStamp(distDir) {
|
|
|
45192
46046
|
function findRepoRoot(fromDir) {
|
|
45193
46047
|
let dir = fromDir;
|
|
45194
46048
|
for (;; ) {
|
|
45195
|
-
if (existsSync10(
|
|
46049
|
+
if (existsSync10(join18(dir, ".git")))
|
|
45196
46050
|
return dir;
|
|
45197
|
-
const parent =
|
|
46051
|
+
const parent = dirname12(dir);
|
|
45198
46052
|
if (parent === dir)
|
|
45199
46053
|
return null;
|
|
45200
46054
|
dir = parent;
|
|
@@ -45218,7 +46072,7 @@ function checkBuildStaleness(deps = {}) {
|
|
|
45218
46072
|
const metaUrl = deps.metaUrl ?? import.meta.url;
|
|
45219
46073
|
let distDir;
|
|
45220
46074
|
try {
|
|
45221
|
-
distDir =
|
|
46075
|
+
distDir = dirname12(fileURLToPath(metaUrl));
|
|
45222
46076
|
} catch {
|
|
45223
46077
|
return { kind: "unknown" };
|
|
45224
46078
|
}
|
|
@@ -45242,7 +46096,7 @@ function checkBuildStaleness(deps = {}) {
|
|
|
45242
46096
|
|
|
45243
46097
|
// src/lib/memory-links.ts
|
|
45244
46098
|
import { readFileSync as readFileSync28 } from "node:fs";
|
|
45245
|
-
import { relative as relative15, sep as
|
|
46099
|
+
import { relative as relative15, sep as sep8 } from "node:path";
|
|
45246
46100
|
var MEMORY_FILE_RE = /^(workspace-memory\.md|user-memory\.md|artists\/[^/]+\/memory\.md|memory\/[^/]+\.md|memory\/private\/[^/]+\.md)$/i;
|
|
45247
46101
|
function scanMemoryLinks(deps) {
|
|
45248
46102
|
const listFiles = deps.listFiles ?? ((r) => walkSubtreeFiles(r, isVcsInternal));
|
|
@@ -45255,7 +46109,7 @@ function scanMemoryLinks(deps) {
|
|
|
45255
46109
|
} catch {
|
|
45256
46110
|
return { scanned: 0, unreadable: [], dead: [] };
|
|
45257
46111
|
}
|
|
45258
|
-
const relPaths = absPaths.map((p) => relative15(root, p).split(
|
|
46112
|
+
const relPaths = absPaths.map((p) => relative15(root, p).split(sep8).join("/"));
|
|
45259
46113
|
const artists = buildArtistIndex(relPaths, root, (abs) => readFile(abs));
|
|
45260
46114
|
const dead = [];
|
|
45261
46115
|
const unreadable = [];
|
|
@@ -46321,7 +47175,7 @@ async function callVoiceGate(req) {
|
|
|
46321
47175
|
}
|
|
46322
47176
|
|
|
46323
47177
|
// src/lib/voice-gate/attempts.ts
|
|
46324
|
-
import { createHash as
|
|
47178
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
46325
47179
|
import {
|
|
46326
47180
|
mkdirSync as mkdirSync12,
|
|
46327
47181
|
readFileSync as readFileSync30,
|
|
@@ -46329,7 +47183,7 @@ import {
|
|
|
46329
47183
|
unlinkSync as unlinkSync9,
|
|
46330
47184
|
writeFileSync as writeFileSync9
|
|
46331
47185
|
} from "node:fs";
|
|
46332
|
-
import { join as
|
|
47186
|
+
import { join as join19 } from "node:path";
|
|
46333
47187
|
var MAX_REGENS = 2;
|
|
46334
47188
|
var ENTRY_TTL_MS = 24 * 60 * 60 * 1000;
|
|
46335
47189
|
var AttemptEntrySchema = exports_external.object({
|
|
@@ -46342,13 +47196,13 @@ var AttemptsFileSchema = exports_external.object({
|
|
|
46342
47196
|
entries: exports_external.record(exports_external.string(), AttemptEntrySchema)
|
|
46343
47197
|
});
|
|
46344
47198
|
function attemptsDir(workspaceSlug) {
|
|
46345
|
-
return
|
|
47199
|
+
return join19(plinthHome(), "_voice_gate", workspaceSlug);
|
|
46346
47200
|
}
|
|
46347
47201
|
function attemptsPath(workspaceSlug) {
|
|
46348
|
-
return
|
|
47202
|
+
return join19(attemptsDir(workspaceSlug), "attempts.json");
|
|
46349
47203
|
}
|
|
46350
47204
|
function attemptKey(identity) {
|
|
46351
|
-
return
|
|
47205
|
+
return createHash5("sha256").update(identity, "utf8").digest("hex").slice(0, 32);
|
|
46352
47206
|
}
|
|
46353
47207
|
function emptyFile() {
|
|
46354
47208
|
return { version: 1, entries: {} };
|
|
@@ -46520,7 +47374,7 @@ function denyUngated(detail) {
|
|
|
46520
47374
|
// src/lib/voice-gate/payload.ts
|
|
46521
47375
|
import { readFileSync as readFileSync31, statSync as statSync20 } from "node:fs";
|
|
46522
47376
|
import { homedir as homedir2 } from "node:os";
|
|
46523
|
-
import { isAbsolute as isAbsolute4, resolve as
|
|
47377
|
+
import { isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
|
|
46524
47378
|
var MAX_BODY_BYTES = 256 * 1024;
|
|
46525
47379
|
var HELPER_BASENAME = "push_gmail_draft.py";
|
|
46526
47380
|
var DRAFT_FLAGS = ["--to", "--body-file", "--body-stdin"];
|
|
@@ -46714,13 +47568,13 @@ function applyCd(tokens, state) {
|
|
|
46714
47568
|
return { dir: expanded, known: true };
|
|
46715
47569
|
if (!state.known)
|
|
46716
47570
|
return state;
|
|
46717
|
-
return { dir:
|
|
47571
|
+
return { dir: resolve6(state.dir, expanded), known: true };
|
|
46718
47572
|
}
|
|
46719
47573
|
function expandHome(path5) {
|
|
46720
47574
|
if (path5 === "~")
|
|
46721
47575
|
return homedir2();
|
|
46722
47576
|
if (path5.startsWith("~/"))
|
|
46723
|
-
return
|
|
47577
|
+
return resolve6(homedir2(), path5.slice(2));
|
|
46724
47578
|
return path5;
|
|
46725
47579
|
}
|
|
46726
47580
|
function classifyHelperSegment(tokens, dirState, readBodyFile) {
|
|
@@ -46746,7 +47600,7 @@ function classifyHelperSegment(tokens, dirState, readBodyFile) {
|
|
|
46746
47600
|
unreadable: `the draft push uses a relative \`--body-file\` (${bodyFile}) after a \`cd\` ` + `the voice gate cannot resolve, so it cannot tell which file ships. Pass an ` + `absolute \`--body-file\` path.`
|
|
46747
47601
|
};
|
|
46748
47602
|
}
|
|
46749
|
-
const resolved = isAbsolute4(expanded) ? expanded :
|
|
47603
|
+
const resolved = isAbsolute4(expanded) ? expanded : resolve6(dirState.dir, expanded);
|
|
46750
47604
|
let body;
|
|
46751
47605
|
try {
|
|
46752
47606
|
body = readBodyFile(resolved);
|