@safeskill/cli 0.2.1 → 0.2.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 +4 -0
- package/README.md +10 -0
- package/dist/cli.mjs +114 -24
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -76,9 +76,18 @@ npx @safeskill/cli add safeskill://official/acme/code-review@1.2.0
|
|
|
76
76
|
| `-y, --yes` | Skip all confirmation prompts |
|
|
77
77
|
| `--all` | Install all skills to all agents without prompts |
|
|
78
78
|
|
|
79
|
+
### Global Options
|
|
80
|
+
|
|
81
|
+
| Option | Description |
|
|
82
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
83
|
+
| `--region <cn|global>` | Use a specific SafeSkill service region for this run. Overrides `SAFESKILL_REGION`; otherwise auto-detected. |
|
|
84
|
+
|
|
79
85
|
### Examples
|
|
80
86
|
|
|
81
87
|
```bash
|
|
88
|
+
# Force the China service region for this run
|
|
89
|
+
npx @safeskill/cli --region cn add safeskill://official/acme/code-review
|
|
90
|
+
|
|
82
91
|
# List skills in a repository
|
|
83
92
|
npx @safeskill/cli add example-org/agent-skills --list
|
|
84
93
|
|
|
@@ -441,6 +450,7 @@ Ensure you have write access to the target directory.
|
|
|
441
450
|
| Variable | Description |
|
|
442
451
|
| ------------------------- | -------------------------------------------------------------------------- |
|
|
443
452
|
| `INSTALL_INTERNAL_SKILLS` | Set to `1` or `true` to show and install skills marked as `internal: true` |
|
|
453
|
+
| `SAFESKILL_REGION` | Optional fallback service region (`cn` or `global`) when `--region` is not specified |
|
|
444
454
|
| `DISABLE_TELEMETRY` | Set to disable anonymous usage telemetry |
|
|
445
455
|
| `DO_NOT_TRACK` | Alternative way to disable telemetry |
|
|
446
456
|
|
package/dist/cli.mjs
CHANGED
|
@@ -408,13 +408,18 @@ const STATUS_TIMEOUT_MS = 2500;
|
|
|
408
408
|
const DETECT_TIMEOUT_MS = 1500;
|
|
409
409
|
let resolvedRegionPromise = null;
|
|
410
410
|
let regionAvailabilityCache = /* @__PURE__ */ new Map();
|
|
411
|
-
|
|
411
|
+
let safeSkillRegionOverride = null;
|
|
412
|
+
function normalizeSafeSkillRegion(region) {
|
|
412
413
|
const value = region?.trim().toLowerCase();
|
|
413
414
|
if (!value) return null;
|
|
414
415
|
if (value === "cn" || value === "china") return "cn";
|
|
415
416
|
if (value === "global" || value === "io" || value === "intl" || value === "international") return "global";
|
|
416
417
|
return null;
|
|
417
418
|
}
|
|
419
|
+
function setSafeSkillRegionOverride(region) {
|
|
420
|
+
safeSkillRegionOverride = region;
|
|
421
|
+
resolvedRegionPromise = null;
|
|
422
|
+
}
|
|
418
423
|
function withTimeout(timeoutMs) {
|
|
419
424
|
return AbortSignal.timeout(timeoutMs);
|
|
420
425
|
}
|
|
@@ -496,7 +501,8 @@ async function ensureRegionAvailable(region, hubBaseUrl) {
|
|
|
496
501
|
async function resolveSafeSkillRegion() {
|
|
497
502
|
if (resolvedRegionPromise) return resolvedRegionPromise;
|
|
498
503
|
resolvedRegionPromise = (async () => {
|
|
499
|
-
|
|
504
|
+
if (safeSkillRegionOverride) return safeSkillRegionOverride;
|
|
505
|
+
const configuredRegion = normalizeSafeSkillRegion(process.env.SAFESKILL_REGION);
|
|
500
506
|
if (configuredRegion) return configuredRegion;
|
|
501
507
|
return detectRegionByPublicIP();
|
|
502
508
|
})();
|
|
@@ -530,6 +536,19 @@ function normalizeApiBase(baseUrl) {
|
|
|
530
536
|
function buildApiUrl(baseUrl, path) {
|
|
531
537
|
return `${normalizeApiBase(baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
|
|
532
538
|
}
|
|
539
|
+
function getAlternateHubBaseUrl(baseUrl) {
|
|
540
|
+
if (process.env.SAFESKILL_HUB_URL) return null;
|
|
541
|
+
const parsed = new URL(baseUrl);
|
|
542
|
+
if (parsed.hostname === "hubapi.safeskill.io") {
|
|
543
|
+
parsed.hostname = "hubapi.safeskill.cn";
|
|
544
|
+
return parsed.toString().replace(/\/$/, "");
|
|
545
|
+
}
|
|
546
|
+
if (parsed.hostname === "hubapi.safeskill.cn") {
|
|
547
|
+
parsed.hostname = "hubapi.safeskill.io";
|
|
548
|
+
return parsed.toString().replace(/\/$/, "");
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
533
552
|
async function readEnvelope(response) {
|
|
534
553
|
const payload = await response.json();
|
|
535
554
|
if (payload && typeof payload === "object" && "data" in payload) return payload.data;
|
|
@@ -724,26 +743,41 @@ async function searchSkillsCatalog(query, limit = 10) {
|
|
|
724
743
|
}
|
|
725
744
|
}
|
|
726
745
|
async function resolveHubSkill(source, agent) {
|
|
727
|
-
const
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
746
|
+
const baseUrl = await getSafeSkillHubBaseUrl();
|
|
747
|
+
const resolveFromBaseUrl = async (hubBaseUrl) => {
|
|
748
|
+
const resolveUrl = buildApiUrl(hubBaseUrl, "/resolve");
|
|
749
|
+
const resolveWithAgent = agent ? await fetch(resolveUrl, {
|
|
750
|
+
method: "POST",
|
|
751
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
752
|
+
body: JSON.stringify({
|
|
753
|
+
source,
|
|
754
|
+
agent
|
|
755
|
+
})
|
|
756
|
+
}) : null;
|
|
757
|
+
return resolveWithAgent && resolveWithAgent.ok ? resolveWithAgent : fetch(resolveUrl, {
|
|
758
|
+
method: "POST",
|
|
759
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
760
|
+
body: JSON.stringify({ source })
|
|
761
|
+
});
|
|
762
|
+
};
|
|
763
|
+
let resolvedBaseUrl = baseUrl;
|
|
764
|
+
let response = await resolveFromBaseUrl(baseUrl);
|
|
765
|
+
const alternateBaseUrl = response.status >= 500 ? getAlternateHubBaseUrl(baseUrl) : null;
|
|
766
|
+
if (alternateBaseUrl) {
|
|
767
|
+
const alternateResponse = await resolveFromBaseUrl(alternateBaseUrl);
|
|
768
|
+
if (alternateResponse.ok || !response.ok) {
|
|
769
|
+
response = alternateResponse;
|
|
770
|
+
resolvedBaseUrl = alternateBaseUrl;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
741
773
|
if (!response.ok) throw new Error(`hub resolve failed: ${response.status}`);
|
|
742
|
-
|
|
774
|
+
const resolved = await readEnvelope(response);
|
|
775
|
+
resolved.hubBaseUrl = resolvedBaseUrl;
|
|
776
|
+
return resolved;
|
|
743
777
|
}
|
|
744
778
|
async function downloadHubSource(source, agent) {
|
|
745
|
-
const baseUrl = await getSafeSkillHubBaseUrl();
|
|
746
779
|
const resolved = await resolveHubSkill(source, agent);
|
|
780
|
+
const baseUrl = resolved.hubBaseUrl || await getSafeSkillHubBaseUrl();
|
|
747
781
|
const downloadUrl = resolved.download_uri || (resolved.artifact_digest ? buildApiUrl(baseUrl, `/artifacts/${encodeURIComponent(resolved.artifact_digest)}/download`) : "");
|
|
748
782
|
if (!downloadUrl) throw new Error("hub resolve did not return a downloadable artifact");
|
|
749
783
|
let response;
|
|
@@ -2278,7 +2312,7 @@ function createEmptyLocalLock() {
|
|
|
2278
2312
|
skills: {}
|
|
2279
2313
|
};
|
|
2280
2314
|
}
|
|
2281
|
-
var version$1 = "0.2.
|
|
2315
|
+
var version$1 = "0.2.2";
|
|
2282
2316
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2283
2317
|
async function isSourcePrivate(source) {
|
|
2284
2318
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -4369,6 +4403,8 @@ ${BOLD}List Options:${RESET}
|
|
|
4369
4403
|
${BOLD}Options:${RESET}
|
|
4370
4404
|
--help, -h Show this help message
|
|
4371
4405
|
--version, -v Show version number
|
|
4406
|
+
--region <cn|global>
|
|
4407
|
+
Use a specific SafeSkill service region for this run
|
|
4372
4408
|
|
|
4373
4409
|
${BOLD}Examples:${RESET}
|
|
4374
4410
|
${DIM}$${RESET} safeskill add example-org/agent-skills
|
|
@@ -4655,7 +4691,7 @@ async function runCheck(args = []) {
|
|
|
4655
4691
|
});
|
|
4656
4692
|
console.log();
|
|
4657
4693
|
}
|
|
4658
|
-
async function runUpdate() {
|
|
4694
|
+
async function runUpdate(region) {
|
|
4659
4695
|
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
4660
4696
|
console.log();
|
|
4661
4697
|
const lock = readSkillLock();
|
|
@@ -4751,13 +4787,22 @@ async function runUpdate() {
|
|
|
4751
4787
|
console.log(` ${DIM}✗ Failed to update ${update.name}: CLI entrypoint not found at ${cliEntry}${RESET}`);
|
|
4752
4788
|
continue;
|
|
4753
4789
|
}
|
|
4754
|
-
|
|
4790
|
+
const addArgs = region ? [
|
|
4791
|
+
cliEntry,
|
|
4792
|
+
"--region",
|
|
4793
|
+
region,
|
|
4794
|
+
"add",
|
|
4795
|
+
installUrl,
|
|
4796
|
+
"-g",
|
|
4797
|
+
"-y"
|
|
4798
|
+
] : [
|
|
4755
4799
|
cliEntry,
|
|
4756
4800
|
"add",
|
|
4757
4801
|
installUrl,
|
|
4758
4802
|
"-g",
|
|
4759
4803
|
"-y"
|
|
4760
|
-
]
|
|
4804
|
+
];
|
|
4805
|
+
if (spawnSync(process.execPath, addArgs, {
|
|
4761
4806
|
stdio: [
|
|
4762
4807
|
"inherit",
|
|
4763
4808
|
"pipe",
|
|
@@ -4784,8 +4829,53 @@ async function runUpdate() {
|
|
|
4784
4829
|
});
|
|
4785
4830
|
console.log();
|
|
4786
4831
|
}
|
|
4832
|
+
function parseGlobalOptions(args) {
|
|
4833
|
+
const parsedArgs = [];
|
|
4834
|
+
let region;
|
|
4835
|
+
for (let i = 0; i < args.length; i++) {
|
|
4836
|
+
const arg = args[i];
|
|
4837
|
+
if (arg === void 0) continue;
|
|
4838
|
+
if (arg === "--region") {
|
|
4839
|
+
const value = args[i + 1];
|
|
4840
|
+
if (typeof value !== "string" || value.startsWith("-")) return {
|
|
4841
|
+
args: parsedArgs,
|
|
4842
|
+
error: "Missing value for --region. Expected one of: cn, global"
|
|
4843
|
+
};
|
|
4844
|
+
const normalized = normalizeSafeSkillRegion(value);
|
|
4845
|
+
if (!normalized) return {
|
|
4846
|
+
args: parsedArgs,
|
|
4847
|
+
error: `Invalid region: ${value}\nExpected one of: cn, global`
|
|
4848
|
+
};
|
|
4849
|
+
region = normalized;
|
|
4850
|
+
i++;
|
|
4851
|
+
continue;
|
|
4852
|
+
}
|
|
4853
|
+
if (arg.startsWith("--region=")) {
|
|
4854
|
+
const value = arg.slice(9);
|
|
4855
|
+
const normalized = normalizeSafeSkillRegion(value);
|
|
4856
|
+
if (!normalized) return {
|
|
4857
|
+
args: parsedArgs,
|
|
4858
|
+
error: `Invalid region: ${value}\nExpected one of: cn, global`
|
|
4859
|
+
};
|
|
4860
|
+
region = normalized;
|
|
4861
|
+
continue;
|
|
4862
|
+
}
|
|
4863
|
+
parsedArgs.push(arg);
|
|
4864
|
+
}
|
|
4865
|
+
return {
|
|
4866
|
+
args: parsedArgs,
|
|
4867
|
+
region
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4787
4870
|
async function main() {
|
|
4788
|
-
const
|
|
4871
|
+
const parsedGlobalOptions = parseGlobalOptions(process.argv.slice(2));
|
|
4872
|
+
if (parsedGlobalOptions.error) {
|
|
4873
|
+
console.log(parsedGlobalOptions.error);
|
|
4874
|
+
process.exitCode = 1;
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
if (parsedGlobalOptions.region) setSafeSkillRegionOverride(parsedGlobalOptions.region);
|
|
4878
|
+
const args = parsedGlobalOptions.args;
|
|
4789
4879
|
if (args.length === 0) {
|
|
4790
4880
|
showBanner();
|
|
4791
4881
|
return;
|
|
@@ -4844,7 +4934,7 @@ async function main() {
|
|
|
4844
4934
|
break;
|
|
4845
4935
|
case "update":
|
|
4846
4936
|
case "upgrade":
|
|
4847
|
-
runUpdate();
|
|
4937
|
+
runUpdate(parsedGlobalOptions.region);
|
|
4848
4938
|
break;
|
|
4849
4939
|
case "--help":
|
|
4850
4940
|
case "-h":
|