itismyskillmarket 1.3.40 → 1.3.42
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/dist/{chunk-VRXNOGLL.js → chunk-76RGF4GX.js} +349 -42
- package/dist/electron-entry.js +1 -1
- package/dist/index.js +7 -219
- package/gui/app.js +157 -2
- package/gui/index.html +15 -0
- package/gui/style.css +85 -0
- package/package.json +1 -1
|
@@ -581,6 +581,56 @@ var SaitecAdapter = class extends BaseAdapter {
|
|
|
581
581
|
}
|
|
582
582
|
};
|
|
583
583
|
|
|
584
|
+
// src/adapters/codex.ts
|
|
585
|
+
import path9 from "path";
|
|
586
|
+
import os9 from "os";
|
|
587
|
+
import fs10 from "fs-extra";
|
|
588
|
+
var CodexAdapter = class extends BaseAdapter {
|
|
589
|
+
id = "codex";
|
|
590
|
+
name = "Codex CLI";
|
|
591
|
+
skillDir = path9.join(os9.homedir(), ".codex", "skills");
|
|
592
|
+
async isAvailable() {
|
|
593
|
+
if (process.env.CODEX_CLI) return true;
|
|
594
|
+
try {
|
|
595
|
+
return await fs10.pathExists(path9.join(os9.homedir(), ".codex"));
|
|
596
|
+
} catch {
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async isInstalled(skillId) {
|
|
601
|
+
try {
|
|
602
|
+
return await fs10.pathExists(path9.join(this.skillDir, skillId));
|
|
603
|
+
} catch {
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async install(skillId, sourceDir) {
|
|
608
|
+
await fs10.ensureDir(this.skillDir);
|
|
609
|
+
const targetDir = path9.join(this.skillDir, skillId);
|
|
610
|
+
if (await fs10.pathExists(targetDir)) {
|
|
611
|
+
await fs10.remove(targetDir);
|
|
612
|
+
}
|
|
613
|
+
await fs10.copy(sourceDir, targetDir, { recursive: true });
|
|
614
|
+
}
|
|
615
|
+
async uninstall(skillId) {
|
|
616
|
+
const targetDir = path9.join(this.skillDir, skillId);
|
|
617
|
+
if (await fs10.pathExists(targetDir)) {
|
|
618
|
+
await fs10.remove(targetDir);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
async listInstalled() {
|
|
622
|
+
try {
|
|
623
|
+
if (!await fs10.pathExists(this.skillDir)) {
|
|
624
|
+
return [];
|
|
625
|
+
}
|
|
626
|
+
const entries = await fs10.readdir(this.skillDir, { withFileTypes: true });
|
|
627
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
628
|
+
} catch {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
584
634
|
// src/adapters/registry.ts
|
|
585
635
|
var adapters = /* @__PURE__ */ new Map();
|
|
586
636
|
function registerAdapters() {
|
|
@@ -590,12 +640,14 @@ function registerAdapters() {
|
|
|
590
640
|
const openclaw = new OpenClawAdapter();
|
|
591
641
|
const hermes = new HermesAdapter();
|
|
592
642
|
const saitec = new SaitecAdapter();
|
|
643
|
+
const codex = new CodexAdapter();
|
|
593
644
|
adapters.set(opencode.id, opencode);
|
|
594
645
|
adapters.set(claude.id, claude);
|
|
595
646
|
adapters.set(vscode.id, vscode);
|
|
596
647
|
adapters.set(openclaw.id, openclaw);
|
|
597
648
|
adapters.set(hermes.id, hermes);
|
|
598
649
|
adapters.set(saitec.id, saitec);
|
|
650
|
+
adapters.set(codex.id, codex);
|
|
599
651
|
}
|
|
600
652
|
registerAdapters();
|
|
601
653
|
async function detectPlatforms() {
|
|
@@ -617,8 +669,8 @@ function getAdapterByPlatform(platform) {
|
|
|
617
669
|
vscode: "vscode",
|
|
618
670
|
cursor: "opencode",
|
|
619
671
|
// Cursor uses OpenCode-compatible structure
|
|
620
|
-
codex: "
|
|
621
|
-
// Codex
|
|
672
|
+
codex: "codex",
|
|
673
|
+
// Codex has its own adapter
|
|
622
674
|
antigravity: "opencode",
|
|
623
675
|
// Antigravity uses OpenCode-compatible structure
|
|
624
676
|
openclaw: "openclaw",
|
|
@@ -629,8 +681,8 @@ function getAdapterByPlatform(platform) {
|
|
|
629
681
|
}
|
|
630
682
|
|
|
631
683
|
// src/commands/install.ts
|
|
632
|
-
import
|
|
633
|
-
import
|
|
684
|
+
import fs11 from "fs-extra";
|
|
685
|
+
import path10 from "path";
|
|
634
686
|
import { exec } from "child_process";
|
|
635
687
|
import { promisify } from "util";
|
|
636
688
|
import * as tar from "tar";
|
|
@@ -642,11 +694,11 @@ async function installSkill(skillId, version, options) {
|
|
|
642
694
|
if (options?.sourceDir) {
|
|
643
695
|
console.log(`Installing ${skillId} from local source...`);
|
|
644
696
|
pkgRoot = options.sourceDir;
|
|
645
|
-
const pkgJsonPath =
|
|
697
|
+
const pkgJsonPath = path10.join(pkgRoot, "package.json");
|
|
646
698
|
targetVersion = version || "0.0.0";
|
|
647
|
-
if (await
|
|
699
|
+
if (await fs11.pathExists(pkgJsonPath)) {
|
|
648
700
|
try {
|
|
649
|
-
const pkg = JSON.parse(await
|
|
701
|
+
const pkg = JSON.parse(await fs11.readFile(pkgJsonPath, "utf-8"));
|
|
650
702
|
if (pkg.version) targetVersion = pkg.version;
|
|
651
703
|
} catch {
|
|
652
704
|
}
|
|
@@ -663,20 +715,20 @@ async function installSkill(skillId, version, options) {
|
|
|
663
715
|
throw new Error(`No version found for ${packageName}`);
|
|
664
716
|
}
|
|
665
717
|
const cacheDir = getCacheDir();
|
|
666
|
-
const targetDir =
|
|
667
|
-
if (!await
|
|
718
|
+
const targetDir = path10.join(cacheDir, `${packageName}@${targetVersion}`);
|
|
719
|
+
if (!await fs11.pathExists(targetDir)) {
|
|
668
720
|
console.log("Downloading package...");
|
|
669
|
-
await
|
|
721
|
+
await fs11.ensureDir(cacheDir);
|
|
670
722
|
try {
|
|
671
723
|
const { stdout } = await execAsync(
|
|
672
724
|
`npm pack ${packageName}@${targetVersion} --pack-destination "${cacheDir}"`
|
|
673
725
|
);
|
|
674
726
|
const tarballName = stdout.trim();
|
|
675
|
-
const tarballPath =
|
|
676
|
-
if (await
|
|
727
|
+
const tarballPath = path10.join(cacheDir, tarballName);
|
|
728
|
+
if (await fs11.pathExists(tarballPath)) {
|
|
677
729
|
await tar.extract({ file: tarballPath, cwd: cacheDir });
|
|
678
|
-
await
|
|
679
|
-
await
|
|
730
|
+
await fs11.remove(tarballPath);
|
|
731
|
+
await fs11.move(path10.join(cacheDir, "package"), targetDir, { overwrite: true });
|
|
680
732
|
}
|
|
681
733
|
} catch (err) {
|
|
682
734
|
throw new Error(`Failed to download package: ${err}`);
|
|
@@ -685,29 +737,29 @@ async function installSkill(skillId, version, options) {
|
|
|
685
737
|
pkgRoot = targetDir;
|
|
686
738
|
}
|
|
687
739
|
const skillsDir = getSkillsDir();
|
|
688
|
-
const skillVersionDir =
|
|
740
|
+
const skillVersionDir = path10.join(skillsDir, `${skillId}@${targetVersion}`);
|
|
689
741
|
console.log("Setting up skill...");
|
|
690
|
-
await
|
|
691
|
-
if (await
|
|
692
|
-
await
|
|
693
|
-
|
|
694
|
-
|
|
742
|
+
await fs11.ensureDir(skillVersionDir);
|
|
743
|
+
if (await fs11.pathExists(path10.join(pkgRoot, "SKILL.md"))) {
|
|
744
|
+
await fs11.copy(
|
|
745
|
+
path10.join(pkgRoot, "SKILL.md"),
|
|
746
|
+
path10.join(skillVersionDir, "SKILL.md")
|
|
695
747
|
);
|
|
696
748
|
}
|
|
697
|
-
if (await
|
|
698
|
-
await
|
|
699
|
-
|
|
700
|
-
|
|
749
|
+
if (await fs11.pathExists(path10.join(pkgRoot, "metadata.json"))) {
|
|
750
|
+
await fs11.copy(
|
|
751
|
+
path10.join(pkgRoot, "metadata.json"),
|
|
752
|
+
path10.join(skillVersionDir, "metadata.json")
|
|
701
753
|
);
|
|
702
754
|
}
|
|
703
|
-
const skillDir =
|
|
704
|
-
await
|
|
705
|
-
const latestLink =
|
|
755
|
+
const skillDir = path10.join(skillsDir, skillId);
|
|
756
|
+
await fs11.ensureDir(skillDir);
|
|
757
|
+
const latestLink = path10.join(skillDir, LATEST_LINK);
|
|
706
758
|
try {
|
|
707
|
-
await
|
|
708
|
-
await
|
|
759
|
+
await fs11.remove(latestLink);
|
|
760
|
+
await fs11.symlink(skillVersionDir, latestLink, "junction");
|
|
709
761
|
} catch {
|
|
710
|
-
await
|
|
762
|
+
await fs11.copy(skillVersionDir, path10.join(skillDir, LATEST_LINK), { overwrite: true });
|
|
711
763
|
}
|
|
712
764
|
let targetAdapters = [];
|
|
713
765
|
if (options?.platforms && options.platforms.length > 0) {
|
|
@@ -768,8 +820,8 @@ Installing to ${targetAdapters.length} platform(s)...
|
|
|
768
820
|
}
|
|
769
821
|
|
|
770
822
|
// src/commands/uninstall.ts
|
|
771
|
-
import
|
|
772
|
-
import
|
|
823
|
+
import fs12 from "fs-extra";
|
|
824
|
+
import path11 from "path";
|
|
773
825
|
import readline from "readline";
|
|
774
826
|
async function askConfirmation(message) {
|
|
775
827
|
const rl = readline.createInterface({
|
|
@@ -794,12 +846,12 @@ async function getUninstallPreview(skillId, options) {
|
|
|
794
846
|
platformNames = adapters2.map((a) => a.name);
|
|
795
847
|
}
|
|
796
848
|
const skillsDir = getSkillsDir();
|
|
797
|
-
const localPath =
|
|
849
|
+
const localPath = path11.join(skillsDir, skillId);
|
|
798
850
|
const platformLinksDir = getPlatformLinksDir();
|
|
799
851
|
const platformLinks = [];
|
|
800
852
|
for (const platform of PLATFORMS) {
|
|
801
|
-
const linkPath =
|
|
802
|
-
if (await
|
|
853
|
+
const linkPath = path11.join(platformLinksDir, platform, "skills", skillId);
|
|
854
|
+
if (await fs12.pathExists(linkPath)) {
|
|
803
855
|
platformLinks.push(linkPath);
|
|
804
856
|
}
|
|
805
857
|
}
|
|
@@ -878,17 +930,17 @@ Uninstalling from ${validAdapters.length} platform(s)...
|
|
|
878
930
|
}
|
|
879
931
|
}
|
|
880
932
|
const skillsDir = getSkillsDir();
|
|
881
|
-
const skillDir =
|
|
882
|
-
if (await
|
|
883
|
-
await
|
|
933
|
+
const skillDir = path11.join(skillsDir, skillId);
|
|
934
|
+
if (await fs12.pathExists(skillDir)) {
|
|
935
|
+
await fs12.remove(skillDir);
|
|
884
936
|
console.log(`\u2705 Removed local files: ${skillDir}`);
|
|
885
937
|
}
|
|
886
938
|
const platformLinksDir = getPlatformLinksDir();
|
|
887
939
|
let removedLinks = 0;
|
|
888
940
|
for (const platform of PLATFORMS) {
|
|
889
|
-
const linkPath =
|
|
890
|
-
if (await
|
|
891
|
-
await
|
|
941
|
+
const linkPath = path11.join(platformLinksDir, platform, "skills", skillId);
|
|
942
|
+
if (await fs12.pathExists(linkPath)) {
|
|
943
|
+
await fs12.remove(linkPath);
|
|
892
944
|
removedLinks++;
|
|
893
945
|
}
|
|
894
946
|
}
|
|
@@ -1596,6 +1648,222 @@ async function adminAccess(skillId, level) {
|
|
|
1596
1648
|
`);
|
|
1597
1649
|
}
|
|
1598
1650
|
|
|
1651
|
+
// src/commands/config.ts
|
|
1652
|
+
import path12 from "path";
|
|
1653
|
+
import fs13 from "fs-extra";
|
|
1654
|
+
import os10 from "os";
|
|
1655
|
+
var CONFIG_DEFINITIONS = [
|
|
1656
|
+
{
|
|
1657
|
+
key: "npmScope",
|
|
1658
|
+
envVar: "SKM_NPM_SCOPE",
|
|
1659
|
+
defaultValue: "@itismyskillmarket",
|
|
1660
|
+
description: "Primary npm scope for publishing and lookup"
|
|
1661
|
+
},
|
|
1662
|
+
{
|
|
1663
|
+
key: "npmScopeFallback",
|
|
1664
|
+
envVar: "SKM_NPM_SCOPE_FALLBACK",
|
|
1665
|
+
defaultValue: "@wanxuchen",
|
|
1666
|
+
description: "Fallback npm scope (backward compatibility)"
|
|
1667
|
+
},
|
|
1668
|
+
{
|
|
1669
|
+
key: "npmRegistry",
|
|
1670
|
+
envVar: "SKM_NPM_REGISTRY",
|
|
1671
|
+
defaultValue: "https://registry.npmjs.org",
|
|
1672
|
+
description: "npm registry URL (mirror/proxy support)"
|
|
1673
|
+
},
|
|
1674
|
+
{
|
|
1675
|
+
key: "npmScopes",
|
|
1676
|
+
envVar: "SKM_NPM_SCOPES",
|
|
1677
|
+
defaultValue: "@itismyskillmarket,@wanxuchen,@thisisskillmarket,@this-is-skillmarket,@skillmarket",
|
|
1678
|
+
description: "Comma-separated list of npm scopes to search"
|
|
1679
|
+
},
|
|
1680
|
+
{
|
|
1681
|
+
key: "skmUrl",
|
|
1682
|
+
envVar: "SKM_URL",
|
|
1683
|
+
defaultValue: "https://www.npmjs.com/package/@itismyskillmarket",
|
|
1684
|
+
description: "Base URL for skill links (publish output)"
|
|
1685
|
+
}
|
|
1686
|
+
];
|
|
1687
|
+
function getConfigPath() {
|
|
1688
|
+
return path12.join(os10.homedir(), ".skillmarket", "config.json");
|
|
1689
|
+
}
|
|
1690
|
+
async function readConfigFile() {
|
|
1691
|
+
try {
|
|
1692
|
+
const configPath = getConfigPath();
|
|
1693
|
+
if (await fs13.pathExists(configPath)) {
|
|
1694
|
+
const data = await fs13.readJson(configPath);
|
|
1695
|
+
const valid = {};
|
|
1696
|
+
for (const def of CONFIG_DEFINITIONS) {
|
|
1697
|
+
if (data[def.key] !== void 0) {
|
|
1698
|
+
valid[def.key] = String(data[def.key]);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return valid;
|
|
1702
|
+
}
|
|
1703
|
+
} catch {
|
|
1704
|
+
}
|
|
1705
|
+
return {};
|
|
1706
|
+
}
|
|
1707
|
+
async function writeConfigFile(updates) {
|
|
1708
|
+
const configPath = getConfigPath();
|
|
1709
|
+
await fs13.ensureDir(path12.dirname(configPath));
|
|
1710
|
+
let existing = {};
|
|
1711
|
+
try {
|
|
1712
|
+
if (await fs13.pathExists(configPath)) {
|
|
1713
|
+
existing = await fs13.readJson(configPath);
|
|
1714
|
+
}
|
|
1715
|
+
} catch {
|
|
1716
|
+
}
|
|
1717
|
+
const merged = { ...existing, ...updates };
|
|
1718
|
+
for (const key of Object.keys(merged)) {
|
|
1719
|
+
if (merged[key] === void 0) {
|
|
1720
|
+
delete merged[key];
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
await fs13.writeJson(configPath, merged, { spaces: 2 });
|
|
1724
|
+
return merged;
|
|
1725
|
+
}
|
|
1726
|
+
async function removeConfigKeys(keys) {
|
|
1727
|
+
const configPath = getConfigPath();
|
|
1728
|
+
if (!await fs13.pathExists(configPath)) return;
|
|
1729
|
+
try {
|
|
1730
|
+
const existing = await fs13.readJson(configPath);
|
|
1731
|
+
for (const key of keys) {
|
|
1732
|
+
delete existing[key];
|
|
1733
|
+
}
|
|
1734
|
+
await fs13.writeJson(configPath, existing, { spaces: 2 });
|
|
1735
|
+
} catch {
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
async function removeConfigFile() {
|
|
1739
|
+
const configPath = getConfigPath();
|
|
1740
|
+
if (await fs13.pathExists(configPath)) {
|
|
1741
|
+
await fs13.remove(configPath);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
async function getAllConfig() {
|
|
1745
|
+
const fileConfig2 = await readConfigFile();
|
|
1746
|
+
return CONFIG_DEFINITIONS.map((def) => {
|
|
1747
|
+
const envValue = process.env[def.envVar];
|
|
1748
|
+
if (envValue !== void 0) {
|
|
1749
|
+
return {
|
|
1750
|
+
...def,
|
|
1751
|
+
value: envValue,
|
|
1752
|
+
source: "env"
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
const fileValue = fileConfig2[def.key];
|
|
1756
|
+
if (fileValue !== void 0) {
|
|
1757
|
+
return {
|
|
1758
|
+
...def,
|
|
1759
|
+
value: fileValue,
|
|
1760
|
+
source: "file"
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
return {
|
|
1764
|
+
...def,
|
|
1765
|
+
value: def.defaultValue,
|
|
1766
|
+
source: "default"
|
|
1767
|
+
};
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
async function getConfig(key) {
|
|
1771
|
+
const all = await getAllConfig();
|
|
1772
|
+
return all.find((c) => c.key === key) || null;
|
|
1773
|
+
}
|
|
1774
|
+
async function listConfig() {
|
|
1775
|
+
const entries = await getAllConfig();
|
|
1776
|
+
console.log("\n\u{1F527} SkillMarket Configuration\n");
|
|
1777
|
+
const maxKeyLen = Math.max(...entries.map((e) => e.key.length));
|
|
1778
|
+
for (const entry of entries) {
|
|
1779
|
+
const key = entry.key.padEnd(maxKeyLen + 2);
|
|
1780
|
+
const sourceBadge = getSourceBadge(entry.source);
|
|
1781
|
+
console.log(` ${sourceBadge} ${key}${entry.value}`);
|
|
1782
|
+
}
|
|
1783
|
+
console.log("");
|
|
1784
|
+
console.log(" \u6765\u6E90: \u{1F535} \u73AF\u5883\u53D8\u91CF \u{1F7E2} \u914D\u7F6E\u6587\u4EF6 \u26AA \u9ED8\u8BA4\u503C");
|
|
1785
|
+
console.log(" \u914D\u7F6E\u6587\u4EF6: " + getConfigPath());
|
|
1786
|
+
console.log("");
|
|
1787
|
+
console.log(" \u7528\u6CD5:");
|
|
1788
|
+
console.log(" skm config get <key> \u67E5\u770B\u914D\u7F6E\u503C");
|
|
1789
|
+
console.log(" skm config set <key> <v> \u8BBE\u7F6E\u914D\u7F6E\u503C");
|
|
1790
|
+
console.log(" skm config reset <key> \u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C");
|
|
1791
|
+
console.log(" skm config reset --all \u5168\u90E8\u91CD\u7F6E");
|
|
1792
|
+
console.log("");
|
|
1793
|
+
}
|
|
1794
|
+
function getSourceBadge(source) {
|
|
1795
|
+
switch (source) {
|
|
1796
|
+
case "env":
|
|
1797
|
+
return "\u{1F535}";
|
|
1798
|
+
case "file":
|
|
1799
|
+
return "\u{1F7E2}";
|
|
1800
|
+
case "default":
|
|
1801
|
+
return "\u26AA";
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
async function getConfigValue(key) {
|
|
1805
|
+
const entry = await getConfig(key);
|
|
1806
|
+
if (!entry) {
|
|
1807
|
+
console.error(`\u274C Unknown config key: "${key}"`);
|
|
1808
|
+
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
1809
|
+
process.exit(1);
|
|
1810
|
+
}
|
|
1811
|
+
console.log(`
|
|
1812
|
+
\u{1F527} ${entry.key}`);
|
|
1813
|
+
console.log(` Value: ${entry.value}`);
|
|
1814
|
+
console.log(` Source: ${entry.source}`);
|
|
1815
|
+
console.log(` Env var: ${entry.envVar}`);
|
|
1816
|
+
console.log(` Default: ${entry.defaultValue}`);
|
|
1817
|
+
console.log(` Description: ${entry.description}`);
|
|
1818
|
+
console.log("");
|
|
1819
|
+
}
|
|
1820
|
+
async function setConfigValue(key, value) {
|
|
1821
|
+
const def = CONFIG_DEFINITIONS.find((d) => d.key === key);
|
|
1822
|
+
if (!def) {
|
|
1823
|
+
console.error(`\u274C Unknown config key: "${key}"`);
|
|
1824
|
+
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
1825
|
+
process.exit(1);
|
|
1826
|
+
}
|
|
1827
|
+
await writeConfigFile({ [key]: value });
|
|
1828
|
+
console.log(`
|
|
1829
|
+
\u2705 ${key} set to "${value}"`);
|
|
1830
|
+
console.log(` Stored in: ${getConfigPath()}`);
|
|
1831
|
+
console.log("");
|
|
1832
|
+
if (process.env[def.envVar] !== void 0) {
|
|
1833
|
+
console.log(` \u26A0\uFE0F Currently overridden by environment variable ${def.envVar}=${process.env[def.envVar]}`);
|
|
1834
|
+
console.log(` To use the config file value, unset the environment variable.`);
|
|
1835
|
+
console.log("");
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
async function resetConfig(key, all = false) {
|
|
1839
|
+
if (all) {
|
|
1840
|
+
await removeConfigFile();
|
|
1841
|
+
console.log("\n\u2705 All configuration reset to defaults.");
|
|
1842
|
+
console.log(` Removed: ${getConfigPath()}`);
|
|
1843
|
+
console.log("");
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
if (key) {
|
|
1847
|
+
const def = CONFIG_DEFINITIONS.find((d) => d.key === key);
|
|
1848
|
+
if (!def) {
|
|
1849
|
+
console.error(`\u274C Unknown config key: "${key}"`);
|
|
1850
|
+
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
1851
|
+
process.exit(1);
|
|
1852
|
+
}
|
|
1853
|
+
await removeConfigKeys([key]);
|
|
1854
|
+
const sourceNow = process.env[def.envVar] ? "env" : "default";
|
|
1855
|
+
console.log(`
|
|
1856
|
+
\u2705 ${key} reset to ${sourceNow === "env" ? "environment variable" : "default"} value.`);
|
|
1857
|
+
console.log(` Effective value: "${sourceNow === "env" ? process.env[def.envVar] : def.defaultValue}"`);
|
|
1858
|
+
console.log("");
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
console.log("\n\u{1F527} Usage: skm config reset <key>");
|
|
1862
|
+
console.log(" skm config reset --all");
|
|
1863
|
+
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
1864
|
+
console.log("");
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1599
1867
|
// src/commands/ui.ts
|
|
1600
1868
|
var MAX_UPLOAD_SIZE = 50 * 1024 * 1024;
|
|
1601
1869
|
var __filename = fileURLToPath2(import.meta.url);
|
|
@@ -1661,7 +1929,8 @@ function parseBody(req) {
|
|
|
1661
1929
|
}
|
|
1662
1930
|
var API_ROUTES = {
|
|
1663
1931
|
GET: {},
|
|
1664
|
-
POST: {}
|
|
1932
|
+
POST: {},
|
|
1933
|
+
DELETE: {}
|
|
1665
1934
|
};
|
|
1666
1935
|
API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
1667
1936
|
try {
|
|
@@ -1873,6 +2142,40 @@ API_ROUTES.GET["/api/config"] = async (_req, res, _url) => {
|
|
|
1873
2142
|
skillScopes: SKILL_SCOPES
|
|
1874
2143
|
});
|
|
1875
2144
|
};
|
|
2145
|
+
API_ROUTES.GET["/api/github-token"] = async (_req, res, _url) => {
|
|
2146
|
+
try {
|
|
2147
|
+
const config = await readConfigFile();
|
|
2148
|
+
const token = config.githubToken || "";
|
|
2149
|
+
jsonResponse(res, 200, {
|
|
2150
|
+
hasToken: !!token,
|
|
2151
|
+
tokenPrefix: token ? token.substring(0, 6) + "\u2026" : ""
|
|
2152
|
+
});
|
|
2153
|
+
} catch (err) {
|
|
2154
|
+
jsonResponse(res, 500, { error: String(err) });
|
|
2155
|
+
}
|
|
2156
|
+
};
|
|
2157
|
+
API_ROUTES.POST["/api/github-token"] = async (req, res, _url) => {
|
|
2158
|
+
try {
|
|
2159
|
+
const body = await parseBody(req);
|
|
2160
|
+
const token = String(body.token || "").trim();
|
|
2161
|
+
if (!token) {
|
|
2162
|
+
jsonResponse(res, 400, { error: "Token is required" });
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
await writeConfigFile({ githubToken: token });
|
|
2166
|
+
jsonResponse(res, 200, { success: true, message: "GitHub token saved successfully" });
|
|
2167
|
+
} catch (err) {
|
|
2168
|
+
jsonResponse(res, 500, { error: String(err) });
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
API_ROUTES.DELETE["/api/github-token"] = async (_req, res, _url) => {
|
|
2172
|
+
try {
|
|
2173
|
+
await removeConfigKeys(["githubToken"]);
|
|
2174
|
+
jsonResponse(res, 200, { success: true, message: "GitHub token removed successfully" });
|
|
2175
|
+
} catch (err) {
|
|
2176
|
+
jsonResponse(res, 500, { error: String(err) });
|
|
2177
|
+
}
|
|
2178
|
+
};
|
|
1876
2179
|
API_ROUTES.POST["/api/install"] = async (req, res, _url) => {
|
|
1877
2180
|
try {
|
|
1878
2181
|
const body = await parseBody(req);
|
|
@@ -2345,5 +2648,9 @@ export {
|
|
|
2345
2648
|
adminOwnerAdd,
|
|
2346
2649
|
adminOwnerRemove,
|
|
2347
2650
|
adminAccess,
|
|
2651
|
+
listConfig,
|
|
2652
|
+
getConfigValue,
|
|
2653
|
+
setConfigValue,
|
|
2654
|
+
resetConfig,
|
|
2348
2655
|
startGuiServer
|
|
2349
2656
|
};
|
package/dist/electron-entry.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -21,20 +21,24 @@ import {
|
|
|
21
21
|
fetchSkillPackage,
|
|
22
22
|
getAdapterByPlatform,
|
|
23
23
|
getAllAdapters,
|
|
24
|
+
getConfigValue,
|
|
24
25
|
getInstalledSkills,
|
|
25
26
|
getPlatformLinksDir,
|
|
26
27
|
getSkillsDir,
|
|
27
28
|
installSkill,
|
|
28
29
|
isSkillInstalled,
|
|
30
|
+
listConfig,
|
|
29
31
|
loadRegistry,
|
|
30
32
|
publishSkill,
|
|
33
|
+
resetConfig,
|
|
31
34
|
saveRegistry,
|
|
32
35
|
searchSkillmarketPackages,
|
|
36
|
+
setConfigValue,
|
|
33
37
|
startGuiServer,
|
|
34
38
|
uninstallAll,
|
|
35
39
|
uninstallSkill,
|
|
36
40
|
updateSkill
|
|
37
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-76RGF4GX.js";
|
|
38
42
|
|
|
39
43
|
// src/cli.ts
|
|
40
44
|
import { Command } from "commander";
|
|
@@ -303,13 +307,13 @@ function parseGitHubUrl(input) {
|
|
|
303
307
|
const repo = match[2].replace(/\.git$/, "");
|
|
304
308
|
const branch = match[3] || "main";
|
|
305
309
|
const commitOrPath = match[4] || match[3];
|
|
306
|
-
const
|
|
310
|
+
const path4 = match[4] || void 0;
|
|
307
311
|
return {
|
|
308
312
|
owner,
|
|
309
313
|
repo,
|
|
310
314
|
branch: commitOrPath && !commitOrPath.includes("/") ? commitOrPath : branch,
|
|
311
315
|
commit: commitOrPath?.match(/^[0-9a-f]{40}$/) ? commitOrPath : void 0,
|
|
312
|
-
path:
|
|
316
|
+
path: path4
|
|
313
317
|
};
|
|
314
318
|
}
|
|
315
319
|
}
|
|
@@ -641,222 +645,6 @@ async function verifySkill(skillName) {
|
|
|
641
645
|
}
|
|
642
646
|
}
|
|
643
647
|
|
|
644
|
-
// src/commands/config.ts
|
|
645
|
-
import path4 from "path";
|
|
646
|
-
import fs4 from "fs-extra";
|
|
647
|
-
import os from "os";
|
|
648
|
-
var CONFIG_DEFINITIONS = [
|
|
649
|
-
{
|
|
650
|
-
key: "npmScope",
|
|
651
|
-
envVar: "SKM_NPM_SCOPE",
|
|
652
|
-
defaultValue: "@itismyskillmarket",
|
|
653
|
-
description: "Primary npm scope for publishing and lookup"
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
key: "npmScopeFallback",
|
|
657
|
-
envVar: "SKM_NPM_SCOPE_FALLBACK",
|
|
658
|
-
defaultValue: "@wanxuchen",
|
|
659
|
-
description: "Fallback npm scope (backward compatibility)"
|
|
660
|
-
},
|
|
661
|
-
{
|
|
662
|
-
key: "npmRegistry",
|
|
663
|
-
envVar: "SKM_NPM_REGISTRY",
|
|
664
|
-
defaultValue: "https://registry.npmjs.org",
|
|
665
|
-
description: "npm registry URL (mirror/proxy support)"
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
key: "npmScopes",
|
|
669
|
-
envVar: "SKM_NPM_SCOPES",
|
|
670
|
-
defaultValue: "@itismyskillmarket,@wanxuchen,@thisisskillmarket,@this-is-skillmarket,@skillmarket",
|
|
671
|
-
description: "Comma-separated list of npm scopes to search"
|
|
672
|
-
},
|
|
673
|
-
{
|
|
674
|
-
key: "skmUrl",
|
|
675
|
-
envVar: "SKM_URL",
|
|
676
|
-
defaultValue: "https://www.npmjs.com/package/@itismyskillmarket",
|
|
677
|
-
description: "Base URL for skill links (publish output)"
|
|
678
|
-
}
|
|
679
|
-
];
|
|
680
|
-
function getConfigPath() {
|
|
681
|
-
return path4.join(os.homedir(), ".skillmarket", "config.json");
|
|
682
|
-
}
|
|
683
|
-
async function readConfigFile() {
|
|
684
|
-
try {
|
|
685
|
-
const configPath = getConfigPath();
|
|
686
|
-
if (await fs4.pathExists(configPath)) {
|
|
687
|
-
const data = await fs4.readJson(configPath);
|
|
688
|
-
const valid = {};
|
|
689
|
-
for (const def of CONFIG_DEFINITIONS) {
|
|
690
|
-
if (data[def.key] !== void 0) {
|
|
691
|
-
valid[def.key] = String(data[def.key]);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
return valid;
|
|
695
|
-
}
|
|
696
|
-
} catch {
|
|
697
|
-
}
|
|
698
|
-
return {};
|
|
699
|
-
}
|
|
700
|
-
async function writeConfigFile(updates) {
|
|
701
|
-
const configPath = getConfigPath();
|
|
702
|
-
await fs4.ensureDir(path4.dirname(configPath));
|
|
703
|
-
let existing = {};
|
|
704
|
-
try {
|
|
705
|
-
if (await fs4.pathExists(configPath)) {
|
|
706
|
-
existing = await fs4.readJson(configPath);
|
|
707
|
-
}
|
|
708
|
-
} catch {
|
|
709
|
-
}
|
|
710
|
-
const merged = { ...existing, ...updates };
|
|
711
|
-
for (const key of Object.keys(merged)) {
|
|
712
|
-
if (merged[key] === void 0) {
|
|
713
|
-
delete merged[key];
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
await fs4.writeJson(configPath, merged, { spaces: 2 });
|
|
717
|
-
return merged;
|
|
718
|
-
}
|
|
719
|
-
async function removeConfigKeys(keys) {
|
|
720
|
-
const configPath = getConfigPath();
|
|
721
|
-
if (!await fs4.pathExists(configPath)) return;
|
|
722
|
-
try {
|
|
723
|
-
const existing = await fs4.readJson(configPath);
|
|
724
|
-
for (const key of keys) {
|
|
725
|
-
delete existing[key];
|
|
726
|
-
}
|
|
727
|
-
await fs4.writeJson(configPath, existing, { spaces: 2 });
|
|
728
|
-
} catch {
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
async function removeConfigFile() {
|
|
732
|
-
const configPath = getConfigPath();
|
|
733
|
-
if (await fs4.pathExists(configPath)) {
|
|
734
|
-
await fs4.remove(configPath);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
async function getAllConfig() {
|
|
738
|
-
const fileConfig = await readConfigFile();
|
|
739
|
-
return CONFIG_DEFINITIONS.map((def) => {
|
|
740
|
-
const envValue = process.env[def.envVar];
|
|
741
|
-
if (envValue !== void 0) {
|
|
742
|
-
return {
|
|
743
|
-
...def,
|
|
744
|
-
value: envValue,
|
|
745
|
-
source: "env"
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
const fileValue = fileConfig[def.key];
|
|
749
|
-
if (fileValue !== void 0) {
|
|
750
|
-
return {
|
|
751
|
-
...def,
|
|
752
|
-
value: fileValue,
|
|
753
|
-
source: "file"
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
return {
|
|
757
|
-
...def,
|
|
758
|
-
value: def.defaultValue,
|
|
759
|
-
source: "default"
|
|
760
|
-
};
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
async function getConfig(key) {
|
|
764
|
-
const all = await getAllConfig();
|
|
765
|
-
return all.find((c) => c.key === key) || null;
|
|
766
|
-
}
|
|
767
|
-
async function listConfig() {
|
|
768
|
-
const entries = await getAllConfig();
|
|
769
|
-
console.log("\n\u{1F527} SkillMarket Configuration\n");
|
|
770
|
-
const maxKeyLen = Math.max(...entries.map((e) => e.key.length));
|
|
771
|
-
for (const entry of entries) {
|
|
772
|
-
const key = entry.key.padEnd(maxKeyLen + 2);
|
|
773
|
-
const sourceBadge = getSourceBadge(entry.source);
|
|
774
|
-
console.log(` ${sourceBadge} ${key}${entry.value}`);
|
|
775
|
-
}
|
|
776
|
-
console.log("");
|
|
777
|
-
console.log(" \u6765\u6E90: \u{1F535} \u73AF\u5883\u53D8\u91CF \u{1F7E2} \u914D\u7F6E\u6587\u4EF6 \u26AA \u9ED8\u8BA4\u503C");
|
|
778
|
-
console.log(" \u914D\u7F6E\u6587\u4EF6: " + getConfigPath());
|
|
779
|
-
console.log("");
|
|
780
|
-
console.log(" \u7528\u6CD5:");
|
|
781
|
-
console.log(" skm config get <key> \u67E5\u770B\u914D\u7F6E\u503C");
|
|
782
|
-
console.log(" skm config set <key> <v> \u8BBE\u7F6E\u914D\u7F6E\u503C");
|
|
783
|
-
console.log(" skm config reset <key> \u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C");
|
|
784
|
-
console.log(" skm config reset --all \u5168\u90E8\u91CD\u7F6E");
|
|
785
|
-
console.log("");
|
|
786
|
-
}
|
|
787
|
-
function getSourceBadge(source) {
|
|
788
|
-
switch (source) {
|
|
789
|
-
case "env":
|
|
790
|
-
return "\u{1F535}";
|
|
791
|
-
case "file":
|
|
792
|
-
return "\u{1F7E2}";
|
|
793
|
-
case "default":
|
|
794
|
-
return "\u26AA";
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
async function getConfigValue(key) {
|
|
798
|
-
const entry = await getConfig(key);
|
|
799
|
-
if (!entry) {
|
|
800
|
-
console.error(`\u274C Unknown config key: "${key}"`);
|
|
801
|
-
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
802
|
-
process.exit(1);
|
|
803
|
-
}
|
|
804
|
-
console.log(`
|
|
805
|
-
\u{1F527} ${entry.key}`);
|
|
806
|
-
console.log(` Value: ${entry.value}`);
|
|
807
|
-
console.log(` Source: ${entry.source}`);
|
|
808
|
-
console.log(` Env var: ${entry.envVar}`);
|
|
809
|
-
console.log(` Default: ${entry.defaultValue}`);
|
|
810
|
-
console.log(` Description: ${entry.description}`);
|
|
811
|
-
console.log("");
|
|
812
|
-
}
|
|
813
|
-
async function setConfigValue(key, value) {
|
|
814
|
-
const def = CONFIG_DEFINITIONS.find((d) => d.key === key);
|
|
815
|
-
if (!def) {
|
|
816
|
-
console.error(`\u274C Unknown config key: "${key}"`);
|
|
817
|
-
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
818
|
-
process.exit(1);
|
|
819
|
-
}
|
|
820
|
-
await writeConfigFile({ [key]: value });
|
|
821
|
-
console.log(`
|
|
822
|
-
\u2705 ${key} set to "${value}"`);
|
|
823
|
-
console.log(` Stored in: ${getConfigPath()}`);
|
|
824
|
-
console.log("");
|
|
825
|
-
if (process.env[def.envVar] !== void 0) {
|
|
826
|
-
console.log(` \u26A0\uFE0F Currently overridden by environment variable ${def.envVar}=${process.env[def.envVar]}`);
|
|
827
|
-
console.log(` To use the config file value, unset the environment variable.`);
|
|
828
|
-
console.log("");
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
async function resetConfig(key, all = false) {
|
|
832
|
-
if (all) {
|
|
833
|
-
await removeConfigFile();
|
|
834
|
-
console.log("\n\u2705 All configuration reset to defaults.");
|
|
835
|
-
console.log(` Removed: ${getConfigPath()}`);
|
|
836
|
-
console.log("");
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
if (key) {
|
|
840
|
-
const def = CONFIG_DEFINITIONS.find((d) => d.key === key);
|
|
841
|
-
if (!def) {
|
|
842
|
-
console.error(`\u274C Unknown config key: "${key}"`);
|
|
843
|
-
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
844
|
-
process.exit(1);
|
|
845
|
-
}
|
|
846
|
-
await removeConfigKeys([key]);
|
|
847
|
-
const sourceNow = process.env[def.envVar] ? "env" : "default";
|
|
848
|
-
console.log(`
|
|
849
|
-
\u2705 ${key} reset to ${sourceNow === "env" ? "environment variable" : "default"} value.`);
|
|
850
|
-
console.log(` Effective value: "${sourceNow === "env" ? process.env[def.envVar] : def.defaultValue}"`);
|
|
851
|
-
console.log("");
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
console.log("\n\u{1F527} Usage: skm config reset <key>");
|
|
855
|
-
console.log(" skm config reset --all");
|
|
856
|
-
console.log(` Valid keys: ${CONFIG_DEFINITIONS.map((d) => d.key).join(", ")}`);
|
|
857
|
-
console.log("");
|
|
858
|
-
}
|
|
859
|
-
|
|
860
648
|
// src/cli.ts
|
|
861
649
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
862
650
|
var __dirname2 = dirname(__filename2);
|
package/gui/app.js
CHANGED
|
@@ -248,6 +248,24 @@ const translations = {
|
|
|
248
248
|
|
|
249
249
|
// 通用错误
|
|
250
250
|
'error.generic': 'Error',
|
|
251
|
+
|
|
252
|
+
// GitHub Token
|
|
253
|
+
'token.title': '🔑 GitHub Token',
|
|
254
|
+
'token.desc': 'Used to create GitHub Releases and other operations from the desktop app.',
|
|
255
|
+
'token.checking': 'Checking...',
|
|
256
|
+
'token.active': '✅ Token set ({prefix})',
|
|
257
|
+
'token.inactive': '❌ Token not set',
|
|
258
|
+
'token.checkFailed': '⚠️ Check failed: {error}',
|
|
259
|
+
'token.placeholder': 'Enter GitHub Personal Access Token (ghp_... / gho_...)',
|
|
260
|
+
'token.placeholderOverride': 'Enter new Token to override current value...',
|
|
261
|
+
'token.save': '💾 Save',
|
|
262
|
+
'token.remove': '🗑 Remove',
|
|
263
|
+
'token.hint': 'Token is stored in',
|
|
264
|
+
'token.saved': '✅ GitHub Token saved',
|
|
265
|
+
'token.removed': '✅ GitHub Token removed',
|
|
266
|
+
'token.saveFailed': 'Save failed: {error}',
|
|
267
|
+
'token.removeFailed': 'Remove failed: {error}',
|
|
268
|
+
'token.inputRequired': 'Please enter a GitHub Token',
|
|
251
269
|
},
|
|
252
270
|
|
|
253
271
|
zh: {
|
|
@@ -476,6 +494,24 @@ const translations = {
|
|
|
476
494
|
|
|
477
495
|
// 通用错误
|
|
478
496
|
'error.generic': '错误',
|
|
497
|
+
|
|
498
|
+
// GitHub Token
|
|
499
|
+
'token.title': '🔑 GitHub Token',
|
|
500
|
+
'token.desc': '用于从桌面软件直接创建 GitHub Release 等操作。',
|
|
501
|
+
'token.checking': '检查中...',
|
|
502
|
+
'token.active': '✅ Token 已设置 ({prefix})',
|
|
503
|
+
'token.inactive': '❌ 未设置 Token',
|
|
504
|
+
'token.checkFailed': '⚠️ 检查失败: {error}',
|
|
505
|
+
'token.placeholder': '输入 GitHub Personal Access Token (ghp_... / gho_...)',
|
|
506
|
+
'token.placeholderOverride': '输入新 Token 以覆盖当前值...',
|
|
507
|
+
'token.save': '💾 保存',
|
|
508
|
+
'token.remove': '🗑 移除',
|
|
509
|
+
'token.hint': 'Token 保存在',
|
|
510
|
+
'token.saved': '✅ GitHub Token 已保存',
|
|
511
|
+
'token.removed': '✅ GitHub Token 已移除',
|
|
512
|
+
'token.saveFailed': '保存失败: {error}',
|
|
513
|
+
'token.removeFailed': '移除失败: {error}',
|
|
514
|
+
'token.inputRequired': '请输入 GitHub Token',
|
|
479
515
|
}
|
|
480
516
|
};
|
|
481
517
|
|
|
@@ -634,6 +670,25 @@ function applyI18nToStaticElements() {
|
|
|
634
670
|
if (uploadActionBoth) uploadActionBoth.innerHTML = t('upload.actionBoth');
|
|
635
671
|
if (uploadActionDiscard) uploadActionDiscard.innerHTML = t('upload.actionDiscard');
|
|
636
672
|
if (uploadProgressText) uploadProgressText.textContent = t('upload.processing');
|
|
673
|
+
|
|
674
|
+
// GitHub Token 静态文本
|
|
675
|
+
const tokenTitle = document.getElementById('token-section-title');
|
|
676
|
+
const tokenDesc = document.getElementById('token-section-desc');
|
|
677
|
+
const tokenHint = document.getElementById('token-section-hint');
|
|
678
|
+
const tokenSaveBtn = document.getElementById('token-save-btn');
|
|
679
|
+
const tokenRemoveBtn = document.getElementById('token-remove-btn');
|
|
680
|
+
const tokenInput = document.getElementById('token-input');
|
|
681
|
+
|
|
682
|
+
if (tokenTitle) tokenTitle.textContent = t('token.title');
|
|
683
|
+
if (tokenDesc) tokenDesc.textContent = t('token.desc');
|
|
684
|
+
if (tokenHint) tokenHint.textContent = t('token.hint');
|
|
685
|
+
if (tokenSaveBtn) tokenSaveBtn.textContent = t('token.save');
|
|
686
|
+
if (tokenRemoveBtn) tokenRemoveBtn.textContent = t('token.remove');
|
|
687
|
+
if (tokenInput && !tokenInput.value) {
|
|
688
|
+
// Only update placeholder when input is empty
|
|
689
|
+
const hasToken = document.getElementById('token-indicator')?.classList.contains('token-active');
|
|
690
|
+
tokenInput.placeholder = hasToken ? t('token.placeholderOverride') : t('token.placeholder');
|
|
691
|
+
}
|
|
637
692
|
}
|
|
638
693
|
|
|
639
694
|
// -----------------------------------------------------------------------------
|
|
@@ -833,6 +888,23 @@ function initializeControls() {
|
|
|
833
888
|
});
|
|
834
889
|
}
|
|
835
890
|
|
|
891
|
+
// GitHub Token 管理
|
|
892
|
+
const tokenSaveBtn = document.getElementById('token-save-btn');
|
|
893
|
+
const tokenRemoveBtn = document.getElementById('token-remove-btn');
|
|
894
|
+
const tokenInput = document.getElementById('token-input');
|
|
895
|
+
|
|
896
|
+
if (tokenSaveBtn) {
|
|
897
|
+
tokenSaveBtn.addEventListener('click', saveGithubToken);
|
|
898
|
+
}
|
|
899
|
+
if (tokenRemoveBtn) {
|
|
900
|
+
tokenRemoveBtn.addEventListener('click', removeGithubToken);
|
|
901
|
+
}
|
|
902
|
+
if (tokenInput) {
|
|
903
|
+
tokenInput.addEventListener('keydown', (e) => {
|
|
904
|
+
if (e.key === 'Enter') saveGithubToken();
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
836
908
|
// 语言切换
|
|
837
909
|
const langSelect = document.getElementById('lang-select');
|
|
838
910
|
if (langSelect) {
|
|
@@ -1160,8 +1232,10 @@ async function loadHelp() {
|
|
|
1160
1232
|
container.innerHTML = `<div class="loading">${t('loading.generic')}</div>`;
|
|
1161
1233
|
|
|
1162
1234
|
try {
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1235
|
+
const [config] = await Promise.all([
|
|
1236
|
+
fetch('/api/config').then(r => r.json()),
|
|
1237
|
+
checkGithubToken(),
|
|
1238
|
+
]);
|
|
1165
1239
|
renderHelp(config, container);
|
|
1166
1240
|
} catch (err) {
|
|
1167
1241
|
container.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
|
|
@@ -1276,6 +1350,87 @@ skm config reset --all # 全部恢复默认</pre>
|
|
|
1276
1350
|
`;
|
|
1277
1351
|
}
|
|
1278
1352
|
|
|
1353
|
+
// -----------------------------------------------------------------------------
|
|
1354
|
+
// GitHub Token 管理
|
|
1355
|
+
// -----------------------------------------------------------------------------
|
|
1356
|
+
|
|
1357
|
+
async function checkGithubToken() {
|
|
1358
|
+
try {
|
|
1359
|
+
const response = await fetch('/api/github-token');
|
|
1360
|
+
const data = await response.json();
|
|
1361
|
+
|
|
1362
|
+
const indicator = document.getElementById('token-indicator');
|
|
1363
|
+
const statusText = document.getElementById('token-status-text');
|
|
1364
|
+
const tokenInput = document.getElementById('token-input');
|
|
1365
|
+
const removeBtn = document.getElementById('token-remove-btn');
|
|
1366
|
+
|
|
1367
|
+
if (data.hasToken) {
|
|
1368
|
+
indicator.className = 'token-indicator token-active';
|
|
1369
|
+
statusText.textContent = t('token.active', { prefix: data.tokenPrefix });
|
|
1370
|
+
if (removeBtn) removeBtn.style.display = '';
|
|
1371
|
+
if (tokenInput) tokenInput.placeholder = t('token.placeholderOverride');
|
|
1372
|
+
// Re-apply i18n for button texts
|
|
1373
|
+
const saveBtn = document.getElementById('token-save-btn');
|
|
1374
|
+
const rmBtn = document.getElementById('token-remove-btn');
|
|
1375
|
+
if (saveBtn) saveBtn.textContent = t('token.save');
|
|
1376
|
+
if (rmBtn) rmBtn.textContent = t('token.remove');
|
|
1377
|
+
} else {
|
|
1378
|
+
indicator.className = 'token-indicator token-none';
|
|
1379
|
+
statusText.textContent = t('token.inactive');
|
|
1380
|
+
if (removeBtn) removeBtn.style.display = 'none';
|
|
1381
|
+
if (tokenInput) tokenInput.placeholder = t('token.placeholder');
|
|
1382
|
+
}
|
|
1383
|
+
} catch (err) {
|
|
1384
|
+
const statusText = document.getElementById('token-status-text');
|
|
1385
|
+
if (statusText) statusText.textContent = t('token.checkFailed', { error: err.message });
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
async function saveGithubToken() {
|
|
1390
|
+
const input = document.getElementById('token-input');
|
|
1391
|
+
const token = input ? input.value.trim() : '';
|
|
1392
|
+
|
|
1393
|
+
if (!token) {
|
|
1394
|
+
showToast(t('token.inputRequired'), 'error');
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
try {
|
|
1399
|
+
const response = await fetch('/api/github-token', {
|
|
1400
|
+
method: 'POST',
|
|
1401
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1402
|
+
body: JSON.stringify({ token }),
|
|
1403
|
+
});
|
|
1404
|
+
const result = await response.json();
|
|
1405
|
+
if (result.error) {
|
|
1406
|
+
showToast(t('token.saveFailed', { error: result.error }), 'error');
|
|
1407
|
+
} else {
|
|
1408
|
+
showToast(t('token.saved'), 'success');
|
|
1409
|
+
input.value = '';
|
|
1410
|
+
checkGithubToken();
|
|
1411
|
+
}
|
|
1412
|
+
} catch (err) {
|
|
1413
|
+
showToast(t('token.saveFailed', { error: err.message }), 'error');
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
async function removeGithubToken() {
|
|
1418
|
+
try {
|
|
1419
|
+
const response = await fetch('/api/github-token', {
|
|
1420
|
+
method: 'DELETE',
|
|
1421
|
+
});
|
|
1422
|
+
const result = await response.json();
|
|
1423
|
+
if (result.error) {
|
|
1424
|
+
showToast(t('token.removeFailed', { error: result.error }), 'error');
|
|
1425
|
+
} else {
|
|
1426
|
+
showToast(t('token.removed'), 'success');
|
|
1427
|
+
checkGithubToken();
|
|
1428
|
+
}
|
|
1429
|
+
} catch (err) {
|
|
1430
|
+
showToast(t('token.removeFailed', { error: err.message }), 'error');
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1279
1434
|
async function installSkill(skillId) {
|
|
1280
1435
|
try {
|
|
1281
1436
|
showToast(t('toast.installing', { skillId: skillId }), 'info');
|
package/gui/index.html
CHANGED
|
@@ -82,6 +82,21 @@
|
|
|
82
82
|
<div class="view-header">
|
|
83
83
|
<h2>Help & Configuration</h2>
|
|
84
84
|
</div>
|
|
85
|
+
<!-- GitHub Token 管理 -->
|
|
86
|
+
<div class="help-token-section" id="help-token-section">
|
|
87
|
+
<h3 id="token-section-title">🔑 GitHub Token</h3>
|
|
88
|
+
<p class="help-token-desc" id="token-section-desc">用于从桌面软件直接创建 GitHub Release 等操作。</p>
|
|
89
|
+
<div class="help-token-status" id="token-status">
|
|
90
|
+
<span class="token-indicator token-unknown" id="token-indicator"></span>
|
|
91
|
+
<span id="token-status-text">检查中...</span>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="help-token-input-row">
|
|
94
|
+
<input type="password" id="token-input" placeholder="输入 GitHub Personal Access Token (ghp_... / gho_...)" spellcheck="false">
|
|
95
|
+
<button id="token-save-btn" class="btn btn-success"></button>
|
|
96
|
+
<button id="token-remove-btn" class="btn btn-danger" style="display:none"></button>
|
|
97
|
+
</div>
|
|
98
|
+
<p class="help-token-hint"><span id="token-section-hint">Token 保存在</span> <code>~/.skillmarket/config.json</code>。</p>
|
|
99
|
+
</div>
|
|
85
100
|
<div id="help-content"></div>
|
|
86
101
|
</div>
|
|
87
102
|
|
package/gui/style.css
CHANGED
|
@@ -1171,3 +1171,88 @@ body {
|
|
|
1171
1171
|
background: var(--bg-secondary);
|
|
1172
1172
|
color: var(--text-secondary);
|
|
1173
1173
|
}
|
|
1174
|
+
|
|
1175
|
+
/* -----------------------------------------------------------------------------
|
|
1176
|
+
GitHub Token 管理(Help 视图顶部)
|
|
1177
|
+
----------------------------------------------------------------------------- */
|
|
1178
|
+
|
|
1179
|
+
.help-token-section {
|
|
1180
|
+
background: var(--bg-secondary);
|
|
1181
|
+
border: 1px solid var(--border-color);
|
|
1182
|
+
border-radius: 10px;
|
|
1183
|
+
padding: 20px 22px;
|
|
1184
|
+
margin-bottom: 18px;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.help-token-section h3 {
|
|
1188
|
+
color: var(--accent);
|
|
1189
|
+
font-size: 1.05rem;
|
|
1190
|
+
margin-bottom: 6px;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.help-token-desc {
|
|
1194
|
+
color: var(--text-muted);
|
|
1195
|
+
font-size: 0.82rem;
|
|
1196
|
+
margin-bottom: 12px;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
.help-token-status {
|
|
1200
|
+
display: flex;
|
|
1201
|
+
align-items: center;
|
|
1202
|
+
gap: 8px;
|
|
1203
|
+
margin-bottom: 10px;
|
|
1204
|
+
font-size: 0.85rem;
|
|
1205
|
+
color: var(--text-secondary);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.token-indicator {
|
|
1209
|
+
width: 10px;
|
|
1210
|
+
height: 10px;
|
|
1211
|
+
border-radius: 50%;
|
|
1212
|
+
display: inline-block;
|
|
1213
|
+
flex-shrink: 0;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.token-indicator.token-unknown { background: var(--text-muted); }
|
|
1217
|
+
.token-indicator.token-active { background: var(--success); }
|
|
1218
|
+
.token-indicator.token-none { background: var(--danger); }
|
|
1219
|
+
|
|
1220
|
+
.help-token-input-row {
|
|
1221
|
+
display: flex;
|
|
1222
|
+
gap: 8px;
|
|
1223
|
+
align-items: center;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.help-token-input-row input {
|
|
1227
|
+
flex: 1;
|
|
1228
|
+
padding: 8px 12px;
|
|
1229
|
+
background: var(--bg-primary);
|
|
1230
|
+
border: 1px solid var(--border-color);
|
|
1231
|
+
border-radius: 6px;
|
|
1232
|
+
color: var(--text-secondary);
|
|
1233
|
+
font-size: 0.85rem;
|
|
1234
|
+
font-family: 'Menlo', 'Consolas', monospace;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.help-token-input-row input:focus {
|
|
1238
|
+
outline: none;
|
|
1239
|
+
border-color: var(--accent);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
.help-token-input-row input::placeholder {
|
|
1243
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1244
|
+
color: var(--text-muted);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
.help-token-hint {
|
|
1248
|
+
color: var(--text-muted);
|
|
1249
|
+
font-size: 0.75rem;
|
|
1250
|
+
margin-top: 8px;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
.help-token-hint code {
|
|
1254
|
+
background: var(--bg-card);
|
|
1255
|
+
padding: 1px 5px;
|
|
1256
|
+
border-radius: 3px;
|
|
1257
|
+
font-size: 0.72rem;
|
|
1258
|
+
}
|