@safeskill/cli 0.3.0 → 0.3.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/CHANGELOG.md +7 -1
- package/bin/cli.mjs +2 -0
- package/dist/cli.mjs +45 -72
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.3.
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
- Fixed the published npm binary entrypoint so `npx @safeskill/cli` executes the bundled CLI instead of exiting with no output.
|
|
6
|
+
|
|
7
|
+
## 0.3.1
|
|
4
8
|
|
|
5
9
|
- Added upstream synchronization infrastructure with `vercel-labs/skills` kept as a subtree reference under `upstream/vercel-labs-skills`.
|
|
6
10
|
- Added metadata sanitization, local frontmatter parsing, GitHub blob tree utilities, bounded nested skill discovery, and improved Git source parsing and clone fallback behavior.
|
|
@@ -9,6 +13,8 @@
|
|
|
9
13
|
- Expanded supported agent targets, including Eve subagents and additional upstream agent definitions, while retaining SafeSkill-specific `flocks` and `kimi-cli` support.
|
|
10
14
|
- Added agent environment detection via `@vercel/detect-agent`, including banner suppression and non-interactive `find` guidance in detected agent environments.
|
|
11
15
|
- Updated the test script to exclude subtree and temporary worktree test suites from normal project verification.
|
|
16
|
+
- Fixed SafeSkill hub installs so region fallback is attempted during resolve instead of being blocked by the status endpoint.
|
|
17
|
+
- Added `hermes` as an alias for the `hermes-agent` install target.
|
|
12
18
|
|
|
13
19
|
## 0.2.2
|
|
14
20
|
|
package/bin/cli.mjs
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -623,10 +623,8 @@ const REGION_DOMAINS = {
|
|
|
623
623
|
global: "safeskill.io"
|
|
624
624
|
};
|
|
625
625
|
const REGION_DETECT_URLS = ["https://ifconfig.co/country-iso", "https://ipapi.co/country/"];
|
|
626
|
-
const STATUS_TIMEOUT_MS = 2500;
|
|
627
626
|
const DETECT_TIMEOUT_MS = 1500;
|
|
628
627
|
let resolvedRegionPromise = null;
|
|
629
|
-
let regionAvailabilityCache = /* @__PURE__ */ new Map();
|
|
630
628
|
let safeSkillRegionOverride = null;
|
|
631
629
|
function normalizeSafeSkillRegion(region) {
|
|
632
630
|
const value = region?.trim().toLowerCase();
|
|
@@ -662,61 +660,11 @@ async function detectRegionByPublicIP() {
|
|
|
662
660
|
}
|
|
663
661
|
return "global";
|
|
664
662
|
}
|
|
665
|
-
function hasCustomEndpoints() {
|
|
666
|
-
return !!(process.env.SAFESKILL_HUB_URL || process.env.SAFESKILL_SEARCH_URL || process.env.SAFESKILL_TELEMETRY_URL || process.env.SKILLS_API_URL);
|
|
667
|
-
}
|
|
668
663
|
function ensureAbsoluteUrl(url, fallbackPath) {
|
|
669
664
|
const parsed = new URL(url);
|
|
670
665
|
if (!parsed.pathname || parsed.pathname === "/") parsed.pathname = fallbackPath;
|
|
671
666
|
return parsed.toString().replace(/\/$/, "");
|
|
672
667
|
}
|
|
673
|
-
function getShellSpecificCommands(variable, value) {
|
|
674
|
-
const shell = process.env.SHELL;
|
|
675
|
-
const platform = process.platform;
|
|
676
|
-
const parentProcess = process.env.PROMPT || process.env.COMSPEC;
|
|
677
|
-
const commands = [];
|
|
678
|
-
if (platform === "win32") {
|
|
679
|
-
process.env.WT_SESSION || process.env.TERM_PROGRAM;
|
|
680
|
-
const isPowerShell = process.env.PSModulePath || parentProcess?.includes("powershell.exe") || parentProcess?.includes("pwsh.exe");
|
|
681
|
-
const isCMD = parentProcess?.includes("cmd.exe");
|
|
682
|
-
if (isCMD && !isPowerShell) commands.push(`CMD: set ${variable}=${value}`);
|
|
683
|
-
else if (isPowerShell && !isCMD) commands.push(`PowerShell: $env:${variable}="${value}"`);
|
|
684
|
-
else if (isCMD && isPowerShell) {
|
|
685
|
-
commands.push(`CMD: set ${variable}=${value}`);
|
|
686
|
-
commands.push(`PowerShell: $env:${variable}="${value}"`);
|
|
687
|
-
} else {
|
|
688
|
-
commands.push(`CMD: set ${variable}=${value}`);
|
|
689
|
-
commands.push(`PowerShell: $env:${variable}="${value}"`);
|
|
690
|
-
}
|
|
691
|
-
} else if (shell?.includes("bash") || shell?.includes("zsh")) commands.push(`bash/zsh: export ${variable}="${value}"`);
|
|
692
|
-
else if (shell?.includes("fish")) commands.push(`fish: set -gx ${variable} "${value}"`);
|
|
693
|
-
else commands.push(`export ${variable}="${value}"`);
|
|
694
|
-
return commands;
|
|
695
|
-
}
|
|
696
|
-
async function ensureRegionAvailable(region, hubBaseUrl) {
|
|
697
|
-
if (hasCustomEndpoints()) return;
|
|
698
|
-
const cacheKey = `${region}`;
|
|
699
|
-
if (regionAvailabilityCache.has(cacheKey)) {
|
|
700
|
-
await regionAvailabilityCache.get(cacheKey);
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
const availabilityPromise = (async () => {
|
|
704
|
-
const statusUrl = new URL("/api/v1/status", hubBaseUrl).toString();
|
|
705
|
-
try {
|
|
706
|
-
if ((await fetch(statusUrl, {
|
|
707
|
-
signal: withTimeout(STATUS_TIMEOUT_MS),
|
|
708
|
-
headers: getHeaders()
|
|
709
|
-
})).ok) return;
|
|
710
|
-
} catch {}
|
|
711
|
-
const alternateRegion = region === "cn" ? "global" : "cn";
|
|
712
|
-
REGION_DOMAINS[alternateRegion];
|
|
713
|
-
const currentDomain = REGION_DOMAINS[region];
|
|
714
|
-
const commandList = getShellSpecificCommands("SAFESKILL_REGION", alternateRegion).map((cmd) => ` ${cmd}`).join("\n");
|
|
715
|
-
throw new Error(`SafeSkill ${region} region (${currentDomain}) is currently unavailable.\nTry these solutions:\n 1. Set environment variable:\n${commandList}\n 2. Or disable telemetry: DISABLE_TELEMETRY=1\n\nFor assistance, contact: dev@safeskill.io`);
|
|
716
|
-
})();
|
|
717
|
-
regionAvailabilityCache.set(cacheKey, availabilityPromise);
|
|
718
|
-
await availabilityPromise;
|
|
719
|
-
}
|
|
720
668
|
async function resolveSafeSkillRegion() {
|
|
721
669
|
if (resolvedRegionPromise) return resolvedRegionPromise;
|
|
722
670
|
resolvedRegionPromise = (async () => {
|
|
@@ -729,10 +677,7 @@ async function resolveSafeSkillRegion() {
|
|
|
729
677
|
}
|
|
730
678
|
async function getSafeSkillHubBaseUrl() {
|
|
731
679
|
if (process.env.SAFESKILL_HUB_URL) return process.env.SAFESKILL_HUB_URL;
|
|
732
|
-
|
|
733
|
-
const baseUrl = `https://hubapi.${REGION_DOMAINS[region]}`;
|
|
734
|
-
await ensureRegionAvailable(region, baseUrl);
|
|
735
|
-
return baseUrl;
|
|
680
|
+
return `https://hubapi.${REGION_DOMAINS[await resolveSafeSkillRegion()]}`;
|
|
736
681
|
}
|
|
737
682
|
async function getSafeSkillSearchUrlWithHub(hubBaseUrl) {
|
|
738
683
|
const configured = process.env.SAFESKILL_SEARCH_URL || process.env.SKILLS_API_URL;
|
|
@@ -972,7 +917,7 @@ async function resolveHubSkill(source, agent) {
|
|
|
972
917
|
source,
|
|
973
918
|
agent
|
|
974
919
|
})
|
|
975
|
-
}) : null;
|
|
920
|
+
}).catch(() => null) : null;
|
|
976
921
|
return resolveWithAgent && resolveWithAgent.ok ? resolveWithAgent : fetch(resolveUrl, {
|
|
977
922
|
method: "POST",
|
|
978
923
|
headers: getHeaders({ "content-type": "application/json" }),
|
|
@@ -980,15 +925,25 @@ async function resolveHubSkill(source, agent) {
|
|
|
980
925
|
});
|
|
981
926
|
};
|
|
982
927
|
let resolvedBaseUrl = baseUrl;
|
|
983
|
-
let response =
|
|
984
|
-
|
|
928
|
+
let response = null;
|
|
929
|
+
let lastError;
|
|
930
|
+
try {
|
|
931
|
+
response = await resolveFromBaseUrl(baseUrl);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
lastError = error;
|
|
934
|
+
}
|
|
935
|
+
const alternateBaseUrl = !response || response.status >= 500 ? getAlternateHubBaseUrl(baseUrl) : null;
|
|
985
936
|
if (alternateBaseUrl) {
|
|
986
|
-
const alternateResponse = await resolveFromBaseUrl(alternateBaseUrl)
|
|
987
|
-
|
|
937
|
+
const alternateResponse = await resolveFromBaseUrl(alternateBaseUrl).catch((error) => {
|
|
938
|
+
lastError = error;
|
|
939
|
+
return null;
|
|
940
|
+
});
|
|
941
|
+
if (alternateResponse && (alternateResponse.ok || !response || !response.ok)) {
|
|
988
942
|
response = alternateResponse;
|
|
989
943
|
resolvedBaseUrl = alternateBaseUrl;
|
|
990
944
|
}
|
|
991
945
|
}
|
|
946
|
+
if (!response) throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error("hub resolve failed");
|
|
992
947
|
if (!response.ok) throw new Error(`hub resolve failed: ${response.status}`);
|
|
993
948
|
const resolved = await readEnvelope(response);
|
|
994
949
|
resolved.hubBaseUrl = resolvedBaseUrl;
|
|
@@ -2080,6 +2035,21 @@ const agents = {
|
|
|
2080
2035
|
detectInstalled: async () => false
|
|
2081
2036
|
}
|
|
2082
2037
|
};
|
|
2038
|
+
const agentAliases = { hermes: "hermes-agent" };
|
|
2039
|
+
function normalizeAgentType(input) {
|
|
2040
|
+
if (input in agents) return input;
|
|
2041
|
+
return agentAliases[input] ?? null;
|
|
2042
|
+
}
|
|
2043
|
+
function normalizeAgentTypes(inputs) {
|
|
2044
|
+
const normalized = inputs.map((agent) => ({
|
|
2045
|
+
input: agent,
|
|
2046
|
+
normalized: normalizeAgentType(agent)
|
|
2047
|
+
}));
|
|
2048
|
+
return {
|
|
2049
|
+
normalizedAgents: normalized.filter((agent) => Boolean(agent.normalized)).map((agent) => agent.normalized),
|
|
2050
|
+
invalidAgents: normalized.filter((agent) => !agent.normalized).map((agent) => agent.input)
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2083
2053
|
async function detectInstalledAgents() {
|
|
2084
2054
|
return (await Promise.all(Object.entries(agents).map(async ([type, config]) => ({
|
|
2085
2055
|
type,
|
|
@@ -2930,7 +2900,7 @@ async function saveSelectedAgents(agents) {
|
|
|
2930
2900
|
lock.lastSelectedAgents = agents;
|
|
2931
2901
|
await writeSkillLock(lock);
|
|
2932
2902
|
}
|
|
2933
|
-
var version$1 = "0.3.
|
|
2903
|
+
var version$1 = "0.3.2";
|
|
2934
2904
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2935
2905
|
async function isSourcePrivate(source) {
|
|
2936
2906
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -3169,13 +3139,13 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
3169
3139
|
targetAgents = validAgents;
|
|
3170
3140
|
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
3171
3141
|
} else if (options.agent && options.agent.length > 0) {
|
|
3172
|
-
const invalidAgents = options.agent
|
|
3142
|
+
const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
|
|
3173
3143
|
if (invalidAgents.length > 0) {
|
|
3174
3144
|
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
3175
3145
|
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
3176
3146
|
process.exit(1);
|
|
3177
3147
|
}
|
|
3178
|
-
targetAgents =
|
|
3148
|
+
targetAgents = normalizedAgents;
|
|
3179
3149
|
} else {
|
|
3180
3150
|
spinner.start("Loading agents...");
|
|
3181
3151
|
const installedAgents = await detectInstalledAgents();
|
|
@@ -3581,14 +3551,14 @@ async function runAdd(args, options = {}) {
|
|
|
3581
3551
|
targetAgents = validAgents;
|
|
3582
3552
|
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
3583
3553
|
} else if (options.agent && options.agent.length > 0) {
|
|
3584
|
-
const invalidAgents = options.agent
|
|
3554
|
+
const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
|
|
3585
3555
|
if (invalidAgents.length > 0) {
|
|
3586
3556
|
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
3587
3557
|
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
3588
3558
|
await cleanup(tempDir);
|
|
3589
3559
|
process.exit(1);
|
|
3590
3560
|
}
|
|
3591
|
-
targetAgents =
|
|
3561
|
+
targetAgents = normalizedAgents;
|
|
3592
3562
|
} else {
|
|
3593
3563
|
spinner.start("Loading agents...");
|
|
3594
3564
|
const installedAgents = await detectInstalledAgents();
|
|
@@ -4364,13 +4334,13 @@ async function runSync(args, options = {}) {
|
|
|
4364
4334
|
targetAgents = validAgents;
|
|
4365
4335
|
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
4366
4336
|
} else if (options.agent && options.agent.length > 0) {
|
|
4367
|
-
const invalidAgents = options.agent
|
|
4337
|
+
const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
|
|
4368
4338
|
if (invalidAgents.length > 0) {
|
|
4369
4339
|
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
4370
4340
|
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
4371
4341
|
process.exit(1);
|
|
4372
4342
|
}
|
|
4373
|
-
targetAgents =
|
|
4343
|
+
targetAgents = normalizedAgents;
|
|
4374
4344
|
} else {
|
|
4375
4345
|
spinner.start("Loading agents...");
|
|
4376
4346
|
const installedAgents = await detectInstalledAgents();
|
|
@@ -4614,13 +4584,13 @@ async function runList(args) {
|
|
|
4614
4584
|
let agentFilter;
|
|
4615
4585
|
if (options.agent && options.agent.length > 0) {
|
|
4616
4586
|
const validAgents = Object.keys(agents);
|
|
4617
|
-
const invalidAgents = options.agent
|
|
4587
|
+
const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
|
|
4618
4588
|
if (invalidAgents.length > 0) {
|
|
4619
4589
|
console.log(`${YELLOW}Invalid agents: ${invalidAgents.join(", ")}${RESET$2}`);
|
|
4620
4590
|
console.log(`${DIM$2}Valid agents: ${validAgents.join(", ")}${RESET$2}`);
|
|
4621
4591
|
process.exit(1);
|
|
4622
4592
|
}
|
|
4623
|
-
agentFilter =
|
|
4593
|
+
agentFilter = normalizedAgents;
|
|
4624
4594
|
}
|
|
4625
4595
|
const installedSkills = await listInstalledSkills({
|
|
4626
4596
|
global: scope,
|
|
@@ -4715,14 +4685,16 @@ async function removeCommand(skillNames, options) {
|
|
|
4715
4685
|
Se(import_picocolors.default.yellow("No skills found to remove."));
|
|
4716
4686
|
return;
|
|
4717
4687
|
}
|
|
4688
|
+
let normalizedAgentFilter;
|
|
4718
4689
|
if (options.agent && options.agent.length > 0) {
|
|
4719
4690
|
const validAgents = Object.keys(agents);
|
|
4720
|
-
const invalidAgents = options.agent
|
|
4691
|
+
const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
|
|
4721
4692
|
if (invalidAgents.length > 0) {
|
|
4722
4693
|
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
4723
4694
|
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
4724
4695
|
process.exit(1);
|
|
4725
4696
|
}
|
|
4697
|
+
normalizedAgentFilter = normalizedAgents;
|
|
4726
4698
|
}
|
|
4727
4699
|
let selectedSkills = [];
|
|
4728
4700
|
if (options.all) selectedSkills = installedSkills;
|
|
@@ -4749,7 +4721,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4749
4721
|
selectedSkills = selected;
|
|
4750
4722
|
}
|
|
4751
4723
|
let targetAgents;
|
|
4752
|
-
if (
|
|
4724
|
+
if (normalizedAgentFilter && normalizedAgentFilter.length > 0) targetAgents = normalizedAgentFilter;
|
|
4753
4725
|
else {
|
|
4754
4726
|
targetAgents = Object.keys(agents);
|
|
4755
4727
|
spinner.stop(`Targeting ${targetAgents.length} potential agent(s)`);
|
|
@@ -6762,6 +6734,7 @@ async function main() {
|
|
|
6762
6734
|
}
|
|
6763
6735
|
}
|
|
6764
6736
|
function isCliEntrypoint() {
|
|
6737
|
+
if (process.env.SAFESKILL_CLI_ENTRYPOINT === "1") return true;
|
|
6765
6738
|
const entrypoint = process.argv[1];
|
|
6766
6739
|
return entrypoint ? import.meta.url === pathToFileURL(entrypoint).href : false;
|
|
6767
6740
|
}
|