pakhale 0.1.0 → 0.3.0
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/README.md +5 -0
- package/dist/cli.js +1018 -66
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { parseArgs, styleText } from "node:util";
|
|
7
7
|
import process$1, { stdin, stdout } from "node:process";
|
|
8
8
|
import l__default from "node:readline";
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
9
10
|
import { spawn } from "node:child_process";
|
|
10
11
|
|
|
11
12
|
//#region rolldown:runtime
|
|
@@ -104,7 +105,7 @@ var require_picocolors = /* @__PURE__ */ __commonJS({ "node_modules/picocolors/p
|
|
|
104
105
|
|
|
105
106
|
//#endregion
|
|
106
107
|
//#region config.ts
|
|
107
|
-
var import_picocolors$
|
|
108
|
+
var import_picocolors$4 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
108
109
|
const config = {
|
|
109
110
|
agents: ["claude-code", "opencode"],
|
|
110
111
|
marketplaces: {
|
|
@@ -218,14 +219,14 @@ const config = {
|
|
|
218
219
|
//#endregion
|
|
219
220
|
//#region src/util/log.ts
|
|
220
221
|
const log = {
|
|
221
|
-
agent: (s) => console.log(`\n${import_picocolors$
|
|
222
|
-
section: (s) => console.log(`\n${import_picocolors$
|
|
223
|
-
ok: (s) => console.log(` ${import_picocolors$
|
|
224
|
-
skip: (s) => console.log(` ${import_picocolors$
|
|
225
|
-
warn: (s) => console.log(` ${import_picocolors$
|
|
226
|
-
fail: (s) => console.log(` ${import_picocolors$
|
|
227
|
-
plan: (s) => console.log(` ${import_picocolors$
|
|
228
|
-
info: (s) => console.log(` ${import_picocolors$
|
|
222
|
+
agent: (s) => console.log(`\n${import_picocolors$4.default.bold(import_picocolors$4.default.magenta(`■ ${s}`))}`),
|
|
223
|
+
section: (s) => console.log(`\n${import_picocolors$4.default.bold(import_picocolors$4.default.cyan(`▸ ${s}`))}`),
|
|
224
|
+
ok: (s) => console.log(` ${import_picocolors$4.default.green("✓")} ${s}`),
|
|
225
|
+
skip: (s) => console.log(` ${import_picocolors$4.default.dim("·")} ${import_picocolors$4.default.dim(s)}`),
|
|
226
|
+
warn: (s) => console.log(` ${import_picocolors$4.default.yellow("!")} ${s}`),
|
|
227
|
+
fail: (s) => console.log(` ${import_picocolors$4.default.red("✗")} ${s}`),
|
|
228
|
+
plan: (s) => console.log(` ${import_picocolors$4.default.yellow("would")} ${s}`),
|
|
229
|
+
info: (s) => console.log(` ${import_picocolors$4.default.dim(s)}`)
|
|
229
230
|
};
|
|
230
231
|
|
|
231
232
|
//#endregion
|
|
@@ -633,7 +634,7 @@ function isActionKey(n$2, e) {
|
|
|
633
634
|
for (const s of n$2) if (s !== void 0 && isActionKey(s, e)) return true;
|
|
634
635
|
return false;
|
|
635
636
|
}
|
|
636
|
-
function diffLines(i$1, s) {
|
|
637
|
+
function diffLines$1(i$1, s) {
|
|
637
638
|
if (i$1 === s) return;
|
|
638
639
|
const e = i$1.split(`
|
|
639
640
|
`), t = s.split(`
|
|
@@ -661,8 +662,8 @@ function wrapTextWithPrefix(e, r$1, o, t = o, s = o, n$2) {
|
|
|
661
662
|
hard: true,
|
|
662
663
|
trim: false
|
|
663
664
|
}).split(`
|
|
664
|
-
`).map((c, i$1, m$1) => {
|
|
665
|
-
const d = n$2 ? n$2(c, i$1) : c;
|
|
665
|
+
`).map((c$1, i$1, m$1) => {
|
|
666
|
+
const d = n$2 ? n$2(c$1, i$1) : c$1;
|
|
666
667
|
return i$1 === 0 ? `${t}${d}` : i$1 === m$1.length - 1 ? `${s}${d}` : `${o}${d}`;
|
|
667
668
|
}).join(`
|
|
668
669
|
`);
|
|
@@ -813,7 +814,7 @@ var V = class {
|
|
|
813
814
|
if (t !== this._prevFrame) {
|
|
814
815
|
if (this.state === "initial") this.output.write(import_src$1.cursor.hide);
|
|
815
816
|
else {
|
|
816
|
-
const e = diffLines(this._prevFrame, t), i$1 = getRows(this.output);
|
|
817
|
+
const e = diffLines$1(this._prevFrame, t), i$1 = getRows(this.output);
|
|
817
818
|
if (this.restoreCursor(), e) {
|
|
818
819
|
const n$2 = Math.max(0, e.numLinesAfter - i$1), s = Math.max(0, e.numLinesBefore - i$1);
|
|
819
820
|
let r$1 = e.lines.find((o) => o >= n$2);
|
|
@@ -891,6 +892,34 @@ var a = class extends V {
|
|
|
891
892
|
});
|
|
892
893
|
}
|
|
893
894
|
};
|
|
895
|
+
let n$1 = class n$2 extends V {
|
|
896
|
+
options;
|
|
897
|
+
cursor = 0;
|
|
898
|
+
get _selectedValue() {
|
|
899
|
+
return this.options[this.cursor];
|
|
900
|
+
}
|
|
901
|
+
changeValue() {
|
|
902
|
+
const e = this._selectedValue;
|
|
903
|
+
this.value = e === void 0 ? void 0 : e.value;
|
|
904
|
+
}
|
|
905
|
+
constructor(e) {
|
|
906
|
+
super(e, false), this.options = e.options;
|
|
907
|
+
const o = this.options.findIndex(({ value: s }) => s === e.initialValue), t = o === -1 ? 0 : o;
|
|
908
|
+
this.cursor = this.options[t]?.disabled ? findCursor(t, 1, this.options) : t, this.changeValue(), this.on("cursor", (s) => {
|
|
909
|
+
switch (s) {
|
|
910
|
+
case "left":
|
|
911
|
+
case "up":
|
|
912
|
+
this.cursor = findCursor(this.cursor, -1, this.options);
|
|
913
|
+
break;
|
|
914
|
+
case "down":
|
|
915
|
+
case "right":
|
|
916
|
+
this.cursor = findCursor(this.cursor, 1, this.options);
|
|
917
|
+
break;
|
|
918
|
+
}
|
|
919
|
+
this.changeValue();
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
};
|
|
894
923
|
|
|
895
924
|
//#endregion
|
|
896
925
|
//#region node_modules/@clack/prompts/dist/index.mjs
|
|
@@ -939,11 +968,11 @@ const limitOptions = ({ cursor: l, options: e, style: w, output: p$1 = process.s
|
|
|
939
968
|
const i$1 = getColumns(p$1) - C, m$1 = getRows(p$1), M = styleText("dim", "..."), v = Math.max(m$1 - r$1, 0), a$1 = Math.max(Math.min(b, v), 5);
|
|
940
969
|
let f = 0;
|
|
941
970
|
l >= a$1 - 3 && (f = Math.max(Math.min(l - a$1 + 3, e.length - a$1), 0));
|
|
942
|
-
let d = a$1 < e.length && f > 0, c = a$1 < e.length && f + a$1 < e.length;
|
|
971
|
+
let d = a$1 < e.length && f > 0, c$1 = a$1 < e.length && f + a$1 < e.length;
|
|
943
972
|
const W = Math.min(f + a$1, e.length), s = [];
|
|
944
973
|
let g = 0;
|
|
945
|
-
d && g++, c && g++;
|
|
946
|
-
const T = f + (d ? 1 : 0), y = W - (c ? 1 : 0);
|
|
974
|
+
d && g++, c$1 && g++;
|
|
975
|
+
const T = f + (d ? 1 : 0), y = W - (c$1 ? 1 : 0);
|
|
947
976
|
for (let t = T; t < y; t++) {
|
|
948
977
|
const n$2 = e[t], h$1 = wrapAnsi(n$2 ? w(n$2, t === l) : "", i$1, {
|
|
949
978
|
hard: true,
|
|
@@ -957,12 +986,12 @@ const limitOptions = ({ cursor: l, options: e, style: w, output: p$1 = process.s
|
|
|
957
986
|
const h$1 = l - T;
|
|
958
987
|
let u$4 = v;
|
|
959
988
|
const L = () => I(s, o, 0, h$1, u$4), E = () => I(s, o, h$1 + 1, s.length, u$4, true);
|
|
960
|
-
d ? ({lineCount: o, removals: t} = L(), o > u$4 && (c || (u$4 -= 1), {lineCount: o, removals: n$2} = E())) : (c || (u$4 -= 1), {lineCount: o, removals: n$2} = E(), o > u$4 && (u$4 -= 1, {lineCount: o, removals: t} = L())), t > 0 && (d = true, s.splice(0, t)), n$2 > 0 && (c = true, s.splice(s.length - n$2, n$2));
|
|
989
|
+
d ? ({lineCount: o, removals: t} = L(), o > u$4 && (c$1 || (u$4 -= 1), {lineCount: o, removals: n$2} = E())) : (c$1 || (u$4 -= 1), {lineCount: o, removals: n$2} = E(), o > u$4 && (u$4 -= 1, {lineCount: o, removals: t} = L())), t > 0 && (d = true, s.splice(0, t)), n$2 > 0 && (c$1 = true, s.splice(s.length - n$2, n$2));
|
|
961
990
|
}
|
|
962
991
|
const x = [];
|
|
963
992
|
d && x.push(M);
|
|
964
993
|
for (const t of s) for (const n$2 of t) x.push(n$2);
|
|
965
|
-
return c && x.push(M), x;
|
|
994
|
+
return c$1 && x.push(M), x;
|
|
966
995
|
};
|
|
967
996
|
const MULTISELECT_INSTRUCTIONS = [
|
|
968
997
|
`${styleText("dim", "↑/↓")} to navigate`,
|
|
@@ -1074,6 +1103,63 @@ const u$3 = {
|
|
|
1074
1103
|
block: unicodeOr("█", "#")
|
|
1075
1104
|
};
|
|
1076
1105
|
const SELECT_INSTRUCTIONS = [`${styleText("dim", "↑/↓")} to navigate`, `${styleText("dim", "Enter:")} confirm`];
|
|
1106
|
+
const c = (t, o) => t.includes(`
|
|
1107
|
+
`) ? t.split(`
|
|
1108
|
+
`).map((d) => o(d)).join(`
|
|
1109
|
+
`) : o(t);
|
|
1110
|
+
const select$1 = (t) => {
|
|
1111
|
+
const o = (n$2, m$1) => {
|
|
1112
|
+
if (n$2 === void 0) return "";
|
|
1113
|
+
const s = n$2.label ?? String(n$2.value);
|
|
1114
|
+
switch (m$1) {
|
|
1115
|
+
case "disabled": return `${styleText("gray", S_RADIO_INACTIVE)} ${c(s, (i$1) => styleText("gray", i$1))}${n$2.hint ? ` ${styleText("dim", `(${n$2.hint ?? "disabled"})`)}` : ""}`;
|
|
1116
|
+
case "selected": return `${c(s, (i$1) => styleText("dim", i$1))}`;
|
|
1117
|
+
case "active": return `${styleText("green", S_RADIO_ACTIVE)} ${s}${n$2.hint ? ` ${styleText("dim", `(${n$2.hint})`)}` : ""}`;
|
|
1118
|
+
case "cancelled": return `${c(s, (i$1) => styleText(["strikethrough", "dim"], i$1))}`;
|
|
1119
|
+
default: return `${styleText("dim", S_RADIO_INACTIVE)} ${c(s, (i$1) => styleText("dim", i$1))}`;
|
|
1120
|
+
}
|
|
1121
|
+
}, d = t.showInstructions ?? true;
|
|
1122
|
+
return new n$1({
|
|
1123
|
+
options: t.options,
|
|
1124
|
+
signal: t.signal,
|
|
1125
|
+
input: t.input,
|
|
1126
|
+
output: t.output,
|
|
1127
|
+
initialValue: t.initialValue,
|
|
1128
|
+
render() {
|
|
1129
|
+
const n$2 = t.withGuide ?? settings.withGuide, m$1 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, i$1 = wrapTextWithPrefix(t.output, t.message, s, m$1), u$4 = `${n$2 ? `${styleText("gray", S_BAR)}
|
|
1130
|
+
` : ""}${i$1}
|
|
1131
|
+
`;
|
|
1132
|
+
switch (this.state) {
|
|
1133
|
+
case "submit": {
|
|
1134
|
+
const r$1 = n$2 ? `${styleText("gray", S_BAR)} ` : "";
|
|
1135
|
+
return `${u$4}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "selected"), r$1)}`;
|
|
1136
|
+
}
|
|
1137
|
+
case "cancel": {
|
|
1138
|
+
const r$1 = n$2 ? `${styleText("gray", S_BAR)} ` : "";
|
|
1139
|
+
return `${u$4}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "cancelled"), r$1)}${n$2 ? `
|
|
1140
|
+
${styleText("gray", S_BAR)}` : ""}`;
|
|
1141
|
+
}
|
|
1142
|
+
default: {
|
|
1143
|
+
const r$1 = n$2 ? `${styleText("cyan", S_BAR)} ` : "", a$1 = u$4.split(`
|
|
1144
|
+
`).length, p$1 = d ? formatInstructionFooter(SELECT_INSTRUCTIONS, n$2) : n$2 ? [styleText("cyan", S_BAR_END)] : [], b = p$1.join(`
|
|
1145
|
+
`), f = p$1.length + 1;
|
|
1146
|
+
return `${u$4}${r$1}${limitOptions({
|
|
1147
|
+
output: t.output,
|
|
1148
|
+
cursor: this.cursor,
|
|
1149
|
+
options: this.options,
|
|
1150
|
+
maxItems: t.maxItems,
|
|
1151
|
+
columnPadding: r$1.length,
|
|
1152
|
+
rowPadding: a$1 + f,
|
|
1153
|
+
style: (g, x) => o(g, g.disabled ? "disabled" : x ? "active" : "inactive")
|
|
1154
|
+
}).join(`
|
|
1155
|
+
${r$1}`)}
|
|
1156
|
+
${b}
|
|
1157
|
+
`;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}).prompt();
|
|
1162
|
+
};
|
|
1077
1163
|
const i = `${styleText("gray", S_BAR)} `;
|
|
1078
1164
|
|
|
1079
1165
|
//#endregion
|
|
@@ -1083,12 +1169,12 @@ async function multiSelect(message, choices) {
|
|
|
1083
1169
|
if (!choices.length) return [];
|
|
1084
1170
|
const picked = await multiselect({
|
|
1085
1171
|
message,
|
|
1086
|
-
options: choices.map((c) => ({
|
|
1087
|
-
value: c.value,
|
|
1088
|
-
label: c.label,
|
|
1089
|
-
hint: c.hint
|
|
1172
|
+
options: choices.map((c$1) => ({
|
|
1173
|
+
value: c$1.value,
|
|
1174
|
+
label: c$1.label,
|
|
1175
|
+
hint: c$1.hint
|
|
1090
1176
|
})),
|
|
1091
|
-
initialValues: choices.map((c) => c.value),
|
|
1177
|
+
initialValues: choices.map((c$1) => c$1.value),
|
|
1092
1178
|
required: false
|
|
1093
1179
|
});
|
|
1094
1180
|
if (isCancel(picked)) {
|
|
@@ -1097,6 +1183,22 @@ async function multiSelect(message, choices) {
|
|
|
1097
1183
|
}
|
|
1098
1184
|
return picked;
|
|
1099
1185
|
}
|
|
1186
|
+
/** One answer from a list. Cancelling returns null — the caller decides what safe means. */
|
|
1187
|
+
async function select(message, choices) {
|
|
1188
|
+
const picked = await select$1({
|
|
1189
|
+
message,
|
|
1190
|
+
options: choices.map((c$1) => ({
|
|
1191
|
+
value: c$1.value,
|
|
1192
|
+
label: c$1.label,
|
|
1193
|
+
hint: c$1.hint
|
|
1194
|
+
}))
|
|
1195
|
+
});
|
|
1196
|
+
if (isCancel(picked)) {
|
|
1197
|
+
cancel("cancelled");
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
return picked;
|
|
1201
|
+
}
|
|
1100
1202
|
function isInteractive() {
|
|
1101
1203
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
1102
1204
|
}
|
|
@@ -1210,7 +1312,7 @@ function logCoverage(config$1) {
|
|
|
1210
1312
|
const nameWidth = Math.max(...rows.map((r$1) => r$1.name.length));
|
|
1211
1313
|
const cellWidths = config$1.agents.map((_, i$1) => Math.max(...rows.map((r$1) => r$1.cells[i$1].length)));
|
|
1212
1314
|
for (const { name: name$1, cells } of rows) {
|
|
1213
|
-
const line = cells.map((c, i$1) => c.padEnd(cellWidths[i$1])).join(" ");
|
|
1315
|
+
const line = cells.map((c$1, i$1) => c$1.padEnd(cellWidths[i$1])).join(" ");
|
|
1214
1316
|
log.info(`${name$1.padEnd(nameWidth)} ${line.trimEnd()}`);
|
|
1215
1317
|
}
|
|
1216
1318
|
}
|
|
@@ -1300,6 +1402,36 @@ function skillInstalls(config$1, agent, home, linkDir) {
|
|
|
1300
1402
|
});
|
|
1301
1403
|
}
|
|
1302
1404
|
|
|
1405
|
+
//#endregion
|
|
1406
|
+
//#region src/commands/setup/agents/state.ts
|
|
1407
|
+
/** Machine-local bookkeeping, never a source of truth — do not sync or vendor it. */
|
|
1408
|
+
function stateFileFor(home) {
|
|
1409
|
+
return join(home, ".agents", ".setup-state.json");
|
|
1410
|
+
}
|
|
1411
|
+
function hashContent(content) {
|
|
1412
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1413
|
+
}
|
|
1414
|
+
function hashValue(value) {
|
|
1415
|
+
return hashContent(stable(value));
|
|
1416
|
+
}
|
|
1417
|
+
async function loadState(file) {
|
|
1418
|
+
try {
|
|
1419
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
1420
|
+
return {
|
|
1421
|
+
files: parsed.files ?? {},
|
|
1422
|
+
keys: parsed.keys ?? {}
|
|
1423
|
+
};
|
|
1424
|
+
} catch {
|
|
1425
|
+
return {
|
|
1426
|
+
files: {},
|
|
1427
|
+
keys: {}
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
async function saveState(file, state) {
|
|
1432
|
+
await writeJson(file, state);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1303
1435
|
//#endregion
|
|
1304
1436
|
//#region src/commands/setup/agents/store.ts
|
|
1305
1437
|
const AGENTS_STORE = ".agents/skills";
|
|
@@ -1312,6 +1444,7 @@ async function buildStorePlan(ctx) {
|
|
|
1312
1444
|
const archiveDir = join(ctx.home, ".agents/.setup-backups", stamp);
|
|
1313
1445
|
return {
|
|
1314
1446
|
archiveDir,
|
|
1447
|
+
stateFile: stateFileFor(ctx.home),
|
|
1315
1448
|
groups: [{
|
|
1316
1449
|
section: "Skills store (~/.agents/skills)",
|
|
1317
1450
|
artifacts: (await authoredSkillNames(ctx)).map((name$1) => ({
|
|
@@ -1398,6 +1531,7 @@ async function buildPlan$1(ctx) {
|
|
|
1398
1531
|
});
|
|
1399
1532
|
return {
|
|
1400
1533
|
archiveDir,
|
|
1534
|
+
stateFile: stateFileFor(ctx.home),
|
|
1401
1535
|
groups
|
|
1402
1536
|
};
|
|
1403
1537
|
}
|
|
@@ -1423,8 +1557,8 @@ function claudeMcp(server) {
|
|
|
1423
1557
|
...server.headers && { headers: server.headers }
|
|
1424
1558
|
};
|
|
1425
1559
|
}
|
|
1426
|
-
function mapValues(record, fn) {
|
|
1427
|
-
return Object.fromEntries(Object.entries(record).map(([k, v]) => [k, fn(v)]));
|
|
1560
|
+
function mapValues(record$1, fn) {
|
|
1561
|
+
return Object.fromEntries(Object.entries(record$1).map(([k, v]) => [k, fn(v)]));
|
|
1428
1562
|
}
|
|
1429
1563
|
async function authoredSkillLinks(ctx, root, archiveDir) {
|
|
1430
1564
|
return (await authoredSkillNames(ctx)).map((name$1) => ({
|
|
@@ -1494,6 +1628,7 @@ async function buildPlan(ctx) {
|
|
|
1494
1628
|
});
|
|
1495
1629
|
return {
|
|
1496
1630
|
archiveDir: join(root, ".setup-backups", stamp),
|
|
1631
|
+
stateFile: stateFileFor(ctx.home),
|
|
1497
1632
|
groups
|
|
1498
1633
|
};
|
|
1499
1634
|
}
|
|
@@ -1561,14 +1696,631 @@ async function which(bin) {
|
|
|
1561
1696
|
])).ok;
|
|
1562
1697
|
}
|
|
1563
1698
|
|
|
1699
|
+
//#endregion
|
|
1700
|
+
//#region node_modules/diff/libesm/diff/base.js
|
|
1701
|
+
var Diff = class {
|
|
1702
|
+
diff(oldStr, newStr, options = {}) {
|
|
1703
|
+
let callback;
|
|
1704
|
+
if (typeof options === "function") {
|
|
1705
|
+
callback = options;
|
|
1706
|
+
options = {};
|
|
1707
|
+
} else if ("callback" in options) callback = options.callback;
|
|
1708
|
+
const oldString = this.castInput(oldStr, options);
|
|
1709
|
+
const newString = this.castInput(newStr, options);
|
|
1710
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
1711
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
1712
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
1713
|
+
}
|
|
1714
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
1715
|
+
var _a;
|
|
1716
|
+
const done = (value) => {
|
|
1717
|
+
value = this.postProcess(value, options);
|
|
1718
|
+
if (callback) {
|
|
1719
|
+
setTimeout(function() {
|
|
1720
|
+
callback(value);
|
|
1721
|
+
}, 0);
|
|
1722
|
+
return;
|
|
1723
|
+
} else return value;
|
|
1724
|
+
};
|
|
1725
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
1726
|
+
let editLength = 1;
|
|
1727
|
+
let maxEditLength = newLen + oldLen;
|
|
1728
|
+
if (options.maxEditLength != null) maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
1729
|
+
const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
|
|
1730
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
1731
|
+
const bestPath = [{
|
|
1732
|
+
oldPos: -1,
|
|
1733
|
+
lastComponent: void 0
|
|
1734
|
+
}];
|
|
1735
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
1736
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
1737
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
1738
|
+
const execEditLength = () => {
|
|
1739
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
1740
|
+
let basePath;
|
|
1741
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
1742
|
+
if (removePath) bestPath[diagonalPath - 1] = void 0;
|
|
1743
|
+
let canAdd = false;
|
|
1744
|
+
if (addPath) {
|
|
1745
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
1746
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
1747
|
+
}
|
|
1748
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
1749
|
+
if (!canAdd && !canRemove) {
|
|
1750
|
+
bestPath[diagonalPath] = void 0;
|
|
1751
|
+
continue;
|
|
1752
|
+
}
|
|
1753
|
+
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) basePath = this.addToPath(addPath, true, false, 0, options);
|
|
1754
|
+
else basePath = this.addToPath(removePath, false, true, 1, options);
|
|
1755
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
1756
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
1757
|
+
else {
|
|
1758
|
+
bestPath[diagonalPath] = basePath;
|
|
1759
|
+
if (basePath.oldPos + 1 >= oldLen) maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
1760
|
+
if (newPos + 1 >= newLen) minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
editLength++;
|
|
1764
|
+
};
|
|
1765
|
+
if (callback) (function exec$1() {
|
|
1766
|
+
setTimeout(function() {
|
|
1767
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) return callback(void 0);
|
|
1768
|
+
if (!execEditLength()) exec$1();
|
|
1769
|
+
}, 0);
|
|
1770
|
+
})();
|
|
1771
|
+
else while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
1772
|
+
const ret = execEditLength();
|
|
1773
|
+
if (ret) return ret;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
addToPath(path, added, removed, oldPosInc, options) {
|
|
1777
|
+
const last = path.lastComponent;
|
|
1778
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) return {
|
|
1779
|
+
oldPos: path.oldPos + oldPosInc,
|
|
1780
|
+
lastComponent: {
|
|
1781
|
+
count: last.count + 1,
|
|
1782
|
+
added,
|
|
1783
|
+
removed,
|
|
1784
|
+
previousComponent: last.previousComponent
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1787
|
+
else return {
|
|
1788
|
+
oldPos: path.oldPos + oldPosInc,
|
|
1789
|
+
lastComponent: {
|
|
1790
|
+
count: 1,
|
|
1791
|
+
added,
|
|
1792
|
+
removed,
|
|
1793
|
+
previousComponent: last
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
1798
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
1799
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
1800
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
1801
|
+
newPos++;
|
|
1802
|
+
oldPos++;
|
|
1803
|
+
commonCount++;
|
|
1804
|
+
if (options.oneChangePerToken) basePath.lastComponent = {
|
|
1805
|
+
count: 1,
|
|
1806
|
+
previousComponent: basePath.lastComponent,
|
|
1807
|
+
added: false,
|
|
1808
|
+
removed: false
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
if (commonCount && !options.oneChangePerToken) basePath.lastComponent = {
|
|
1812
|
+
count: commonCount,
|
|
1813
|
+
previousComponent: basePath.lastComponent,
|
|
1814
|
+
added: false,
|
|
1815
|
+
removed: false
|
|
1816
|
+
};
|
|
1817
|
+
basePath.oldPos = oldPos;
|
|
1818
|
+
return newPos;
|
|
1819
|
+
}
|
|
1820
|
+
equals(left, right, options) {
|
|
1821
|
+
if (options.comparator) return options.comparator(left, right);
|
|
1822
|
+
else return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
1823
|
+
}
|
|
1824
|
+
removeEmpty(array) {
|
|
1825
|
+
const ret = [];
|
|
1826
|
+
for (let i$1 = 0; i$1 < array.length; i$1++) if (array[i$1]) ret.push(array[i$1]);
|
|
1827
|
+
return ret;
|
|
1828
|
+
}
|
|
1829
|
+
castInput(value, options) {
|
|
1830
|
+
return value;
|
|
1831
|
+
}
|
|
1832
|
+
tokenize(value, options) {
|
|
1833
|
+
return Array.from(value);
|
|
1834
|
+
}
|
|
1835
|
+
join(chars) {
|
|
1836
|
+
return chars.join("");
|
|
1837
|
+
}
|
|
1838
|
+
postProcess(changeObjects, options) {
|
|
1839
|
+
return changeObjects;
|
|
1840
|
+
}
|
|
1841
|
+
get useLongestToken() {
|
|
1842
|
+
return false;
|
|
1843
|
+
}
|
|
1844
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
1845
|
+
const components = [];
|
|
1846
|
+
let nextComponent;
|
|
1847
|
+
while (lastComponent) {
|
|
1848
|
+
components.push(lastComponent);
|
|
1849
|
+
nextComponent = lastComponent.previousComponent;
|
|
1850
|
+
delete lastComponent.previousComponent;
|
|
1851
|
+
lastComponent = nextComponent;
|
|
1852
|
+
}
|
|
1853
|
+
components.reverse();
|
|
1854
|
+
const componentLen = components.length;
|
|
1855
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
1856
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
1857
|
+
const component = components[componentPos];
|
|
1858
|
+
if (!component.removed) {
|
|
1859
|
+
if (!component.added && this.useLongestToken) {
|
|
1860
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
1861
|
+
value = value.map(function(value$1, i$1) {
|
|
1862
|
+
const oldValue = oldTokens[oldPos + i$1];
|
|
1863
|
+
return oldValue.length > value$1.length ? oldValue : value$1;
|
|
1864
|
+
});
|
|
1865
|
+
component.value = this.join(value);
|
|
1866
|
+
} else component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
1867
|
+
newPos += component.count;
|
|
1868
|
+
if (!component.added) oldPos += component.count;
|
|
1869
|
+
} else {
|
|
1870
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
1871
|
+
oldPos += component.count;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
return components;
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
//#endregion
|
|
1879
|
+
//#region node_modules/diff/libesm/diff/character.js
|
|
1880
|
+
var CharacterDiff = class extends Diff {};
|
|
1881
|
+
const characterDiff = new CharacterDiff();
|
|
1882
|
+
|
|
1883
|
+
//#endregion
|
|
1884
|
+
//#region node_modules/diff/libesm/util/string.js
|
|
1885
|
+
function longestCommonPrefix(str1, str2) {
|
|
1886
|
+
let i$1;
|
|
1887
|
+
for (i$1 = 0; i$1 < str1.length && i$1 < str2.length; i$1++) if (str1[i$1] != str2[i$1]) return str1.slice(0, i$1);
|
|
1888
|
+
return str1.slice(0, i$1);
|
|
1889
|
+
}
|
|
1890
|
+
function longestCommonSuffix(str1, str2) {
|
|
1891
|
+
let i$1;
|
|
1892
|
+
if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) return "";
|
|
1893
|
+
for (i$1 = 0; i$1 < str1.length && i$1 < str2.length; i$1++) if (str1[str1.length - (i$1 + 1)] != str2[str2.length - (i$1 + 1)]) return str1.slice(-i$1);
|
|
1894
|
+
return str1.slice(-i$1);
|
|
1895
|
+
}
|
|
1896
|
+
function replacePrefix(string, oldPrefix, newPrefix) {
|
|
1897
|
+
if (string.slice(0, oldPrefix.length) != oldPrefix) throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
|
|
1898
|
+
return newPrefix + string.slice(oldPrefix.length);
|
|
1899
|
+
}
|
|
1900
|
+
function replaceSuffix(string, oldSuffix, newSuffix) {
|
|
1901
|
+
if (!oldSuffix) return string + newSuffix;
|
|
1902
|
+
if (string.slice(-oldSuffix.length) != oldSuffix) throw Error(`string ${JSON.stringify(string)} doesn't end with suffix ${JSON.stringify(oldSuffix)}; this is a bug`);
|
|
1903
|
+
return string.slice(0, -oldSuffix.length) + newSuffix;
|
|
1904
|
+
}
|
|
1905
|
+
function removePrefix(string, oldPrefix) {
|
|
1906
|
+
return replacePrefix(string, oldPrefix, "");
|
|
1907
|
+
}
|
|
1908
|
+
function removeSuffix(string, oldSuffix) {
|
|
1909
|
+
return replaceSuffix(string, oldSuffix, "");
|
|
1910
|
+
}
|
|
1911
|
+
function maximumOverlap(string1, string2) {
|
|
1912
|
+
return string2.slice(0, overlapCount(string1, string2));
|
|
1913
|
+
}
|
|
1914
|
+
function overlapCount(a$1, b) {
|
|
1915
|
+
let startA = 0;
|
|
1916
|
+
if (a$1.length > b.length) startA = a$1.length - b.length;
|
|
1917
|
+
let endB = b.length;
|
|
1918
|
+
if (a$1.length < b.length) endB = a$1.length;
|
|
1919
|
+
const map = Array(endB);
|
|
1920
|
+
let k = 0;
|
|
1921
|
+
map[0] = 0;
|
|
1922
|
+
for (let j = 1; j < endB; j++) {
|
|
1923
|
+
if (b[j] == b[k]) map[j] = map[k];
|
|
1924
|
+
else map[j] = k;
|
|
1925
|
+
while (k > 0 && b[j] != b[k]) k = map[k];
|
|
1926
|
+
if (b[j] == b[k]) k++;
|
|
1927
|
+
}
|
|
1928
|
+
k = 0;
|
|
1929
|
+
for (let i$1 = startA; i$1 < a$1.length; i$1++) {
|
|
1930
|
+
while (k > 0 && a$1[i$1] != b[k]) k = map[k];
|
|
1931
|
+
if (a$1[i$1] == b[k]) k++;
|
|
1932
|
+
}
|
|
1933
|
+
return k;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Split a string into segments using a word segmenter, merging consecutive
|
|
1937
|
+
* segments if they are both whitespace segments. Whitespace segments can
|
|
1938
|
+
* appear adjacent to one another for two reasons:
|
|
1939
|
+
* - newlines always get their own segment
|
|
1940
|
+
* - where a diacritic is attached to a whitespace character in the text, the
|
|
1941
|
+
* segment ends after the diacritic, so e.g. " \u0300 " becomes two segments.
|
|
1942
|
+
* This function therefore runs the segmenter's .segment() method and then
|
|
1943
|
+
* merges consecutive segments of whitespace into a single part.
|
|
1944
|
+
*/
|
|
1945
|
+
function segment(string, segmenter) {
|
|
1946
|
+
const parts = [];
|
|
1947
|
+
for (const segmentObj of Array.from(segmenter.segment(string))) {
|
|
1948
|
+
const segment$1 = segmentObj.segment;
|
|
1949
|
+
if (parts.length && /\s/.test(parts[parts.length - 1]) && /\s/.test(segment$1)) parts[parts.length - 1] += segment$1;
|
|
1950
|
+
else parts.push(segment$1);
|
|
1951
|
+
}
|
|
1952
|
+
return parts;
|
|
1953
|
+
}
|
|
1954
|
+
function trailingWs(string, segmenter) {
|
|
1955
|
+
if (segmenter) return leadingAndTrailingWs(string, segmenter)[1];
|
|
1956
|
+
let i$1;
|
|
1957
|
+
for (i$1 = string.length - 1; i$1 >= 0; i$1--) if (!string[i$1].match(/\s/)) break;
|
|
1958
|
+
return string.substring(i$1 + 1);
|
|
1959
|
+
}
|
|
1960
|
+
function leadingWs(string, segmenter) {
|
|
1961
|
+
if (segmenter) return leadingAndTrailingWs(string, segmenter)[0];
|
|
1962
|
+
const match = string.match(/^\s*/);
|
|
1963
|
+
return match ? match[0] : "";
|
|
1964
|
+
}
|
|
1965
|
+
function leadingAndTrailingWs(string, segmenter) {
|
|
1966
|
+
if (!segmenter) return [leadingWs(string), trailingWs(string)];
|
|
1967
|
+
if (segmenter.resolvedOptions().granularity != "word") throw new Error("The segmenter passed must have a granularity of \"word\"");
|
|
1968
|
+
const segments = segment(string, segmenter);
|
|
1969
|
+
const firstSeg = segments[0];
|
|
1970
|
+
const lastSeg = segments[segments.length - 1];
|
|
1971
|
+
return [/\s/.test(firstSeg) ? firstSeg : "", /\s/.test(lastSeg) ? lastSeg : ""];
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
//#endregion
|
|
1975
|
+
//#region node_modules/diff/libesm/diff/word.js
|
|
1976
|
+
const extendedWordChars = "a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}";
|
|
1977
|
+
const tokenizeIncludingWhitespace = new RegExp(`[${extendedWordChars}]+|\\s+|[^${extendedWordChars}]`, "ug");
|
|
1978
|
+
var WordDiff = class extends Diff {
|
|
1979
|
+
equals(left, right, options) {
|
|
1980
|
+
if (options.ignoreCase) {
|
|
1981
|
+
left = left.toLowerCase();
|
|
1982
|
+
right = right.toLowerCase();
|
|
1983
|
+
}
|
|
1984
|
+
return left.trim() === right.trim();
|
|
1985
|
+
}
|
|
1986
|
+
tokenize(value, options = {}) {
|
|
1987
|
+
let parts;
|
|
1988
|
+
if (options.intlSegmenter) {
|
|
1989
|
+
const segmenter = options.intlSegmenter;
|
|
1990
|
+
if (segmenter.resolvedOptions().granularity != "word") throw new Error("The segmenter passed must have a granularity of \"word\"");
|
|
1991
|
+
parts = segment(value, segmenter);
|
|
1992
|
+
} else parts = value.match(tokenizeIncludingWhitespace) || [];
|
|
1993
|
+
const tokens = [];
|
|
1994
|
+
let prevPart = null;
|
|
1995
|
+
parts.forEach((part) => {
|
|
1996
|
+
if (/\s/.test(part)) if (prevPart == null) tokens.push(part);
|
|
1997
|
+
else tokens.push(tokens.pop() + part);
|
|
1998
|
+
else if (prevPart != null && /\s/.test(prevPart)) if (tokens[tokens.length - 1] == prevPart) tokens.push(tokens.pop() + part);
|
|
1999
|
+
else tokens.push(prevPart + part);
|
|
2000
|
+
else tokens.push(part);
|
|
2001
|
+
prevPart = part;
|
|
2002
|
+
});
|
|
2003
|
+
return tokens;
|
|
2004
|
+
}
|
|
2005
|
+
join(tokens) {
|
|
2006
|
+
return tokens.map((token, i$1) => {
|
|
2007
|
+
if (i$1 == 0) return token;
|
|
2008
|
+
else return token.replace(/^\s+/, "");
|
|
2009
|
+
}).join("");
|
|
2010
|
+
}
|
|
2011
|
+
postProcess(changes, options) {
|
|
2012
|
+
if (!changes || options.oneChangePerToken) return changes;
|
|
2013
|
+
let lastKeep = null;
|
|
2014
|
+
let insertion = null;
|
|
2015
|
+
let deletion = null;
|
|
2016
|
+
changes.forEach((change) => {
|
|
2017
|
+
if (change.added) insertion = change;
|
|
2018
|
+
else if (change.removed) deletion = change;
|
|
2019
|
+
else {
|
|
2020
|
+
if (insertion || deletion) dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change, options.intlSegmenter);
|
|
2021
|
+
lastKeep = change;
|
|
2022
|
+
insertion = null;
|
|
2023
|
+
deletion = null;
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
if (insertion || deletion) dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null, options.intlSegmenter);
|
|
2027
|
+
return changes;
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
const wordDiff = new WordDiff();
|
|
2031
|
+
function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep, segmenter) {
|
|
2032
|
+
if (deletion && insertion) {
|
|
2033
|
+
const [oldWsPrefix, oldWsSuffix] = leadingAndTrailingWs(deletion.value, segmenter);
|
|
2034
|
+
const [newWsPrefix, newWsSuffix] = leadingAndTrailingWs(insertion.value, segmenter);
|
|
2035
|
+
if (startKeep) {
|
|
2036
|
+
const commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix);
|
|
2037
|
+
startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix);
|
|
2038
|
+
deletion.value = removePrefix(deletion.value, commonWsPrefix);
|
|
2039
|
+
insertion.value = removePrefix(insertion.value, commonWsPrefix);
|
|
2040
|
+
}
|
|
2041
|
+
if (endKeep) {
|
|
2042
|
+
const commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix);
|
|
2043
|
+
endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix);
|
|
2044
|
+
deletion.value = removeSuffix(deletion.value, commonWsSuffix);
|
|
2045
|
+
insertion.value = removeSuffix(insertion.value, commonWsSuffix);
|
|
2046
|
+
}
|
|
2047
|
+
} else if (insertion) {
|
|
2048
|
+
if (startKeep) {
|
|
2049
|
+
const ws = leadingWs(insertion.value, segmenter);
|
|
2050
|
+
insertion.value = insertion.value.substring(ws.length);
|
|
2051
|
+
}
|
|
2052
|
+
if (endKeep) {
|
|
2053
|
+
const ws = leadingWs(endKeep.value, segmenter);
|
|
2054
|
+
endKeep.value = endKeep.value.substring(ws.length);
|
|
2055
|
+
}
|
|
2056
|
+
} else if (startKeep && endKeep) {
|
|
2057
|
+
const newWsFull = leadingWs(endKeep.value, segmenter), [delWsStart, delWsEnd] = leadingAndTrailingWs(deletion.value, segmenter);
|
|
2058
|
+
const newWsStart = longestCommonPrefix(newWsFull, delWsStart);
|
|
2059
|
+
deletion.value = removePrefix(deletion.value, newWsStart);
|
|
2060
|
+
const newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd);
|
|
2061
|
+
deletion.value = removeSuffix(deletion.value, newWsEnd);
|
|
2062
|
+
endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd);
|
|
2063
|
+
startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length));
|
|
2064
|
+
} else if (endKeep) {
|
|
2065
|
+
const endKeepWsPrefix = leadingWs(endKeep.value, segmenter);
|
|
2066
|
+
const overlap = maximumOverlap(trailingWs(deletion.value, segmenter), endKeepWsPrefix);
|
|
2067
|
+
deletion.value = removeSuffix(deletion.value, overlap);
|
|
2068
|
+
} else if (startKeep) {
|
|
2069
|
+
const overlap = maximumOverlap(trailingWs(startKeep.value, segmenter), leadingWs(deletion.value, segmenter));
|
|
2070
|
+
deletion.value = removePrefix(deletion.value, overlap);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
var WordsWithSpaceDiff = class extends Diff {
|
|
2074
|
+
tokenize(value) {
|
|
2075
|
+
const regex = new RegExp(`(\\r?\\n)|[${extendedWordChars}]+|[^\\S\\n\\r]+|[^${extendedWordChars}]`, "ug");
|
|
2076
|
+
return value.match(regex) || [];
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
2079
|
+
const wordsWithSpaceDiff = new WordsWithSpaceDiff();
|
|
2080
|
+
|
|
2081
|
+
//#endregion
|
|
2082
|
+
//#region node_modules/diff/libesm/diff/line.js
|
|
2083
|
+
var LineDiff = class extends Diff {
|
|
2084
|
+
constructor() {
|
|
2085
|
+
super(...arguments);
|
|
2086
|
+
this.tokenize = tokenize;
|
|
2087
|
+
}
|
|
2088
|
+
equals(left, right, options) {
|
|
2089
|
+
if (options.ignoreWhitespace) {
|
|
2090
|
+
if (!options.newlineIsToken || !left.includes("\n")) left = left.trim();
|
|
2091
|
+
if (!options.newlineIsToken || !right.includes("\n")) right = right.trim();
|
|
2092
|
+
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
2093
|
+
if (left.endsWith("\n")) left = left.slice(0, -1);
|
|
2094
|
+
if (right.endsWith("\n")) right = right.slice(0, -1);
|
|
2095
|
+
}
|
|
2096
|
+
return super.equals(left, right, options);
|
|
2097
|
+
}
|
|
2098
|
+
};
|
|
2099
|
+
const lineDiff = new LineDiff();
|
|
2100
|
+
function diffLines(oldStr, newStr, options) {
|
|
2101
|
+
return lineDiff.diff(oldStr, newStr, options);
|
|
2102
|
+
}
|
|
2103
|
+
function tokenize(value, options) {
|
|
2104
|
+
if (options.stripTrailingCr) value = value.replace(/\r\n/g, "\n");
|
|
2105
|
+
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
2106
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) linesAndNewlines.pop();
|
|
2107
|
+
for (let i$1 = 0; i$1 < linesAndNewlines.length; i$1++) {
|
|
2108
|
+
const line = linesAndNewlines[i$1];
|
|
2109
|
+
if (i$1 % 2 && !options.newlineIsToken) retLines[retLines.length - 1] += line;
|
|
2110
|
+
else retLines.push(line);
|
|
2111
|
+
}
|
|
2112
|
+
return retLines;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
//#endregion
|
|
2116
|
+
//#region node_modules/diff/libesm/diff/sentence.js
|
|
2117
|
+
function isSentenceEndPunct(char) {
|
|
2118
|
+
return char == "." || char == "!" || char == "?";
|
|
2119
|
+
}
|
|
2120
|
+
var SentenceDiff = class extends Diff {
|
|
2121
|
+
tokenize(value) {
|
|
2122
|
+
var _a;
|
|
2123
|
+
const result = [];
|
|
2124
|
+
let tokenStartI = 0;
|
|
2125
|
+
for (let i$1 = 0; i$1 < value.length; i$1++) {
|
|
2126
|
+
if (i$1 == value.length - 1) {
|
|
2127
|
+
result.push(value.slice(tokenStartI));
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
if (isSentenceEndPunct(value[i$1]) && value[i$1 + 1].match(/\s/)) {
|
|
2131
|
+
result.push(value.slice(tokenStartI, i$1 + 1));
|
|
2132
|
+
i$1 = tokenStartI = i$1 + 1;
|
|
2133
|
+
while ((_a = value[i$1 + 1]) === null || _a === void 0 ? void 0 : _a.match(/\s/)) i$1++;
|
|
2134
|
+
result.push(value.slice(tokenStartI, i$1 + 1));
|
|
2135
|
+
tokenStartI = i$1 + 1;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
return result;
|
|
2139
|
+
}
|
|
2140
|
+
};
|
|
2141
|
+
const sentenceDiff = new SentenceDiff();
|
|
2142
|
+
|
|
2143
|
+
//#endregion
|
|
2144
|
+
//#region node_modules/diff/libesm/diff/css.js
|
|
2145
|
+
var CssDiff = class extends Diff {
|
|
2146
|
+
tokenize(value) {
|
|
2147
|
+
return value.split(/([{}:;,]|\s+)/);
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
const cssDiff = new CssDiff();
|
|
2151
|
+
|
|
2152
|
+
//#endregion
|
|
2153
|
+
//#region node_modules/diff/libesm/diff/json.js
|
|
2154
|
+
var JsonDiff = class extends Diff {
|
|
2155
|
+
constructor() {
|
|
2156
|
+
super(...arguments);
|
|
2157
|
+
this.tokenize = tokenize;
|
|
2158
|
+
}
|
|
2159
|
+
get useLongestToken() {
|
|
2160
|
+
return true;
|
|
2161
|
+
}
|
|
2162
|
+
castInput(value, options) {
|
|
2163
|
+
const { undefinedReplacement, stringifyReplacer = (k, v) => typeof v === "undefined" ? undefinedReplacement : v } = options;
|
|
2164
|
+
return typeof value === "string" ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), null, " ");
|
|
2165
|
+
}
|
|
2166
|
+
equals(left, right, options) {
|
|
2167
|
+
return super.equals(left.replace(/,([\r\n])/g, "$1"), right.replace(/,([\r\n])/g, "$1"), options);
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
const jsonDiff = new JsonDiff();
|
|
2171
|
+
function canonicalize(obj, stack, replacementStack, replacer, key) {
|
|
2172
|
+
stack = stack || [];
|
|
2173
|
+
replacementStack = replacementStack || [];
|
|
2174
|
+
if (replacer) obj = replacer(key === void 0 ? "" : key, obj);
|
|
2175
|
+
let i$1;
|
|
2176
|
+
for (i$1 = 0; i$1 < stack.length; i$1 += 1) if (stack[i$1] === obj) return replacementStack[i$1];
|
|
2177
|
+
let canonicalizedObj;
|
|
2178
|
+
if ("[object Array]" === Object.prototype.toString.call(obj)) {
|
|
2179
|
+
stack.push(obj);
|
|
2180
|
+
canonicalizedObj = new Array(obj.length);
|
|
2181
|
+
replacementStack.push(canonicalizedObj);
|
|
2182
|
+
for (i$1 = 0; i$1 < obj.length; i$1 += 1) canonicalizedObj[i$1] = canonicalize(obj[i$1], stack, replacementStack, replacer, String(i$1));
|
|
2183
|
+
stack.pop();
|
|
2184
|
+
replacementStack.pop();
|
|
2185
|
+
return canonicalizedObj;
|
|
2186
|
+
}
|
|
2187
|
+
if (obj && obj.toJSON) obj = obj.toJSON();
|
|
2188
|
+
if (typeof obj === "object" && obj !== null) {
|
|
2189
|
+
stack.push(obj);
|
|
2190
|
+
canonicalizedObj = {};
|
|
2191
|
+
replacementStack.push(canonicalizedObj);
|
|
2192
|
+
const sortedKeys = [];
|
|
2193
|
+
let key$1;
|
|
2194
|
+
for (key$1 in obj)
|
|
2195
|
+
/* istanbul ignore else */
|
|
2196
|
+
if (Object.prototype.hasOwnProperty.call(obj, key$1)) sortedKeys.push(key$1);
|
|
2197
|
+
sortedKeys.sort();
|
|
2198
|
+
for (i$1 = 0; i$1 < sortedKeys.length; i$1 += 1) {
|
|
2199
|
+
key$1 = sortedKeys[i$1];
|
|
2200
|
+
canonicalizedObj[key$1] = canonicalize(obj[key$1], stack, replacementStack, replacer, key$1);
|
|
2201
|
+
}
|
|
2202
|
+
stack.pop();
|
|
2203
|
+
replacementStack.pop();
|
|
2204
|
+
} else canonicalizedObj = obj;
|
|
2205
|
+
return canonicalizedObj;
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
//#endregion
|
|
2209
|
+
//#region node_modules/diff/libesm/diff/array.js
|
|
2210
|
+
var ArrayDiff = class extends Diff {
|
|
2211
|
+
tokenize(value) {
|
|
2212
|
+
return value.slice();
|
|
2213
|
+
}
|
|
2214
|
+
join(value) {
|
|
2215
|
+
return value;
|
|
2216
|
+
}
|
|
2217
|
+
removeEmpty(value) {
|
|
2218
|
+
return value;
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
const arrayDiff = new ArrayDiff();
|
|
2222
|
+
|
|
2223
|
+
//#endregion
|
|
2224
|
+
//#region node_modules/diff/libesm/patch/create.js
|
|
2225
|
+
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
2226
|
+
let optionsObj;
|
|
2227
|
+
if (!options) optionsObj = {};
|
|
2228
|
+
else if (typeof options === "function") optionsObj = { callback: options };
|
|
2229
|
+
else optionsObj = options;
|
|
2230
|
+
if (typeof optionsObj.context === "undefined") optionsObj.context = 4;
|
|
2231
|
+
const context = optionsObj.context;
|
|
2232
|
+
if (optionsObj.newlineIsToken) throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
|
|
2233
|
+
if (!optionsObj.callback) return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
|
|
2234
|
+
else {
|
|
2235
|
+
const { callback } = optionsObj;
|
|
2236
|
+
diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff$1) => {
|
|
2237
|
+
callback(diffLinesResultToPatch(diff$1));
|
|
2238
|
+
} }));
|
|
2239
|
+
}
|
|
2240
|
+
function diffLinesResultToPatch(diff$1) {
|
|
2241
|
+
if (!diff$1) return;
|
|
2242
|
+
diff$1.push({
|
|
2243
|
+
value: "",
|
|
2244
|
+
lines: []
|
|
2245
|
+
});
|
|
2246
|
+
function contextLines(lines) {
|
|
2247
|
+
return lines.map(function(entry) {
|
|
2248
|
+
return " " + entry;
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
const hunks = [];
|
|
2252
|
+
let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
|
|
2253
|
+
for (let i$1 = 0; i$1 < diff$1.length; i$1++) {
|
|
2254
|
+
const current = diff$1[i$1], lines = current.lines || splitLines(current.value);
|
|
2255
|
+
current.lines = lines;
|
|
2256
|
+
if (current.added || current.removed) {
|
|
2257
|
+
if (!oldRangeStart) {
|
|
2258
|
+
const prev = diff$1[i$1 - 1];
|
|
2259
|
+
oldRangeStart = oldLine;
|
|
2260
|
+
newRangeStart = newLine;
|
|
2261
|
+
if (prev) {
|
|
2262
|
+
curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
|
|
2263
|
+
oldRangeStart -= curRange.length;
|
|
2264
|
+
newRangeStart -= curRange.length;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
for (const line of lines) curRange.push((current.added ? "+" : "-") + line);
|
|
2268
|
+
if (current.added) newLine += lines.length;
|
|
2269
|
+
else oldLine += lines.length;
|
|
2270
|
+
} else {
|
|
2271
|
+
if (oldRangeStart) if (lines.length <= context * 2 && i$1 < diff$1.length - 2) for (const line of contextLines(lines)) curRange.push(line);
|
|
2272
|
+
else {
|
|
2273
|
+
const contextSize = Math.min(lines.length, context);
|
|
2274
|
+
for (const line of contextLines(lines.slice(0, contextSize))) curRange.push(line);
|
|
2275
|
+
const hunk = {
|
|
2276
|
+
oldStart: oldRangeStart,
|
|
2277
|
+
oldLines: oldLine - oldRangeStart + contextSize,
|
|
2278
|
+
newStart: newRangeStart,
|
|
2279
|
+
newLines: newLine - newRangeStart + contextSize,
|
|
2280
|
+
lines: curRange
|
|
2281
|
+
};
|
|
2282
|
+
hunks.push(hunk);
|
|
2283
|
+
oldRangeStart = 0;
|
|
2284
|
+
newRangeStart = 0;
|
|
2285
|
+
curRange = [];
|
|
2286
|
+
}
|
|
2287
|
+
oldLine += lines.length;
|
|
2288
|
+
newLine += lines.length;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
for (const hunk of hunks) for (let i$1 = 0; i$1 < hunk.lines.length; i$1++) if (hunk.lines[i$1].endsWith("\n")) hunk.lines[i$1] = hunk.lines[i$1].slice(0, -1);
|
|
2292
|
+
else {
|
|
2293
|
+
hunk.lines.splice(i$1 + 1, 0, "\");
|
|
2294
|
+
i$1++;
|
|
2295
|
+
}
|
|
2296
|
+
return {
|
|
2297
|
+
oldFileName,
|
|
2298
|
+
newFileName,
|
|
2299
|
+
oldHeader,
|
|
2300
|
+
newHeader,
|
|
2301
|
+
hunks
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Split `text` into an array of lines, including the trailing newline character (where present)
|
|
2307
|
+
*/
|
|
2308
|
+
function splitLines(text) {
|
|
2309
|
+
const hasTrailingNl = text.endsWith("\n");
|
|
2310
|
+
const result = text.split("\n").map((line) => line + "\n");
|
|
2311
|
+
if (hasTrailingNl) result.pop();
|
|
2312
|
+
else result.push(result.pop().slice(0, -1));
|
|
2313
|
+
return result;
|
|
2314
|
+
}
|
|
2315
|
+
|
|
1564
2316
|
//#endregion
|
|
1565
2317
|
//#region src/commands/setup/agents/diff.ts
|
|
1566
|
-
async function diff(plan) {
|
|
2318
|
+
async function diff(plan, state) {
|
|
1567
2319
|
const probes = new Probes();
|
|
1568
2320
|
const sections = [];
|
|
1569
2321
|
for (const group of plan.groups) {
|
|
1570
2322
|
const changes = [];
|
|
1571
|
-
for (const artifact of group.artifacts) changes.push(await changeFor(artifact, plan.archiveDir, probes));
|
|
2323
|
+
for (const artifact of group.artifacts) changes.push(await changeFor(artifact, plan.archiveDir, state, probes));
|
|
1572
2324
|
sections.push({
|
|
1573
2325
|
title: group.section,
|
|
1574
2326
|
changes
|
|
@@ -1576,29 +2328,62 @@ async function diff(plan) {
|
|
|
1576
2328
|
}
|
|
1577
2329
|
return sections;
|
|
1578
2330
|
}
|
|
1579
|
-
function changeFor(a$1, archiveDir, probes) {
|
|
2331
|
+
function changeFor(a$1, archiveDir, state, probes) {
|
|
1580
2332
|
switch (a$1.kind) {
|
|
1581
|
-
case "file": return fileChange(a$1);
|
|
2333
|
+
case "file": return fileChange(a$1, archiveDir, state);
|
|
1582
2334
|
case "symlink": return symlinkChange(a$1);
|
|
1583
|
-
case "jsonMerge": return jsonMergeChange(a$1, archiveDir);
|
|
2335
|
+
case "jsonMerge": return jsonMergeChange(a$1, archiveDir, state);
|
|
1584
2336
|
case "install": return installChange(a$1, probes);
|
|
1585
2337
|
}
|
|
1586
2338
|
}
|
|
1587
|
-
async function fileChange(a$1) {
|
|
2339
|
+
async function fileChange(a$1, archiveDir, state) {
|
|
1588
2340
|
const next = await readFile(a$1.src);
|
|
1589
|
-
|
|
2341
|
+
const current = await readFile(a$1.dest).catch(() => null);
|
|
2342
|
+
const record$1 = {
|
|
2343
|
+
file: a$1.dest,
|
|
2344
|
+
hash: hashContent(next)
|
|
2345
|
+
};
|
|
2346
|
+
if (current?.equals(next)) return {
|
|
1590
2347
|
status: "unchanged",
|
|
1591
|
-
label: a$1.dest
|
|
2348
|
+
label: a$1.dest,
|
|
2349
|
+
record: record$1
|
|
2350
|
+
};
|
|
2351
|
+
const action = {
|
|
2352
|
+
do: "write-file",
|
|
2353
|
+
src: a$1.src,
|
|
2354
|
+
dest: a$1.dest,
|
|
2355
|
+
...a$1.mode && { mode: a$1.mode }
|
|
2356
|
+
};
|
|
2357
|
+
if (current === null && a$1.dest in state.files) return {
|
|
2358
|
+
status: "conflict",
|
|
2359
|
+
label: a$1.dest,
|
|
2360
|
+
summary: "was deleted on this machine",
|
|
2361
|
+
diffText: unified(basename(a$1.dest), "", next.toString()),
|
|
2362
|
+
apply: {
|
|
2363
|
+
action,
|
|
2364
|
+
record: record$1
|
|
2365
|
+
},
|
|
2366
|
+
keep: null
|
|
2367
|
+
};
|
|
2368
|
+
if (current !== null && state.files[a$1.dest] !== hashContent(current)) return {
|
|
2369
|
+
status: "conflict",
|
|
2370
|
+
label: a$1.dest,
|
|
2371
|
+
summary: "has content setup did not write",
|
|
2372
|
+
diffText: unified(basename(a$1.dest), current.toString(), next.toString()),
|
|
2373
|
+
apply: {
|
|
2374
|
+
action: {
|
|
2375
|
+
...action,
|
|
2376
|
+
backupTo: archiveDir
|
|
2377
|
+
},
|
|
2378
|
+
record: record$1
|
|
2379
|
+
},
|
|
2380
|
+
keep: null
|
|
1592
2381
|
};
|
|
1593
2382
|
return {
|
|
1594
2383
|
status: "pending",
|
|
1595
2384
|
label: a$1.dest,
|
|
1596
|
-
action
|
|
1597
|
-
|
|
1598
|
-
src: a$1.src,
|
|
1599
|
-
dest: a$1.dest,
|
|
1600
|
-
...a$1.mode && { mode: a$1.mode }
|
|
1601
|
-
}
|
|
2385
|
+
action,
|
|
2386
|
+
record: record$1
|
|
1602
2387
|
};
|
|
1603
2388
|
}
|
|
1604
2389
|
async function symlinkChange(a$1) {
|
|
@@ -1613,7 +2398,8 @@ async function symlinkChange(a$1) {
|
|
|
1613
2398
|
label,
|
|
1614
2399
|
action: link
|
|
1615
2400
|
};
|
|
1616
|
-
|
|
2401
|
+
const st = await lstat(a$1.linkPath);
|
|
2402
|
+
if (st.isSymbolicLink()) {
|
|
1617
2403
|
if (await readlink(a$1.linkPath) === a$1.target) return {
|
|
1618
2404
|
status: "unchanged",
|
|
1619
2405
|
label
|
|
@@ -1634,49 +2420,106 @@ async function symlinkChange(a$1) {
|
|
|
1634
2420
|
};
|
|
1635
2421
|
const archiveTo = join(a$1.archiveDisplaced, basename(a$1.linkPath));
|
|
1636
2422
|
return {
|
|
1637
|
-
status: "
|
|
2423
|
+
status: "conflict",
|
|
1638
2424
|
label,
|
|
1639
|
-
|
|
2425
|
+
summary: `a real ${st.isDirectory() ? "directory" : "file"} sits where the link goes`,
|
|
2426
|
+
diffText: "",
|
|
2427
|
+
apply: { action: {
|
|
1640
2428
|
...link,
|
|
1641
2429
|
archiveTo
|
|
1642
|
-
}
|
|
2430
|
+
} },
|
|
2431
|
+
keep: null
|
|
1643
2432
|
};
|
|
1644
2433
|
}
|
|
1645
|
-
async function jsonMergeChange(a$1, archiveDir) {
|
|
2434
|
+
async function jsonMergeChange(a$1, archiveDir, state) {
|
|
1646
2435
|
const label = basename(a$1.dest);
|
|
1647
2436
|
const raw = await readFile(a$1.dest, "utf8").catch(() => null);
|
|
1648
2437
|
let current = {};
|
|
1649
|
-
if (raw !== null)
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2438
|
+
if (raw !== null) {
|
|
2439
|
+
let parsed;
|
|
2440
|
+
try {
|
|
2441
|
+
parsed = JSON.parse(raw);
|
|
2442
|
+
} catch {
|
|
2443
|
+
return {
|
|
2444
|
+
status: "blocked",
|
|
2445
|
+
label,
|
|
2446
|
+
reason: "not parseable JSON (comments?)"
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {
|
|
1653
2450
|
status: "blocked",
|
|
1654
2451
|
label,
|
|
1655
|
-
reason: "not
|
|
2452
|
+
reason: "not a JSON object"
|
|
1656
2453
|
};
|
|
2454
|
+
current = parsed;
|
|
1657
2455
|
}
|
|
1658
|
-
const
|
|
1659
|
-
...current,
|
|
2456
|
+
const desired = {
|
|
1660
2457
|
...a$1.managed,
|
|
1661
2458
|
...a$1.derived
|
|
1662
2459
|
};
|
|
2460
|
+
const value = {
|
|
2461
|
+
...current,
|
|
2462
|
+
...desired
|
|
2463
|
+
};
|
|
2464
|
+
const record$1 = {
|
|
2465
|
+
file: a$1.dest,
|
|
2466
|
+
keys: Object.fromEntries(Object.keys(desired).map((k) => [k, hashValue(desired[k])]))
|
|
2467
|
+
};
|
|
1663
2468
|
if (stable(current) === stable(value)) return {
|
|
1664
2469
|
status: "unchanged",
|
|
1665
|
-
label
|
|
2470
|
+
label,
|
|
2471
|
+
record: record$1
|
|
1666
2472
|
};
|
|
1667
2473
|
const notes = Object.keys(value).filter((k) => stable(current[k]) !== stable(value[k])).map((k) => `set ${k}${k in a$1.derived ? " (derived)" : ""}`);
|
|
1668
2474
|
const unmanaged = Object.keys(current).filter((k) => !(k in a$1.managed) && !(k in a$1.derived));
|
|
1669
2475
|
if (unmanaged.length) notes.push(`preserved unmanaged keys: ${unmanaged.join(", ")}`);
|
|
1670
|
-
|
|
2476
|
+
const action = {
|
|
2477
|
+
do: "write-json",
|
|
2478
|
+
dest: a$1.dest,
|
|
2479
|
+
value,
|
|
2480
|
+
backupTo: archiveDir
|
|
2481
|
+
};
|
|
2482
|
+
const ours = state.keys[a$1.dest] ?? {};
|
|
2483
|
+
const contested = Object.keys(desired).filter((k) => {
|
|
2484
|
+
if (stable(current[k]) === stable(desired[k])) return false;
|
|
2485
|
+
if (k in current) return ours[k] !== hashValue(current[k]);
|
|
2486
|
+
return k in ours;
|
|
2487
|
+
});
|
|
2488
|
+
if (!contested.length) return {
|
|
1671
2489
|
status: "pending",
|
|
1672
2490
|
label,
|
|
2491
|
+
action,
|
|
2492
|
+
notes,
|
|
2493
|
+
record: record$1
|
|
2494
|
+
};
|
|
2495
|
+
const keepValue = { ...value };
|
|
2496
|
+
for (const k of contested) if (k in current) keepValue[k] = current[k];
|
|
2497
|
+
else delete keepValue[k];
|
|
2498
|
+
const keepRecord = {
|
|
2499
|
+
file: a$1.dest,
|
|
2500
|
+
keys: Object.fromEntries(Object.entries(record$1.keys).filter(([k]) => !contested.includes(k)))
|
|
2501
|
+
};
|
|
2502
|
+
const keep = stable(keepValue) === stable(current) ? null : {
|
|
1673
2503
|
action: {
|
|
1674
2504
|
do: "write-json",
|
|
1675
2505
|
dest: a$1.dest,
|
|
1676
|
-
value,
|
|
2506
|
+
value: keepValue,
|
|
1677
2507
|
backupTo: archiveDir
|
|
1678
2508
|
},
|
|
1679
|
-
|
|
2509
|
+
record: keepRecord,
|
|
2510
|
+
notes: [`kept your values: ${contested.join(", ")}`]
|
|
2511
|
+
};
|
|
2512
|
+
return {
|
|
2513
|
+
status: "conflict",
|
|
2514
|
+
label,
|
|
2515
|
+
summary: `set outside setup: ${contested.join(", ")}`,
|
|
2516
|
+
diffText: contested.map((k) => unified(k, k in current ? pretty(current[k]) : "", pretty(desired[k]))).join("\n"),
|
|
2517
|
+
apply: {
|
|
2518
|
+
action,
|
|
2519
|
+
record: record$1,
|
|
2520
|
+
notes
|
|
2521
|
+
},
|
|
2522
|
+
keep
|
|
1680
2523
|
};
|
|
1681
2524
|
}
|
|
1682
2525
|
async function installChange(a$1, probes) {
|
|
@@ -1698,6 +2541,18 @@ async function installChange(a$1, probes) {
|
|
|
1698
2541
|
}
|
|
1699
2542
|
};
|
|
1700
2543
|
}
|
|
2544
|
+
function pretty(value) {
|
|
2545
|
+
return `${JSON.stringify(value, null, 2) ?? "null"}\n`;
|
|
2546
|
+
}
|
|
2547
|
+
function unified(name$1, before, after) {
|
|
2548
|
+
const patch = structuredPatch(name$1, name$1, before, after);
|
|
2549
|
+
const lines = [`── ${name$1}`];
|
|
2550
|
+
for (const hunk of patch.hunks) {
|
|
2551
|
+
lines.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
|
|
2552
|
+
lines.push(...hunk.lines);
|
|
2553
|
+
}
|
|
2554
|
+
return lines.join("\n");
|
|
2555
|
+
}
|
|
1701
2556
|
/** Memoised machine lookups — `claude plugin list` costs a process spawn, so it runs once. */
|
|
1702
2557
|
var Probes = class {
|
|
1703
2558
|
onPathCache = /* @__PURE__ */ new Map();
|
|
@@ -1753,8 +2608,11 @@ var Probes = class {
|
|
|
1753
2608
|
//#endregion
|
|
1754
2609
|
//#region src/commands/setup/agents/executor.ts
|
|
1755
2610
|
/** The one place `dryRun` is consulted: the same diff is either printed or committed. */
|
|
1756
|
-
async function runPlan(plan, dryRun) {
|
|
1757
|
-
|
|
2611
|
+
async function runPlan(plan, dryRun, resolve$1) {
|
|
2612
|
+
const state = await loadState(plan.stateFile);
|
|
2613
|
+
const before = stable(state);
|
|
2614
|
+
await walk(await diff(plan, state), dryRun ? render : (change) => commit(change, state, resolve$1));
|
|
2615
|
+
if (!dryRun && stable(state) !== before) await saveState(plan.stateFile, state);
|
|
1758
2616
|
}
|
|
1759
2617
|
async function walk(sections, handle) {
|
|
1760
2618
|
for (const { title, changes } of sections) {
|
|
@@ -1769,29 +2627,59 @@ function render(change) {
|
|
|
1769
2627
|
case "pending":
|
|
1770
2628
|
log.plan(phrase(change.action));
|
|
1771
2629
|
for (const note of change.notes ?? []) log.info(note);
|
|
2630
|
+
return;
|
|
2631
|
+
case "conflict":
|
|
2632
|
+
log.warn(`conflict: ${change.label} — ${change.summary}`);
|
|
2633
|
+
log.info("will ask keep / use config / show diff (piped runs keep; --force uses config)");
|
|
1772
2634
|
}
|
|
1773
2635
|
}
|
|
1774
|
-
async function commit(change) {
|
|
2636
|
+
async function commit(change, state, resolve$1) {
|
|
1775
2637
|
switch (change.status) {
|
|
1776
|
-
case "unchanged":
|
|
2638
|
+
case "unchanged":
|
|
2639
|
+
record(state, change.record);
|
|
2640
|
+
return log.skip(`${change.label} (unchanged)`);
|
|
1777
2641
|
case "blocked": return log.fail(`${change.label} — ${change.reason}, leaving it alone`);
|
|
1778
2642
|
case "pending": {
|
|
1779
2643
|
const { ok, notes } = await perform(change.action);
|
|
1780
2644
|
if (!ok) return log.fail(`${change.label}: ${notes.join(" ")}`);
|
|
2645
|
+
record(state, change.record);
|
|
1781
2646
|
log.ok(change.label);
|
|
1782
2647
|
for (const note of [...notes, ...change.notes ?? []]) log.info(note);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
case "conflict": {
|
|
2651
|
+
const outcome = await resolve$1(change) === "apply" ? change.apply : change.keep;
|
|
2652
|
+
if (!outcome) return log.warn(`${change.label} — ${change.summary}; kept as-is (resolve interactively or with --force)`);
|
|
2653
|
+
const { ok, notes } = await perform(outcome.action);
|
|
2654
|
+
if (!ok) return log.fail(`${change.label}: ${notes.join(" ")}`);
|
|
2655
|
+
record(state, outcome.record);
|
|
2656
|
+
log.ok(change.label);
|
|
2657
|
+
for (const note of [...notes, ...outcome.notes ?? []]) log.info(note);
|
|
1783
2658
|
}
|
|
1784
2659
|
}
|
|
1785
2660
|
}
|
|
2661
|
+
function record(state, entry) {
|
|
2662
|
+
if (!entry) return;
|
|
2663
|
+
if ("hash" in entry) state.files[entry.file] = entry.hash;
|
|
2664
|
+
else state.keys[entry.file] = {
|
|
2665
|
+
...state.keys[entry.file],
|
|
2666
|
+
...entry.keys
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
1786
2669
|
async function perform(action) {
|
|
1787
2670
|
switch (action.do) {
|
|
1788
2671
|
case "write-file": {
|
|
2672
|
+
const notes = [];
|
|
2673
|
+
if (action.backupTo) {
|
|
2674
|
+
const bak = await backup(action.dest, action.backupTo);
|
|
2675
|
+
if (bak) notes.push(`backed up → ${bak}`);
|
|
2676
|
+
}
|
|
1789
2677
|
const bytes = await readFile(action.src);
|
|
1790
2678
|
await ensureDir(dirname(action.dest));
|
|
1791
2679
|
await writeFile(action.dest, bytes, action.mode ? { mode: action.mode } : void 0);
|
|
1792
2680
|
return {
|
|
1793
2681
|
ok: true,
|
|
1794
|
-
notes
|
|
2682
|
+
notes
|
|
1795
2683
|
};
|
|
1796
2684
|
}
|
|
1797
2685
|
case "link": {
|
|
@@ -1848,16 +2736,72 @@ function phrase(action) {
|
|
|
1848
2736
|
}
|
|
1849
2737
|
}
|
|
1850
2738
|
|
|
2739
|
+
//#endregion
|
|
2740
|
+
//#region src/commands/setup/agents/resolve.ts
|
|
2741
|
+
var import_picocolors$3 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
2742
|
+
/** `--force`: the config wins every conflict, exactly as if each prompt answered "use config". */
|
|
2743
|
+
const forceResolver = async () => "apply";
|
|
2744
|
+
/** No one to ask — keep what the machine has, dpkg's default for the same reason. */
|
|
2745
|
+
const keepResolver = async () => "keep";
|
|
2746
|
+
function defaultResolver() {
|
|
2747
|
+
return isInteractive() ? interactiveResolver() : keepResolver;
|
|
2748
|
+
}
|
|
2749
|
+
function interactiveResolver(ask = select) {
|
|
2750
|
+
let rest$1 = null;
|
|
2751
|
+
return async (conflict) => {
|
|
2752
|
+
if (rest$1) return rest$1;
|
|
2753
|
+
for (;;) switch (await ask(`${conflict.label} — ${conflict.summary}`, [
|
|
2754
|
+
{
|
|
2755
|
+
value: "keep",
|
|
2756
|
+
label: "Keep what this machine has",
|
|
2757
|
+
hint: "asks again next run"
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
value: "apply",
|
|
2761
|
+
label: "Use the config's version",
|
|
2762
|
+
hint: "existing is backed up"
|
|
2763
|
+
},
|
|
2764
|
+
...conflict.diffText ? [{
|
|
2765
|
+
value: "diff",
|
|
2766
|
+
label: "Show the diff first"
|
|
2767
|
+
}] : [],
|
|
2768
|
+
{
|
|
2769
|
+
value: "keep-all",
|
|
2770
|
+
label: "Keep — here and for every remaining conflict"
|
|
2771
|
+
},
|
|
2772
|
+
{
|
|
2773
|
+
value: "apply-all",
|
|
2774
|
+
label: "Use config — here and for every remaining conflict"
|
|
2775
|
+
}
|
|
2776
|
+
])) {
|
|
2777
|
+
case "diff":
|
|
2778
|
+
printDiff(conflict.diffText);
|
|
2779
|
+
continue;
|
|
2780
|
+
case "apply": return "apply";
|
|
2781
|
+
case "apply-all": return rest$1 = "apply";
|
|
2782
|
+
case "keep-all":
|
|
2783
|
+
case null: return rest$1 = "keep";
|
|
2784
|
+
default: return "keep";
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
function printDiff(text) {
|
|
2789
|
+
for (const line of text.split("\n")) if (line.startsWith("+")) console.log(` ${import_picocolors$3.default.green(line)}`);
|
|
2790
|
+
else if (line.startsWith("-")) console.log(` ${import_picocolors$3.default.red(line)}`);
|
|
2791
|
+
else if (line.startsWith("@@")) console.log(` ${import_picocolors$3.default.cyan(line)}`);
|
|
2792
|
+
else console.log(` ${import_picocolors$3.default.dim(line)}`);
|
|
2793
|
+
}
|
|
2794
|
+
|
|
1851
2795
|
//#endregion
|
|
1852
2796
|
//#region src/commands/setup/agents/apply.ts
|
|
1853
2797
|
/** An agent with no config directory is not on this machine — skip it, never create it. */
|
|
1854
2798
|
function isInstalled(home, emitter) {
|
|
1855
2799
|
return exists(join(home, emitter.configDir));
|
|
1856
2800
|
}
|
|
1857
|
-
async function applyAgents(ctx, ids, dryRun) {
|
|
2801
|
+
async function applyAgents(ctx, ids, dryRun, resolve$1 = defaultResolver()) {
|
|
1858
2802
|
validateExtensions(ctx.config);
|
|
1859
2803
|
logCoverage(ctx.config);
|
|
1860
|
-
await runPlan(await buildStorePlan(ctx), dryRun);
|
|
2804
|
+
await runPlan(await buildStorePlan(ctx), dryRun, resolve$1);
|
|
1861
2805
|
for (const id of ids) {
|
|
1862
2806
|
const emitter = emitters[id];
|
|
1863
2807
|
if (!emitter) continue;
|
|
@@ -1866,7 +2810,7 @@ async function applyAgents(ctx, ids, dryRun) {
|
|
|
1866
2810
|
log.warn(`${id} not installed on this machine — skipping`);
|
|
1867
2811
|
continue;
|
|
1868
2812
|
}
|
|
1869
|
-
await runPlan(await emitter.buildPlan(ctx), dryRun);
|
|
2813
|
+
await runPlan(await emitter.buildPlan(ctx), dryRun, resolve$1);
|
|
1870
2814
|
}
|
|
1871
2815
|
}
|
|
1872
2816
|
|
|
@@ -1881,10 +2825,14 @@ scripted runs apply to every configured agent.
|
|
|
1881
2825
|
|
|
1882
2826
|
plan dry run — show every change, write nothing
|
|
1883
2827
|
|
|
2828
|
+
Anything on the machine that setup did not write is a conflict: interactively you choose
|
|
2829
|
+
keep / use config / see the diff; piped runs keep the machine's version.
|
|
2830
|
+
|
|
1884
2831
|
Options:
|
|
1885
2832
|
-n, --dry-run same as plan
|
|
1886
2833
|
-a, --agent <id> target one agent (repeatable) — skips the prompt
|
|
1887
2834
|
--all every configured agent — skips the prompt
|
|
2835
|
+
--force resolve every conflict in the config's favour, no prompts
|
|
1888
2836
|
--home <dir> apply against a different home directory
|
|
1889
2837
|
-h, --help show this help`;
|
|
1890
2838
|
const agents = {
|
|
@@ -1908,6 +2856,10 @@ const agents = {
|
|
|
1908
2856
|
type: "boolean",
|
|
1909
2857
|
default: false
|
|
1910
2858
|
},
|
|
2859
|
+
force: {
|
|
2860
|
+
type: "boolean",
|
|
2861
|
+
default: false
|
|
2862
|
+
},
|
|
1911
2863
|
home: { type: "string" },
|
|
1912
2864
|
help: {
|
|
1913
2865
|
type: "boolean",
|
|
@@ -1945,7 +2897,7 @@ const agents = {
|
|
|
1945
2897
|
home,
|
|
1946
2898
|
repoRoot: packageRoot$1,
|
|
1947
2899
|
config
|
|
1948
|
-
}, selected, dryRun);
|
|
2900
|
+
}, selected, dryRun, values.force ? forceResolver : defaultResolver());
|
|
1949
2901
|
console.log(dryRun ? `\n${import_picocolors$2.default.yellow("dry run complete — nothing written")}\n` : `\n${import_picocolors$2.default.green("done")}\n`);
|
|
1950
2902
|
return 0;
|
|
1951
2903
|
}
|
|
@@ -2009,7 +2961,7 @@ async function version() {
|
|
|
2009
2961
|
}
|
|
2010
2962
|
function usage() {
|
|
2011
2963
|
console.log(`Usage: pakhale <command> [options]\n\nCommands:`);
|
|
2012
|
-
for (const c of Object.values(commands)) console.log(` ${c.name.padEnd(10)} ${c.describe}`);
|
|
2964
|
+
for (const c$1 of Object.values(commands)) console.log(` ${c$1.name.padEnd(10)} ${c$1.describe}`);
|
|
2013
2965
|
console.log(`\nOptions:\n -h, --help show this help\n -v, --version show version`);
|
|
2014
2966
|
}
|
|
2015
2967
|
if (!name || name === "--help" || name === "-h") {
|