@tommy-ca/lazypi 0.10.1 → 0.10.2
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/bin/lazypi.mjs +91 -31
- package/package.json +1 -1
package/bin/lazypi.mjs
CHANGED
|
@@ -96,6 +96,25 @@ function renderLogo() {
|
|
|
96
96
|
// ---------------------------------------------------------------------------
|
|
97
97
|
const KNOWN_COMMANDS = new Set(["install", "status", "update", "doctor", "remove"]);
|
|
98
98
|
|
|
99
|
+
function assignSelector(flags, key, raw) {
|
|
100
|
+
const list = parseList(raw);
|
|
101
|
+
if (list.length === 0) {
|
|
102
|
+
flags.usageError ??= `--${key} requires a non-empty list`;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
flags[key] = list;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function consumeSelector(args, i, flags, key) {
|
|
109
|
+
const value = args[i + 1];
|
|
110
|
+
if (value == null || value.startsWith("-")) {
|
|
111
|
+
flags.usageError ??= `--${key} requires a non-empty list`;
|
|
112
|
+
return i;
|
|
113
|
+
}
|
|
114
|
+
assignSelector(flags, key, value);
|
|
115
|
+
return i + 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
function parseArgs(args) {
|
|
100
119
|
const flags = {
|
|
101
120
|
command: "install",
|
|
@@ -107,6 +126,7 @@ function parseArgs(args) {
|
|
|
107
126
|
only: null,
|
|
108
127
|
except: null,
|
|
109
128
|
targets: [],
|
|
129
|
+
usageError: null,
|
|
110
130
|
};
|
|
111
131
|
|
|
112
132
|
let i = 0;
|
|
@@ -121,10 +141,10 @@ function parseArgs(args) {
|
|
|
121
141
|
else if (arg === "-y" || arg === "--yes") flags.yes = true;
|
|
122
142
|
else if (arg === "-V" || arg === "--version") flags.version = true;
|
|
123
143
|
else if (arg === "-h" || arg === "--help") flags.help = true;
|
|
124
|
-
else if (arg === "--only")
|
|
125
|
-
else if (arg.startsWith("--only=")) flags
|
|
126
|
-
else if (arg === "--except")
|
|
127
|
-
else if (arg.startsWith("--except=")) flags
|
|
144
|
+
else if (arg === "--only") i = consumeSelector(args, i, flags, "only");
|
|
145
|
+
else if (arg.startsWith("--only=")) assignSelector(flags, "only", arg.slice("--only=".length));
|
|
146
|
+
else if (arg === "--except") i = consumeSelector(args, i, flags, "except");
|
|
147
|
+
else if (arg.startsWith("--except=")) assignSelector(flags, "except", arg.slice("--except=".length));
|
|
128
148
|
else if (flags.command === "remove" && !arg.startsWith("-")) flags.targets.push(arg);
|
|
129
149
|
else {
|
|
130
150
|
console.error(red(`Unknown argument: ${arg}`));
|
|
@@ -134,6 +154,10 @@ function parseArgs(args) {
|
|
|
134
154
|
}
|
|
135
155
|
}
|
|
136
156
|
|
|
157
|
+
if (flags.only && flags.except) {
|
|
158
|
+
flags.usageError ??= "Use either --only or --except, not both";
|
|
159
|
+
}
|
|
160
|
+
|
|
137
161
|
return flags;
|
|
138
162
|
}
|
|
139
163
|
|
|
@@ -152,8 +176,9 @@ function validateSelectors(list, label) {
|
|
|
152
176
|
console.error(red(`Unknown ${label}: ${bad.join(", ")}`));
|
|
153
177
|
console.error(`Valid categories: ${CATEGORIES.join(", ")}`);
|
|
154
178
|
console.error(`Valid package ids: ${[...ids].join(", ")}`);
|
|
155
|
-
|
|
179
|
+
return false;
|
|
156
180
|
}
|
|
181
|
+
return true;
|
|
157
182
|
}
|
|
158
183
|
|
|
159
184
|
function matchesSelector(pkg, selectors) {
|
|
@@ -162,11 +187,11 @@ function matchesSelector(pkg, selectors) {
|
|
|
162
187
|
|
|
163
188
|
function resolveSelection(flags) {
|
|
164
189
|
if (flags.only) {
|
|
165
|
-
validateSelectors(flags.only, "--only");
|
|
190
|
+
if (!validateSelectors(flags.only, "--only")) return null;
|
|
166
191
|
return new Set(PACKAGES.filter((p) => matchesSelector(p, flags.only)).map((p) => p.id));
|
|
167
192
|
}
|
|
168
193
|
if (flags.except) {
|
|
169
|
-
validateSelectors(flags.except, "--except");
|
|
194
|
+
if (!validateSelectors(flags.except, "--except")) return null;
|
|
170
195
|
return new Set(PACKAGES.filter((p) => !matchesSelector(p, flags.except)).map((p) => p.id));
|
|
171
196
|
}
|
|
172
197
|
return new Set(PACKAGES.filter((p) => p.category === "core").map((p) => p.id));
|
|
@@ -224,18 +249,13 @@ ${bold("Examples:")}
|
|
|
224
249
|
// Pi / settings plumbing
|
|
225
250
|
// ---------------------------------------------------------------------------
|
|
226
251
|
const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// shell, so we route spawned commands through the platform shell there while
|
|
230
|
-
// keeping direct execution on Unix.
|
|
231
|
-
export function buildSpawnOptions(options = {}, platformName = platform()) {
|
|
232
|
-
const resolved = { ...options };
|
|
233
|
-
if (platformName === "win32" && resolved.shell == null) resolved.shell = true;
|
|
234
|
-
return resolved;
|
|
252
|
+
export function buildSpawnOptions(options = {}, _platformName = platform()) {
|
|
253
|
+
return { ...options };
|
|
235
254
|
}
|
|
236
255
|
|
|
237
256
|
function spawnCommand(command, args = [], options = {}) {
|
|
238
|
-
|
|
257
|
+
const resolved = command === "pi" || command === "npm" ? commandPath(command) ?? command : command;
|
|
258
|
+
return spawnSync(resolved, args, buildSpawnOptions(options));
|
|
239
259
|
}
|
|
240
260
|
|
|
241
261
|
function hasCmd(name) {
|
|
@@ -296,11 +316,19 @@ function writeSettings(local, mutate) {
|
|
|
296
316
|
return { ok: true, path: current.path, backup, changed: true };
|
|
297
317
|
}
|
|
298
318
|
|
|
299
|
-
function writeSubagentOverrides(local) {
|
|
319
|
+
export function writeSubagentOverrides(local) {
|
|
300
320
|
return writeSettings(local, (settings) => {
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
321
|
+
const existing = settings.subagents?.agentOverrides ?? {};
|
|
322
|
+
const overrides = { ...existing };
|
|
323
|
+
let changed = false;
|
|
324
|
+
for (const name of SUBAGENT_BUILTIN_MODELS) {
|
|
325
|
+
const current = overrides[name];
|
|
326
|
+
if (current && typeof current === "object" && !Array.isArray(current) && current.model === "") continue;
|
|
327
|
+
overrides[name] = { ...(current && typeof current === "object" && !Array.isArray(current) ? current : {}), model: "" };
|
|
328
|
+
changed = true;
|
|
329
|
+
}
|
|
330
|
+
if (!changed) return false;
|
|
331
|
+
settings.subagents = { ...(settings.subagents ?? {}), agentOverrides: overrides };
|
|
304
332
|
return true;
|
|
305
333
|
});
|
|
306
334
|
}
|
|
@@ -325,12 +353,16 @@ function readInstalledSources(local) {
|
|
|
325
353
|
|
|
326
354
|
function commandPath(name) {
|
|
327
355
|
const command = platform() === "win32" ? "where" : "which";
|
|
328
|
-
const probe =
|
|
356
|
+
const probe = spawnSync(command, [name], buildSpawnOptions({ encoding: "utf8" }));
|
|
329
357
|
if (probe.status !== 0) return null;
|
|
330
358
|
const first = String(probe.stdout ?? "").split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
331
359
|
return first || null;
|
|
332
360
|
}
|
|
333
361
|
|
|
362
|
+
function printSettingsParseError(current) {
|
|
363
|
+
console.error(red(`Could not parse ${current.path} — ${current.error}`));
|
|
364
|
+
}
|
|
365
|
+
|
|
334
366
|
function legacySourcesForPackage(pkg) {
|
|
335
367
|
return Array.isArray(pkg.legacySources) ? pkg.legacySources : [];
|
|
336
368
|
}
|
|
@@ -524,6 +556,7 @@ async function ensurePi(flags) {
|
|
|
524
556
|
// ---------------------------------------------------------------------------
|
|
525
557
|
async function cmdInstall(flags) {
|
|
526
558
|
let selectedIds = resolveSelection(flags);
|
|
559
|
+
if (!selectedIds) return 2;
|
|
527
560
|
|
|
528
561
|
const usedSelectionFlag = Boolean(flags.only || flags.except);
|
|
529
562
|
const interactive = !flags.yes && !usedSelectionFlag && isInteractive();
|
|
@@ -532,6 +565,18 @@ async function cmdInstall(flags) {
|
|
|
532
565
|
console.log(renderLogo());
|
|
533
566
|
intro(bold("LazyPi"));
|
|
534
567
|
}
|
|
568
|
+
|
|
569
|
+
const settings = readSettings(flags.local);
|
|
570
|
+
if (settings.error) {
|
|
571
|
+
if (interactive) {
|
|
572
|
+
log.error(`Could not parse ${settings.path} — ${settings.error}`);
|
|
573
|
+
outro(red("Aborted."));
|
|
574
|
+
} else {
|
|
575
|
+
printSettingsParseError(settings);
|
|
576
|
+
}
|
|
577
|
+
return 2;
|
|
578
|
+
}
|
|
579
|
+
|
|
535
580
|
if (!(await ensurePi(flags))) return 127;
|
|
536
581
|
|
|
537
582
|
if (interactive) {
|
|
@@ -550,8 +595,7 @@ async function cmdInstall(flags) {
|
|
|
550
595
|
return 0;
|
|
551
596
|
}
|
|
552
597
|
|
|
553
|
-
const { sources: installedSources
|
|
554
|
-
if (settingsError) log.warn(`Could not parse ${settingsPath(flags.local)} — ${settingsError}`);
|
|
598
|
+
const { sources: installedSources } = readInstalledSources(flags.local);
|
|
555
599
|
|
|
556
600
|
const toInstall = selected.filter((pkg) => {
|
|
557
601
|
return !isPackageInstalled(pkg, installedSources);
|
|
@@ -706,7 +750,7 @@ function cmdStatus(flags) {
|
|
|
706
750
|
console.log(yellow(" (not found — Pi has not written settings yet)"));
|
|
707
751
|
} else if (error) {
|
|
708
752
|
console.error(red(` could not parse: ${error}`));
|
|
709
|
-
return
|
|
753
|
+
return 2;
|
|
710
754
|
}
|
|
711
755
|
|
|
712
756
|
const installed = PACKAGES.filter((pkg) => packageInstallStatus(pkg, sources).installed);
|
|
@@ -746,14 +790,14 @@ function cmdStatus(flags) {
|
|
|
746
790
|
// update
|
|
747
791
|
// ---------------------------------------------------------------------------
|
|
748
792
|
async function cmdUpdate(flags) {
|
|
749
|
-
if (!(await ensurePi(flags))) return 127;
|
|
750
|
-
|
|
751
793
|
const settings = readSettings(flags.local);
|
|
752
794
|
if (settings.error) {
|
|
753
|
-
|
|
754
|
-
return
|
|
795
|
+
printSettingsParseError(settings);
|
|
796
|
+
return 2;
|
|
755
797
|
}
|
|
756
798
|
|
|
799
|
+
if (!(await ensurePi(flags))) return 127;
|
|
800
|
+
|
|
757
801
|
console.log(bold("pi update"));
|
|
758
802
|
|
|
759
803
|
return spawnCommand("pi", flags.local ? ["update", "--extensions"] : ["update"], { stdio: "inherit" }).status ?? 1;
|
|
@@ -763,6 +807,12 @@ async function cmdUpdate(flags) {
|
|
|
763
807
|
// doctor
|
|
764
808
|
// ---------------------------------------------------------------------------
|
|
765
809
|
function cmdDoctor(flags) {
|
|
810
|
+
const settings = readSettings(flags.local);
|
|
811
|
+
if (settings.error) {
|
|
812
|
+
printSettingsParseError(settings);
|
|
813
|
+
return 2;
|
|
814
|
+
}
|
|
815
|
+
|
|
766
816
|
let problems = 0;
|
|
767
817
|
let warnings = 0;
|
|
768
818
|
const pass = (msg) => console.log(` ${green("✓")} ${msg}`);
|
|
@@ -831,12 +881,18 @@ function cmdDoctor(flags) {
|
|
|
831
881
|
// remove
|
|
832
882
|
// ---------------------------------------------------------------------------
|
|
833
883
|
async function cmdRemove(flags, targets) {
|
|
884
|
+
const installed = readInstalledSources(flags.local);
|
|
885
|
+
if (installed.error) {
|
|
886
|
+
printSettingsParseError(installed);
|
|
887
|
+
return 2;
|
|
888
|
+
}
|
|
889
|
+
|
|
834
890
|
if (targets.length === 0) {
|
|
835
891
|
if (!isInteractive()) {
|
|
836
892
|
console.error(red("Usage: npx @tommy-ca/lazypi remove <id|source> [...]"));
|
|
837
893
|
return 2;
|
|
838
894
|
}
|
|
839
|
-
const { sources } =
|
|
895
|
+
const { sources } = installed;
|
|
840
896
|
const installedPkgs = PACKAGES.filter((p) => isPackagePresent(p, sources));
|
|
841
897
|
if (installedPkgs.length === 0) {
|
|
842
898
|
console.log(yellow("No catalog packages are installed."));
|
|
@@ -860,10 +916,9 @@ async function cmdRemove(flags, targets) {
|
|
|
860
916
|
targets = picked;
|
|
861
917
|
}
|
|
862
918
|
|
|
863
|
-
const { sources: installedSources } =
|
|
919
|
+
const { sources: installedSources } = installed;
|
|
864
920
|
let exitCode = 0;
|
|
865
921
|
for (const target of targets) {
|
|
866
|
-
// Resolve a catalog id to its source string, or pass through raw sources
|
|
867
922
|
const pkg = PACKAGES.find((p) => p.id === target);
|
|
868
923
|
const source = pkg ? pkg.source : target;
|
|
869
924
|
|
|
@@ -896,6 +951,11 @@ async function main() {
|
|
|
896
951
|
console.log(`@tommy-ca/lazypi ${VERSION}`);
|
|
897
952
|
return 0;
|
|
898
953
|
}
|
|
954
|
+
if (flags.usageError) {
|
|
955
|
+
console.error(red(flags.usageError));
|
|
956
|
+
printHelp();
|
|
957
|
+
return 2;
|
|
958
|
+
}
|
|
899
959
|
if (flags.help) {
|
|
900
960
|
printHelp();
|
|
901
961
|
return flags.badArg ? 2 : 0;
|