skillfoxx 0.1.1 → 0.1.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/README.en.md +2 -1
- package/README.md +2 -1
- package/dist/cli.js +75 -8
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -16,7 +16,7 @@ Requires Node.js 18.18 or newer.
|
|
|
16
16
|
| `remove <entry>` | removes what SkillFoxx installed |
|
|
17
17
|
| `update [entry]` | updates to the version SkillFoxx has rechecked |
|
|
18
18
|
| `list` | shows what is installed |
|
|
19
|
-
| `doctor` | checks installs: edited by hand, outdated, removed from the catalog |
|
|
19
|
+
| `doctor` | checks installs: edited by hand, outdated, removed from the catalog, environment variables not set |
|
|
20
20
|
| `search <query>` | searches the catalog |
|
|
21
21
|
|
|
22
22
|
Options: `--agent claude-code,cursor` (or `all`), `--project`, `--global`, `-y`/`--yes`, `--force`, `--dry-run`, `--json`, `--lang ru|en`.
|
|
@@ -41,6 +41,7 @@ Without `--agent` the CLI picks the agent it runs inside, otherwise every agent
|
|
|
41
41
|
- A key SkillFoxx did not install, or one edited by hand, is not replaced without `--force`.
|
|
42
42
|
- In a project a secret never goes into a file: the CLI writes an environment variable reference if the agent supports it, otherwise it prints instructions and suggests installing with `--global`.
|
|
43
43
|
- For the user scope the secret value goes into the agent config, the way the agents do it themselves. Lock files hold no values.
|
|
44
|
+
- `doctor` checks required variables against the current environment (names and status only, no values; secrets are listed first among problems). On macOS an app opened from the Dock or Launchpad does not see exports from `~/.zshrc`: after `add`, such variables get a hint about `launchctl setenv` or starting the agent from a terminal where the variable is already set.
|
|
44
45
|
|
|
45
46
|
## Lock files
|
|
46
47
|
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npx skillfoxx add mcp/vv-mcp-server
|
|
|
16
16
|
| `remove <запись>` | удаляет то, что поставил SkillFoxx |
|
|
17
17
|
| `update [запись]` | обновляет до версии, которую сверил SkillFoxx |
|
|
18
18
|
| `list` | показывает установленное |
|
|
19
|
-
| `doctor` | проверяет: изменено руками, устарело, снято из
|
|
19
|
+
| `doctor` | проверяет: изменено руками, устарело, снято из каталога, не заданы переменные окружения |
|
|
20
20
|
| `search <запрос>` | ищет в каталоге |
|
|
21
21
|
|
|
22
22
|
Параметры: `--agent claude-code,cursor` (или `all`), `--project`, `--global`, `-y`/`--yes`, `--force`, `--dry-run`, `--json`, `--lang ru|en`.
|
|
@@ -41,6 +41,7 @@ Claude Code, Cursor, VS Code, Codex, Gemini CLI, Devin, Cline, Zoo Code, OpenCod
|
|
|
41
41
|
- Ключ, который поставил не SkillFoxx или который изменили руками, без `--force` не заменяется.
|
|
42
42
|
- В проекте секрет не пишется в файл никогда: пишется ссылка на переменную окружения, если агент ее понимает, иначе печатается инструкция и совет поставить с `--global`.
|
|
43
43
|
- Для пользователя значение секрета пишется в конфиг агента, как это делают сами агенты. В lock-файле значений нет.
|
|
44
|
+
- `doctor` сверяет обязательные переменные записей с текущим окружением (только имена и статус, без значений; секреты в списке проблем идут первыми). На macOS приложение, открытое из Dock или Launchpad, не видит экспорт из `~/.zshrc`: после `add` для таких переменных печатается подсказка про `launchctl setenv` или запуск агента из терминала, где переменная уже задана.
|
|
44
45
|
|
|
45
46
|
## Lock-файлы
|
|
46
47
|
|
package/dist/cli.js
CHANGED
|
@@ -20078,7 +20078,7 @@ var findOnPath = async (ctx, bin) => {
|
|
|
20078
20078
|
};
|
|
20079
20079
|
|
|
20080
20080
|
// src/version.ts
|
|
20081
|
-
var VERSION = true ? "0.1.
|
|
20081
|
+
var VERSION = true ? "0.1.2" : "0.0.0-dev";
|
|
20082
20082
|
|
|
20083
20083
|
// src/api.ts
|
|
20084
20084
|
var DAY = 24 * 36e5;
|
|
@@ -22465,6 +22465,33 @@ var describePlan = (ctx, actions, root) => actions.map((a) => {
|
|
|
22465
22465
|
var planJson = (actions, root) => actions.map(
|
|
22466
22466
|
(a) => a.type === "mcp" ? { type: a.type, agent: a.agent, component: a.component.name, file: path5.relative(root, a.target.file), key: a.target.key } : { type: a.type, agent: "agent" in a ? a.agent : null, component: componentName(a.component), ...a.type === "manual" ? { reason: a.reason.en } : {} }
|
|
22467
22467
|
);
|
|
22468
|
+
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
|
|
22469
|
+
var isLocalHost = (url2) => {
|
|
22470
|
+
try {
|
|
22471
|
+
const host = new URL(url2).hostname.toLowerCase();
|
|
22472
|
+
return LOCAL_HOSTS.has(host) || host.endsWith(".localhost");
|
|
22473
|
+
} catch {
|
|
22474
|
+
return false;
|
|
22475
|
+
}
|
|
22476
|
+
};
|
|
22477
|
+
var remoteAuthWarnings = (ctx, recipe) => {
|
|
22478
|
+
const warnings = [];
|
|
22479
|
+
for (const c of recipe.components) {
|
|
22480
|
+
if (c.kind !== "mcp-http" || isLocalHost(c.url)) continue;
|
|
22481
|
+
const authVars = new Set(c.headers.filter((h) => h.secret).flatMap((h) => templateVars(h.value)));
|
|
22482
|
+
const missing = [...authVars].filter((name2) => !ctx.env[name2]);
|
|
22483
|
+
if (missing.length) {
|
|
22484
|
+
warnings.push(
|
|
22485
|
+
tr(
|
|
22486
|
+
ctx,
|
|
22487
|
+
`${clean(c.name)}: \u0441\u0435\u0440\u0432\u0435\u0440 ${clean(c.url)} \u0431\u0443\u0434\u0435\u0442 \u0432\u044B\u0437\u0432\u0430\u043D \u0431\u0435\u0437 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043F\u043E\u043A\u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u0430 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F ${missing.join(", ")}.`,
|
|
22488
|
+
`${clean(c.name)}: the server ${clean(c.url)} will be called without authorization until the environment variable ${missing.join(", ")} is set.`
|
|
22489
|
+
)
|
|
22490
|
+
);
|
|
22491
|
+
}
|
|
22492
|
+
}
|
|
22493
|
+
return warnings;
|
|
22494
|
+
};
|
|
22468
22495
|
var manualNote = (ctx, action, entryUrl) => {
|
|
22469
22496
|
const c = action.component;
|
|
22470
22497
|
const who = action.agent ? agentDef(action.agent).title : tr(ctx, "\u0412\u0440\u0443\u0447\u043D\u0443\u044E", "By hand");
|
|
@@ -22569,6 +22596,14 @@ var downloadSkill = async (ctx, source, loaded, component2) => {
|
|
|
22569
22596
|
};
|
|
22570
22597
|
|
|
22571
22598
|
// src/lock.ts
|
|
22599
|
+
var recipeVars = (recipe) => {
|
|
22600
|
+
const out = /* @__PURE__ */ new Map();
|
|
22601
|
+
for (const c of recipe.components) {
|
|
22602
|
+
if (c.kind !== "mcp-stdio" && c.kind !== "mcp-http") continue;
|
|
22603
|
+
for (const e of c.env) if (!out.has(e.name)) out.set(e.name, { name: e.name, required: e.required, secret: e.secret });
|
|
22604
|
+
}
|
|
22605
|
+
return [...out.values()];
|
|
22606
|
+
};
|
|
22572
22607
|
var LOCK_FILE = "skillfoxx-lock.json";
|
|
22573
22608
|
var exists2 = async (p) => Boolean(await stat4(p).catch(() => null));
|
|
22574
22609
|
var upward = async (from, markers) => {
|
|
@@ -22605,6 +22640,7 @@ var lockPathOk = (scope, p) => {
|
|
|
22605
22640
|
const abs = p.replace(/^[A-Za-z]:/, "");
|
|
22606
22641
|
return abs.startsWith("/") && safeRelative(abs.slice(1));
|
|
22607
22642
|
};
|
|
22643
|
+
var varOk = (v) => isObj(v) && isStr(v.name) && VAR_NAME.test(v.name) && typeof v.required === "boolean" && typeof v.secret === "boolean";
|
|
22608
22644
|
var itemOk = (scope, i) => {
|
|
22609
22645
|
if (!isObj(i)) return false;
|
|
22610
22646
|
switch (i.kind) {
|
|
@@ -22625,7 +22661,7 @@ var itemOk = (scope, i) => {
|
|
|
22625
22661
|
var entryOk = (scope, key, e) => {
|
|
22626
22662
|
const m = ENTRY_KEY.exec(key);
|
|
22627
22663
|
if (!m || !isObj(e)) return false;
|
|
22628
|
-
return e.section === m[1] && e.slug === m[2] && e.scope === scope && isStr(e.recipeHash) && strOrNull(e.commitSha) && strOrNull(e.contentDigest) && isStr(e.status) && strOrNull(e.risk) && Array.isArray(e.agents) && e.agents.every(isAgent) && Array.isArray(e.items) && e.items.every((i) => itemOk(scope, i)) && isStr(e.installedAt) && isStr(e.updatedAt) && isStr(e.cliVersion);
|
|
22664
|
+
return e.section === m[1] && e.slug === m[2] && e.scope === scope && isStr(e.recipeHash) && strOrNull(e.commitSha) && strOrNull(e.contentDigest) && isStr(e.status) && strOrNull(e.risk) && Array.isArray(e.agents) && e.agents.every(isAgent) && Array.isArray(e.items) && e.items.every((i) => itemOk(scope, i)) && (e.vars === void 0 || Array.isArray(e.vars) && e.vars.every(varOk)) && isStr(e.installedAt) && isStr(e.updatedAt) && isStr(e.cliVersion);
|
|
22629
22665
|
};
|
|
22630
22666
|
var readLock = async (file3, scope) => {
|
|
22631
22667
|
let text;
|
|
@@ -22979,7 +23015,7 @@ var knownSecrets = async (ctx, p) => {
|
|
|
22979
23015
|
var install = async (ctx, p) => {
|
|
22980
23016
|
const backup = new Backup(ctx);
|
|
22981
23017
|
const created = [];
|
|
22982
|
-
const res = { items: [], notes: [], touched: /* @__PURE__ */ new Set(), cliInstalled: false };
|
|
23018
|
+
const res = { items: [], notes: [], touched: /* @__PURE__ */ new Set(), cliInstalled: false, requiredEnvRefs: /* @__PURE__ */ new Set() };
|
|
22983
23019
|
const prev = p.previous?.items ?? [];
|
|
22984
23020
|
const owned = new Set(prev.flatMap((i) => i.kind === "skill" ? [lockItemTarget(ctx, p.scope, p.root, i)] : []).filter((x) => Boolean(x)));
|
|
22985
23021
|
let rolledBack = null;
|
|
@@ -23087,6 +23123,7 @@ var installMcp = async (ctx, p, action, values, backup, prev, res) => {
|
|
|
23087
23123
|
if (p.scope === "project" && action.agent === "codex") res.notes.push(tr(ctx, "Codex \u0447\u0438\u0442\u0430\u0435\u0442 .codex/config.toml \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0445 \u043F\u0440\u043E\u0435\u043A\u0442\u0430\u0445.", "Codex reads .codex/config.toml only in trusted projects."));
|
|
23088
23124
|
if (p.scope === "project" && action.agent === "amp") res.notes.push(tr(ctx, "Amp: \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0435\u0440 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 amp mcp approve.", "Amp: approve the server with amp mcp approve."));
|
|
23089
23125
|
if (r.references.length) res.notes.push(tr(ctx, `\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043F\u0435\u0440\u0435\u0434 \u0437\u0430\u043F\u0443\u0441\u043A\u043E\u043C \u0430\u0433\u0435\u043D\u0442\u0430: ${r.references.join(", ")}`, `Set environment variables before starting the agent: ${r.references.join(", ")}`));
|
|
23126
|
+
for (const name2 of r.references) if (r.component.env.find((e) => e.name === name2)?.required) res.requiredEnvRefs.add(name2);
|
|
23090
23127
|
if (r.secrets.length) res.notes.push(tr(ctx, `${title}: \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0441\u0435\u043A\u0440\u0435\u0442\u043E\u0432 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u044B \u0432 ${file3}.`, `${title}: secret values are stored in ${file3}.`));
|
|
23091
23128
|
};
|
|
23092
23129
|
var installCli = async (ctx, p, action, res) => {
|
|
@@ -23167,6 +23204,21 @@ var findInstalled = async (ctx, input2, flags) => {
|
|
|
23167
23204
|
};
|
|
23168
23205
|
|
|
23169
23206
|
// src/commands/add.ts
|
|
23207
|
+
var envHint = (ctx, names) => {
|
|
23208
|
+
const list = names.join(", ");
|
|
23209
|
+
if (ctx.platform === "darwin") {
|
|
23210
|
+
return tr(
|
|
23211
|
+
ctx,
|
|
23212
|
+
`\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B \u0430\u0433\u0435\u043D\u0442\u0443 \u043F\u0440\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0435: ${list}. \u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435, \u043E\u0442\u043A\u0440\u044B\u0442\u043E\u0435 \u0438\u0437 Dock \u0438\u043B\u0438 Launchpad, \u043D\u0435 \u0432\u0438\u0434\u0438\u0442 \u044D\u043A\u0441\u043F\u043E\u0440\u0442 \u0438\u0437 ~/.zshrc. \u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 launchctl setenv \u0418\u041C\u042F \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 (\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u043E \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438) \u0438\u043B\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0439\u0442\u0435 \u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u0437 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430, \u0433\u0434\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u0443\u0436\u0435 \u0437\u0430\u0434\u0430\u043D\u0430, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 open -a Cursor \u0438\u0437 \u044D\u0442\u043E\u0433\u043E \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430.`,
|
|
23213
|
+
`The agent needs these environment variables at launch: ${list}. An app opened from the Dock or Launchpad does not see exports from ~/.zshrc. Set the value with launchctl setenv NAME value (lasts until reboot), or start the agent from a terminal where the variable is already set, for example open -a Cursor from that terminal.`
|
|
23214
|
+
);
|
|
23215
|
+
}
|
|
23216
|
+
return tr(
|
|
23217
|
+
ctx,
|
|
23218
|
+
`\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B \u0430\u0433\u0435\u043D\u0442\u0443 \u043F\u0440\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0435: ${list}. \u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u0438\u0445 \u0432 \u043F\u0440\u043E\u0444\u0438\u043B\u0435 \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0438 \u0438\u043B\u0438 \u0432 \u0441\u043E\u0431\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0445 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u0430\u0433\u0435\u043D\u0442\u0430.`,
|
|
23219
|
+
`The agent needs these environment variables at launch: ${list}. Set them in your shell profile or in the agent's own environment settings.`
|
|
23220
|
+
);
|
|
23221
|
+
};
|
|
23170
23222
|
var runAdd = async (ctx, args, flags) => {
|
|
23171
23223
|
if (args.length !== 1) throw new CliError(64, tr(ctx, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043E\u0434\u043D\u0443 \u0437\u0430\u043F\u0438\u0441\u044C: npx skillfoxx add <\u0440\u0430\u0437\u0434\u0435\u043B>/<slug>", "Name one entry: npx skillfoxx add <section>/<slug>"));
|
|
23172
23224
|
const { section, slug } = await resolveTarget(ctx, args[0]);
|
|
@@ -23176,7 +23228,7 @@ var runAdd = async (ctx, args, flags) => {
|
|
|
23176
23228
|
if (!g.ok) throw new CliError(g.code, g.message);
|
|
23177
23229
|
const b = body;
|
|
23178
23230
|
const recipe = b.recipe;
|
|
23179
|
-
for (const w of [...g.warnings, ...await checkRuntimes(ctx, recipe)]) ctx.err(w);
|
|
23231
|
+
for (const w of [...g.warnings, ...await checkRuntimes(ctx, recipe), ...remoteAuthWarnings(ctx, recipe)]) ctx.err(w);
|
|
23180
23232
|
await riskConsent(ctx, g, Boolean(flags.yes));
|
|
23181
23233
|
const scope = await pickScope(ctx, flags);
|
|
23182
23234
|
const root = await rootFor(ctx, scope);
|
|
@@ -23207,12 +23259,15 @@ var runAdd = async (ctx, args, flags) => {
|
|
|
23207
23259
|
scope,
|
|
23208
23260
|
agents: [.../* @__PURE__ */ new Set([...previous?.agents ?? [], ...result.touched])],
|
|
23209
23261
|
items: mergeItems(previous?.items ?? [], result.items),
|
|
23262
|
+
vars: recipeVars(recipe),
|
|
23210
23263
|
installedAt: previous?.installedAt ?? now,
|
|
23211
23264
|
updatedAt: now,
|
|
23212
23265
|
cliVersion: VERSION
|
|
23213
23266
|
};
|
|
23214
23267
|
await writeLock(lockFile, lock);
|
|
23215
23268
|
for (const note of new Set(result.notes)) ctx.out(note);
|
|
23269
|
+
const missingEnv = [...result.requiredEnvRefs].filter((n) => !ctx.env[n]);
|
|
23270
|
+
if (missingEnv.length) ctx.out(envHint(ctx, missingEnv));
|
|
23216
23271
|
await sendEvents(ctx, installEvents(key, result, previous ? "update" : "add"));
|
|
23217
23272
|
ctx.out(tr(ctx, `\u0413\u043E\u0442\u043E\u0432\u043E: ${key}`, `Done: ${key}`));
|
|
23218
23273
|
return 0;
|
|
@@ -23220,6 +23275,8 @@ var runAdd = async (ctx, args, flags) => {
|
|
|
23220
23275
|
|
|
23221
23276
|
// src/commands/doctor.ts
|
|
23222
23277
|
import { lstat as lstat3, stat as stat5 } from "node:fs/promises";
|
|
23278
|
+
var envRows = (ctx, key, scope, vars) => (vars ?? []).map((v) => ({ key, scope, name: v.name, required: v.required, secret: v.secret, status: ctx.env[v.name] ? "ok" : "missing" }));
|
|
23279
|
+
var envProblems = (ctx, rows) => rows.filter((r) => r.required && r.status === "missing").sort((a, b) => Number(b.secret) - Number(a.secret)).map((r) => ({ key: r.key, scope: r.scope, kind: "env", detail: r.secret ? tr(ctx, `${r.name} (\u0441\u0435\u043A\u0440\u0435\u0442)`, `${r.name} (secret)`) : r.name }));
|
|
23223
23280
|
var localProblems = async (ctx, key, scope, entry, root) => {
|
|
23224
23281
|
const out = [];
|
|
23225
23282
|
for (const item of entry.items) {
|
|
@@ -23259,15 +23316,20 @@ var LABEL = {
|
|
|
23259
23316
|
blocked: ["\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430", "installation is blocked"],
|
|
23260
23317
|
unsupported: ["\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F", "automatic install is no longer supported"],
|
|
23261
23318
|
risk: ["\u0440\u0438\u0441\u043A \u0432\u044B\u0440\u043E\u0441 \u0434\u043E high", "risk went up to high"],
|
|
23262
|
-
unchecked: ["\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C", "could not check"]
|
|
23319
|
+
unchecked: ["\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C", "could not check"],
|
|
23320
|
+
env: ["\u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u0430", "environment variable is not set"]
|
|
23263
23321
|
};
|
|
23264
23322
|
var runDoctor = async (ctx, _args, flags) => {
|
|
23265
23323
|
const problems = [];
|
|
23324
|
+
const env = [];
|
|
23266
23325
|
let checked = 0;
|
|
23267
23326
|
for (const s of await lockScopes(ctx, flags)) {
|
|
23268
23327
|
for (const [key, entry] of Object.entries(s.lock.entries)) {
|
|
23269
23328
|
checked++;
|
|
23270
23329
|
problems.push(...await localProblems(ctx, key, s.scope, entry, s.root));
|
|
23330
|
+
const rows = envRows(ctx, key, s.scope, entry.vars);
|
|
23331
|
+
env.push(...rows);
|
|
23332
|
+
problems.push(...envProblems(ctx, rows));
|
|
23271
23333
|
try {
|
|
23272
23334
|
const body = await fetchRecipe(ctx, entry.section, entry.slug);
|
|
23273
23335
|
const g = gate(ctx, body);
|
|
@@ -23284,9 +23346,13 @@ var runDoctor = async (ctx, _args, flags) => {
|
|
|
23284
23346
|
}
|
|
23285
23347
|
}
|
|
23286
23348
|
}
|
|
23287
|
-
|
|
23288
|
-
|
|
23289
|
-
else
|
|
23349
|
+
const optionalMissing = env.filter((r) => !r.required && r.status === "missing");
|
|
23350
|
+
if (flags.json) printJson(ctx, { checked, problems, env });
|
|
23351
|
+
else if (!problems.length && !optionalMissing.length) ctx.out(tr(ctx, `\u041F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E \u0437\u0430\u043F\u0438\u0441\u0435\u0439: ${checked}, \u043F\u0440\u043E\u0431\u043B\u0435\u043C \u043D\u0435\u0442.`, `Entries checked: ${checked}, no problems.`));
|
|
23352
|
+
else {
|
|
23353
|
+
for (const p of problems) ctx.out(`${p.key}: ${tr(ctx, ...LABEL[p.kind])}, ${p.detail}`);
|
|
23354
|
+
for (const r of optionalMissing) ctx.out(tr(ctx, `${r.key}: \u043D\u0435\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u0430, ${r.name}`, `${r.key}: optional variable is not set, ${r.name}`));
|
|
23355
|
+
}
|
|
23290
23356
|
return problems.length ? 2 : 0;
|
|
23291
23357
|
};
|
|
23292
23358
|
|
|
@@ -23486,6 +23552,7 @@ var runUpdate = async (ctx, args, flags) => {
|
|
|
23486
23552
|
risk: body.risk.level ?? null,
|
|
23487
23553
|
agents: [...result.touched],
|
|
23488
23554
|
items: result.items,
|
|
23555
|
+
vars: recipeVars(recipe),
|
|
23489
23556
|
updatedAt: ctx.now().toISOString(),
|
|
23490
23557
|
cliVersion: VERSION
|
|
23491
23558
|
};
|
package/package.json
CHANGED