@anytio/pspm 0.11.0 → 0.13.0
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 +40 -2
- package/CLI_GUIDE.md +245 -6
- package/README.md +5 -1
- package/dist/index.js +1711 -948
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -3,12 +3,12 @@ import { stat, writeFile, readdir, mkdir, rm, rename, access as access$1, readFi
|
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
import { join, dirname, basename, resolve, relative } from 'path';
|
|
5
5
|
import * as ini from 'ini';
|
|
6
|
+
import { createHash, randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
|
|
6
7
|
import ignore from 'ignore';
|
|
7
|
-
import { createHash, randomBytes } from 'crypto';
|
|
8
8
|
import * as semver2 from 'semver';
|
|
9
9
|
import semver2__default from 'semver';
|
|
10
10
|
import { checkbox } from '@inquirer/prompts';
|
|
11
|
-
import { readFileSync } from 'fs';
|
|
11
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
12
12
|
import { fileURLToPath, URL as URL$1 } from 'url';
|
|
13
13
|
import { Command } from 'commander';
|
|
14
14
|
import { createInterface } from 'readline';
|
|
@@ -603,6 +603,8 @@ __export(config_exports, {
|
|
|
603
603
|
findProjectConfig: () => findProjectConfig,
|
|
604
604
|
getCacheDir: () => getCacheDir,
|
|
605
605
|
getConfigPath: () => getConfigPath,
|
|
606
|
+
getEncryptionKey: () => getEncryptionKey,
|
|
607
|
+
getEncryptionScope: () => getEncryptionScope,
|
|
606
608
|
getLegacyLockfilePath: () => getLegacyLockfilePath,
|
|
607
609
|
getLegacySkillsDir: () => getLegacySkillsDir,
|
|
608
610
|
getLockfilePath: () => getLockfilePath,
|
|
@@ -613,9 +615,11 @@ __export(config_exports, {
|
|
|
613
615
|
isGlobalMode: () => isGlobalMode,
|
|
614
616
|
isLoggedIn: () => isLoggedIn,
|
|
615
617
|
readUserConfig: () => readUserConfig,
|
|
618
|
+
removeEncryptionKey: () => removeEncryptionKey,
|
|
616
619
|
requireApiKey: () => requireApiKey,
|
|
617
620
|
resolveConfig: () => resolveConfig,
|
|
618
621
|
setCredentials: () => setCredentials,
|
|
622
|
+
setEncryptionKey: () => setEncryptionKey,
|
|
619
623
|
setGlobalMode: () => setGlobalMode,
|
|
620
624
|
writeUserConfig: () => writeUserConfig
|
|
621
625
|
});
|
|
@@ -685,12 +689,21 @@ async function readUserConfig() {
|
|
|
685
689
|
}
|
|
686
690
|
}
|
|
687
691
|
}
|
|
692
|
+
const encryptionKeys = {};
|
|
693
|
+
for (const key of Object.keys(parsed)) {
|
|
694
|
+
const encKeyMatch = key.match(/^encryption-key:(.+)$/);
|
|
695
|
+
if (encKeyMatch) {
|
|
696
|
+
const scope = encKeyMatch[1];
|
|
697
|
+
encryptionKeys[scope] = parsed[key];
|
|
698
|
+
}
|
|
699
|
+
}
|
|
688
700
|
return {
|
|
689
701
|
registry: parsed.registry,
|
|
690
702
|
authToken: parsed.authToken,
|
|
691
703
|
username: parsed.username,
|
|
692
704
|
scopedRegistries: Object.keys(scopedRegistries).length > 0 ? scopedRegistries : void 0,
|
|
693
|
-
registryTokens: Object.keys(registryTokens).length > 0 ? registryTokens : void 0
|
|
705
|
+
registryTokens: Object.keys(registryTokens).length > 0 ? registryTokens : void 0,
|
|
706
|
+
encryptionKeys: Object.keys(encryptionKeys).length > 0 ? encryptionKeys : void 0
|
|
694
707
|
};
|
|
695
708
|
} catch (error) {
|
|
696
709
|
if (process.env.PSPM_DEBUG) {
|
|
@@ -713,6 +726,12 @@ async function writeUserConfig(config2) {
|
|
|
713
726
|
if (config2.username) {
|
|
714
727
|
lines.push(`username = ${config2.username}`);
|
|
715
728
|
}
|
|
729
|
+
if (config2.encryptionKeys && Object.keys(config2.encryptionKeys).length > 0) {
|
|
730
|
+
lines.push("; Encryption keys (scope -> passphrase)");
|
|
731
|
+
for (const [scope, key] of Object.entries(config2.encryptionKeys)) {
|
|
732
|
+
lines.push(`encryption-key:${scope} = ${key}`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
716
735
|
lines.push("");
|
|
717
736
|
await mkdir(dirname(configPath), { recursive: true });
|
|
718
737
|
await writeFile(configPath, lines.join("\n"));
|
|
@@ -896,6 +915,36 @@ async function getRegistryUrl() {
|
|
|
896
915
|
const resolved = await resolveConfig();
|
|
897
916
|
return resolved.registryUrl;
|
|
898
917
|
}
|
|
918
|
+
async function getEncryptionKey(scope) {
|
|
919
|
+
const envSuffix = scope.replace(/^@/, "").replace(/\//g, "_").toUpperCase();
|
|
920
|
+
const envKey = process.env[`PSPM_ENCRYPTION_KEY_${envSuffix}`];
|
|
921
|
+
if (envKey) {
|
|
922
|
+
return envKey;
|
|
923
|
+
}
|
|
924
|
+
const config2 = await readUserConfig();
|
|
925
|
+
return config2.encryptionKeys?.[scope];
|
|
926
|
+
}
|
|
927
|
+
async function setEncryptionKey(scope, key) {
|
|
928
|
+
const config2 = await readUserConfig();
|
|
929
|
+
if (!config2.encryptionKeys) {
|
|
930
|
+
config2.encryptionKeys = {};
|
|
931
|
+
}
|
|
932
|
+
config2.encryptionKeys[scope] = key;
|
|
933
|
+
await writeUserConfig(config2);
|
|
934
|
+
}
|
|
935
|
+
async function removeEncryptionKey(scope) {
|
|
936
|
+
const config2 = await readUserConfig();
|
|
937
|
+
if (config2.encryptionKeys) {
|
|
938
|
+
delete config2.encryptionKeys[scope];
|
|
939
|
+
if (Object.keys(config2.encryptionKeys).length === 0) {
|
|
940
|
+
config2.encryptionKeys = void 0;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
await writeUserConfig(config2);
|
|
944
|
+
}
|
|
945
|
+
function getEncryptionScope(namespace, owner) {
|
|
946
|
+
return `@${namespace}/${owner}`;
|
|
947
|
+
}
|
|
899
948
|
var DEFAULT_REGISTRY_URL, _globalMode;
|
|
900
949
|
var init_config = __esm({
|
|
901
950
|
"src/config.ts"() {
|
|
@@ -904,6 +953,62 @@ var init_config = __esm({
|
|
|
904
953
|
_globalMode = false;
|
|
905
954
|
}
|
|
906
955
|
});
|
|
956
|
+
function deriveKey(passphrase, salt) {
|
|
957
|
+
return scryptSync(passphrase, salt, KEY_LENGTH, {
|
|
958
|
+
N: SCRYPT_COST,
|
|
959
|
+
r: SCRYPT_BLOCK_SIZE,
|
|
960
|
+
p: SCRYPT_PARALLELISM
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
function encryptBuffer(data, passphrase, scope) {
|
|
964
|
+
const salt = randomBytes(SALT_LENGTH);
|
|
965
|
+
const iv = randomBytes(IV_LENGTH);
|
|
966
|
+
const key = deriveKey(passphrase, salt);
|
|
967
|
+
const cipher = createCipheriv(ALGORITHM, key, iv, {
|
|
968
|
+
authTagLength: AUTH_TAG_LENGTH
|
|
969
|
+
});
|
|
970
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
971
|
+
const authTag = cipher.getAuthTag();
|
|
972
|
+
return {
|
|
973
|
+
encrypted,
|
|
974
|
+
metadata: {
|
|
975
|
+
algorithm: ALGORITHM,
|
|
976
|
+
kdf: "scrypt",
|
|
977
|
+
salt: salt.toString("hex"),
|
|
978
|
+
iv: iv.toString("hex"),
|
|
979
|
+
authTag: authTag.toString("hex"),
|
|
980
|
+
scope
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function decryptBuffer(encrypted, passphrase, metadata) {
|
|
985
|
+
const salt = Buffer.from(metadata.salt, "hex");
|
|
986
|
+
const iv = Buffer.from(metadata.iv, "hex");
|
|
987
|
+
const authTag = Buffer.from(metadata.authTag, "hex");
|
|
988
|
+
const key = deriveKey(passphrase, salt);
|
|
989
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv, {
|
|
990
|
+
authTagLength: AUTH_TAG_LENGTH
|
|
991
|
+
});
|
|
992
|
+
decipher.setAuthTag(authTag);
|
|
993
|
+
const decrypted = Buffer.concat([
|
|
994
|
+
decipher.update(encrypted),
|
|
995
|
+
decipher.final()
|
|
996
|
+
]);
|
|
997
|
+
return decrypted;
|
|
998
|
+
}
|
|
999
|
+
var ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, SCRYPT_COST, SCRYPT_BLOCK_SIZE, SCRYPT_PARALLELISM, AUTH_TAG_LENGTH;
|
|
1000
|
+
var init_encryption = __esm({
|
|
1001
|
+
"src/lib/encryption.ts"() {
|
|
1002
|
+
ALGORITHM = "aes-256-gcm";
|
|
1003
|
+
KEY_LENGTH = 32;
|
|
1004
|
+
IV_LENGTH = 16;
|
|
1005
|
+
SALT_LENGTH = 32;
|
|
1006
|
+
SCRYPT_COST = 16384;
|
|
1007
|
+
SCRYPT_BLOCK_SIZE = 8;
|
|
1008
|
+
SCRYPT_PARALLELISM = 1;
|
|
1009
|
+
AUTH_TAG_LENGTH = 16;
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
907
1012
|
async function loadIgnorePatterns(cwd = process.cwd()) {
|
|
908
1013
|
const ig = ignore();
|
|
909
1014
|
ig.add(ALWAYS_IGNORED);
|
|
@@ -968,7 +1073,9 @@ function validateManifest(manifest) {
|
|
|
968
1073
|
if (!manifest.version) {
|
|
969
1074
|
return { valid: false, error: "Manifest must have a 'version' field" };
|
|
970
1075
|
}
|
|
971
|
-
|
|
1076
|
+
const parts = manifest.name.split("/");
|
|
1077
|
+
const bareName = parts[parts.length - 1];
|
|
1078
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(bareName)) {
|
|
972
1079
|
return {
|
|
973
1080
|
valid: false,
|
|
974
1081
|
error: "Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores"
|
|
@@ -1624,6 +1731,7 @@ var init_specifier2 = __esm({
|
|
|
1624
1731
|
// src/lib/index.ts
|
|
1625
1732
|
var init_lib = __esm({
|
|
1626
1733
|
"src/lib/index.ts"() {
|
|
1734
|
+
init_encryption();
|
|
1627
1735
|
init_ignore();
|
|
1628
1736
|
init_integrity();
|
|
1629
1737
|
init_lockfile();
|
|
@@ -3329,110 +3437,649 @@ var init_add = __esm({
|
|
|
3329
3437
|
}
|
|
3330
3438
|
});
|
|
3331
3439
|
|
|
3332
|
-
// src/commands/
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3440
|
+
// src/commands/install.ts
|
|
3441
|
+
var install_exports = {};
|
|
3442
|
+
__export(install_exports, {
|
|
3443
|
+
install: () => install
|
|
3444
|
+
});
|
|
3445
|
+
function getCacheFilePath(cacheDir, integrity) {
|
|
3446
|
+
const match = integrity.match(/^sha256-(.+)$/);
|
|
3447
|
+
if (!match) {
|
|
3448
|
+
throw new Error(`Invalid integrity format: ${integrity}`);
|
|
3449
|
+
}
|
|
3450
|
+
const base64Hash = match[1];
|
|
3451
|
+
const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
|
|
3452
|
+
return join(cacheDir, `sha256-${hexHash}.tgz`);
|
|
3338
3453
|
}
|
|
3339
|
-
async function
|
|
3454
|
+
async function readFromCache(cacheDir, integrity) {
|
|
3340
3455
|
try {
|
|
3341
|
-
const
|
|
3342
|
-
const
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3456
|
+
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
3457
|
+
const data = await readFile(cachePath);
|
|
3458
|
+
const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
|
|
3459
|
+
if (actualIntegrity !== integrity) {
|
|
3460
|
+
await rm(cachePath, { force: true });
|
|
3461
|
+
return null;
|
|
3346
3462
|
}
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3463
|
+
return data;
|
|
3464
|
+
} catch {
|
|
3465
|
+
return null;
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
async function writeToCache(cacheDir, integrity, data) {
|
|
3469
|
+
try {
|
|
3470
|
+
await mkdir(cacheDir, { recursive: true });
|
|
3471
|
+
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
3472
|
+
await writeFile(cachePath, data);
|
|
3473
|
+
} catch {
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
async function install(specifiers, options) {
|
|
3477
|
+
if (options.global) {
|
|
3478
|
+
const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3479
|
+
setGlobalMode2(true);
|
|
3480
|
+
}
|
|
3481
|
+
if (options.list) {
|
|
3482
|
+
const listSpecifiers = await resolveListToSpecifiers(options.list);
|
|
3483
|
+
if (listSpecifiers.length === 0) {
|
|
3484
|
+
console.log("No skills in the list to install.");
|
|
3485
|
+
return;
|
|
3350
3486
|
}
|
|
3351
|
-
const
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3487
|
+
const allSpecifiers = [...specifiers, ...listSpecifiers];
|
|
3488
|
+
const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3489
|
+
await add2(allSpecifiers, {
|
|
3490
|
+
save: true,
|
|
3491
|
+
agent: options.agent,
|
|
3492
|
+
yes: options.yes,
|
|
3493
|
+
global: options.global
|
|
3494
|
+
});
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3497
|
+
if (specifiers.length > 0) {
|
|
3498
|
+
const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3499
|
+
await add2(specifiers, {
|
|
3500
|
+
save: true,
|
|
3501
|
+
agent: options.agent,
|
|
3502
|
+
yes: options.yes,
|
|
3503
|
+
global: options.global
|
|
3504
|
+
});
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
await installFromLockfile(options);
|
|
3508
|
+
}
|
|
3509
|
+
async function resolveListToSpecifiers(listSpec) {
|
|
3510
|
+
const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
|
|
3511
|
+
if (!match) {
|
|
3512
|
+
console.error(
|
|
3513
|
+
`Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
|
|
3514
|
+
);
|
|
3515
|
+
process.exit(1);
|
|
3516
|
+
}
|
|
3517
|
+
const [, ownerType, ownerName, listName] = match;
|
|
3518
|
+
const config2 = await resolveConfig();
|
|
3519
|
+
configure2({
|
|
3520
|
+
registryUrl: config2.registryUrl,
|
|
3521
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
3522
|
+
});
|
|
3523
|
+
console.log(
|
|
3524
|
+
`Fetching skill list @${ownerType}/${ownerName}/${listName}...
|
|
3525
|
+
`
|
|
3526
|
+
);
|
|
3527
|
+
const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
3528
|
+
const response = await fetchSkillList2(
|
|
3529
|
+
ownerType,
|
|
3530
|
+
ownerName,
|
|
3531
|
+
listName
|
|
3532
|
+
);
|
|
3533
|
+
if (response.status !== 200 || !response.data) {
|
|
3534
|
+
const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
|
|
3535
|
+
console.error(`Error: ${errorMsg}`);
|
|
3536
|
+
process.exit(1);
|
|
3537
|
+
}
|
|
3538
|
+
const list2 = response.data;
|
|
3539
|
+
console.log(
|
|
3540
|
+
`List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
|
|
3541
|
+
);
|
|
3542
|
+
console.log(`Skills: ${list2.items.length}
|
|
3543
|
+
`);
|
|
3544
|
+
const specifiers = [];
|
|
3545
|
+
for (const item of list2.items) {
|
|
3546
|
+
const ns = item.namespace === "org" ? "org" : "user";
|
|
3547
|
+
let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
|
|
3548
|
+
if (item.pinnedVersion) {
|
|
3549
|
+
spec += `@${item.pinnedVersion}`;
|
|
3550
|
+
}
|
|
3551
|
+
specifiers.push(spec);
|
|
3552
|
+
}
|
|
3553
|
+
return specifiers;
|
|
3554
|
+
}
|
|
3555
|
+
async function installFromLockfile(options) {
|
|
3556
|
+
try {
|
|
3557
|
+
const config2 = await resolveConfig();
|
|
3558
|
+
const registryUrl = config2.registryUrl;
|
|
3559
|
+
const apiKey = getTokenForRegistry(config2, registryUrl);
|
|
3560
|
+
const skillsDir = options.dir || getSkillsDir();
|
|
3561
|
+
const cacheDir = getCacheDir();
|
|
3562
|
+
await migrateLockfileIfNeeded();
|
|
3563
|
+
let lockfile = await readLockfile();
|
|
3564
|
+
const manifestDeps = await getDependencies();
|
|
3565
|
+
const manifestGitHubDeps = await getGitHubDependencies();
|
|
3566
|
+
const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
3567
|
+
const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
|
|
3568
|
+
const installedSkills = [];
|
|
3569
|
+
const missingDeps = [];
|
|
3570
|
+
for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
|
|
3571
|
+
if (!lockfilePackages[fullName]) {
|
|
3572
|
+
missingDeps.push({ fullName, versionRange });
|
|
3380
3573
|
}
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
console.error(
|
|
3385
|
-
` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
|
|
3386
|
-
);
|
|
3387
|
-
console.error(``);
|
|
3388
|
-
console.error(` Examples:`);
|
|
3389
|
-
console.error(` pspm access @user/myname/my-skill --public`);
|
|
3574
|
+
}
|
|
3575
|
+
if (missingDeps.length > 0) {
|
|
3576
|
+
if (options.frozenLockfile) {
|
|
3390
3577
|
console.error(
|
|
3391
|
-
|
|
3392
|
-
);
|
|
3393
|
-
process.exit(1);
|
|
3394
|
-
}
|
|
3395
|
-
packageName = parsed.name;
|
|
3396
|
-
packageUsername = parsed.owner;
|
|
3397
|
-
} else {
|
|
3398
|
-
const { readFile: readFile10 } = await import('fs/promises');
|
|
3399
|
-
const { join: join18 } = await import('path');
|
|
3400
|
-
let manifest = null;
|
|
3401
|
-
try {
|
|
3402
|
-
const content = await readFile10(
|
|
3403
|
-
join18(process.cwd(), "pspm.json"),
|
|
3404
|
-
"utf-8"
|
|
3578
|
+
"Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
3405
3579
|
);
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
const content = await readFile10(
|
|
3410
|
-
join18(process.cwd(), "package.json"),
|
|
3411
|
-
"utf-8"
|
|
3412
|
-
);
|
|
3413
|
-
manifest = JSON.parse(content);
|
|
3414
|
-
} catch {
|
|
3415
|
-
console.error(
|
|
3416
|
-
"Error: No pspm.json or package.json found in current directory"
|
|
3417
|
-
);
|
|
3418
|
-
console.error(
|
|
3419
|
-
"Either run this command in a package directory or specify a package name"
|
|
3420
|
-
);
|
|
3421
|
-
process.exit(1);
|
|
3580
|
+
console.error("Missing dependencies:");
|
|
3581
|
+
for (const dep of missingDeps) {
|
|
3582
|
+
console.error(` - ${dep.fullName}@${dep.versionRange}`);
|
|
3422
3583
|
}
|
|
3423
|
-
}
|
|
3424
|
-
if (!manifest?.name) {
|
|
3425
|
-
console.error("Error: Package manifest is missing 'name' field");
|
|
3426
3584
|
process.exit(1);
|
|
3427
3585
|
}
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
const
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3586
|
+
console.log(`Resolving ${missingDeps.length} new dependency(ies)...
|
|
3587
|
+
`);
|
|
3588
|
+
configure2({ registryUrl, apiKey });
|
|
3589
|
+
for (const { fullName, versionRange } of missingDeps) {
|
|
3590
|
+
const parsed = parseRegistrySpecifier2(fullName);
|
|
3591
|
+
if (!parsed) {
|
|
3592
|
+
console.error(`Error: Invalid dependency specifier: ${fullName}`);
|
|
3593
|
+
continue;
|
|
3594
|
+
}
|
|
3595
|
+
const { owner, name } = parsed;
|
|
3596
|
+
console.log(`Resolving ${fullName}@${versionRange}...`);
|
|
3597
|
+
const versionsResponse = await listSkillVersions(owner, name);
|
|
3598
|
+
if (versionsResponse.status !== 200) {
|
|
3599
|
+
const errorMessage = extractApiErrorMessage(
|
|
3600
|
+
versionsResponse,
|
|
3601
|
+
`Skill ${fullName} not found`
|
|
3602
|
+
);
|
|
3603
|
+
console.error(`Error: ${errorMessage}`);
|
|
3604
|
+
continue;
|
|
3605
|
+
}
|
|
3606
|
+
const versions = versionsResponse.data;
|
|
3607
|
+
if (versions.length === 0) {
|
|
3608
|
+
console.error(`Error: Skill ${fullName} not found`);
|
|
3609
|
+
continue;
|
|
3610
|
+
}
|
|
3611
|
+
const versionStrings = versions.map(
|
|
3612
|
+
(v) => v.version
|
|
3613
|
+
);
|
|
3614
|
+
const resolved = resolveVersion2(versionRange || "*", versionStrings);
|
|
3615
|
+
if (!resolved) {
|
|
3616
|
+
console.error(
|
|
3617
|
+
`Error: No version matching "${versionRange}" for ${fullName}`
|
|
3618
|
+
);
|
|
3619
|
+
continue;
|
|
3620
|
+
}
|
|
3621
|
+
const versionResponse = await getSkillVersion(owner, name, resolved);
|
|
3622
|
+
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
3623
|
+
const errorMessage = extractApiErrorMessage(
|
|
3624
|
+
versionResponse,
|
|
3625
|
+
`Version ${resolved} not found`
|
|
3626
|
+
);
|
|
3627
|
+
console.error(`Error: ${errorMessage}`);
|
|
3628
|
+
continue;
|
|
3629
|
+
}
|
|
3630
|
+
const versionInfo = versionResponse.data;
|
|
3631
|
+
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
3632
|
+
const downloadHeaders = {};
|
|
3633
|
+
if (!isPresignedUrl && apiKey) {
|
|
3634
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
3635
|
+
}
|
|
3636
|
+
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
3637
|
+
headers: downloadHeaders,
|
|
3638
|
+
redirect: "follow"
|
|
3639
|
+
});
|
|
3640
|
+
if (!tarballResponse.ok) {
|
|
3641
|
+
console.error(
|
|
3642
|
+
`Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
|
|
3643
|
+
);
|
|
3644
|
+
continue;
|
|
3645
|
+
}
|
|
3646
|
+
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
3647
|
+
const integrity = calculateIntegrity(tarballBuffer);
|
|
3648
|
+
const lockfileEntry = {
|
|
3649
|
+
version: resolved,
|
|
3650
|
+
resolved: versionInfo.downloadUrl,
|
|
3651
|
+
integrity
|
|
3652
|
+
};
|
|
3653
|
+
if (versionInfo.manifest?.encryption) {
|
|
3654
|
+
lockfileEntry.encryption = versionInfo.manifest.encryption;
|
|
3655
|
+
}
|
|
3656
|
+
await addToLockfile(fullName, lockfileEntry);
|
|
3657
|
+
await writeToCache(cacheDir, integrity, tarballBuffer);
|
|
3658
|
+
console.log(` Resolved ${fullName}@${resolved}`);
|
|
3659
|
+
}
|
|
3660
|
+
lockfile = await readLockfile();
|
|
3661
|
+
}
|
|
3662
|
+
const missingGitHubDeps = [];
|
|
3663
|
+
for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
|
|
3664
|
+
if (!lockfileGitHubPackages[specifier]) {
|
|
3665
|
+
missingGitHubDeps.push({ specifier, ref });
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
if (missingGitHubDeps.length > 0) {
|
|
3669
|
+
if (options.frozenLockfile) {
|
|
3670
|
+
console.error(
|
|
3671
|
+
"Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
3672
|
+
);
|
|
3673
|
+
console.error("Missing GitHub dependencies:");
|
|
3674
|
+
for (const dep of missingGitHubDeps) {
|
|
3675
|
+
console.error(` - ${dep.specifier}@${dep.ref}`);
|
|
3676
|
+
}
|
|
3677
|
+
process.exit(1);
|
|
3678
|
+
}
|
|
3679
|
+
console.log(
|
|
3680
|
+
`
|
|
3681
|
+
Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
|
|
3682
|
+
`
|
|
3683
|
+
);
|
|
3684
|
+
for (const { specifier, ref } of missingGitHubDeps) {
|
|
3685
|
+
const parsed = parseGitHubSpecifier2(specifier);
|
|
3686
|
+
if (!parsed) {
|
|
3687
|
+
console.error(`Error: Invalid GitHub specifier: ${specifier}`);
|
|
3688
|
+
continue;
|
|
3689
|
+
}
|
|
3690
|
+
parsed.ref = parsed.ref || ref;
|
|
3691
|
+
console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
|
|
3692
|
+
try {
|
|
3693
|
+
const result = await downloadGitHubPackage(parsed);
|
|
3694
|
+
await extractGitHubPackage(parsed, result.buffer, skillsDir);
|
|
3695
|
+
const entry = {
|
|
3696
|
+
version: result.commit.slice(0, 7),
|
|
3697
|
+
resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
|
|
3698
|
+
integrity: result.integrity,
|
|
3699
|
+
gitCommit: result.commit,
|
|
3700
|
+
gitRef: ref || "HEAD"
|
|
3701
|
+
};
|
|
3702
|
+
await addGitHubToLockfile(specifier, entry);
|
|
3703
|
+
await writeToCache(cacheDir, result.integrity, result.buffer);
|
|
3704
|
+
console.log(
|
|
3705
|
+
` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
|
|
3706
|
+
);
|
|
3707
|
+
} catch (error) {
|
|
3708
|
+
if (error instanceof GitHubRateLimitError) {
|
|
3709
|
+
console.error(`Error: ${error.message}`);
|
|
3710
|
+
} else if (error instanceof GitHubPathNotFoundError) {
|
|
3711
|
+
console.error(`Error: ${error.message}`);
|
|
3712
|
+
} else if (error instanceof GitHubNotFoundError) {
|
|
3713
|
+
console.error(`Error: ${error.message}`);
|
|
3714
|
+
} else {
|
|
3715
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3716
|
+
console.error(`Error resolving ${specifier}: ${message}`);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
lockfile = await readLockfile();
|
|
3721
|
+
}
|
|
3722
|
+
const manifest = await readManifest();
|
|
3723
|
+
const agentConfigs = manifest?.agents;
|
|
3724
|
+
let agents;
|
|
3725
|
+
if (options.agent) {
|
|
3726
|
+
agents = parseAgentArg(options.agent);
|
|
3727
|
+
} else if (manifest) {
|
|
3728
|
+
agents = parseAgentArg(void 0);
|
|
3729
|
+
} else if (options.yes) {
|
|
3730
|
+
agents = parseAgentArg(void 0);
|
|
3731
|
+
} else {
|
|
3732
|
+
console.log("\nNo pspm.json found. Let's set up your project.\n");
|
|
3733
|
+
agents = await promptForAgents();
|
|
3734
|
+
console.log();
|
|
3735
|
+
}
|
|
3736
|
+
const packages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
3737
|
+
const packageCount = Object.keys(packages).length;
|
|
3738
|
+
if (packageCount > 0) {
|
|
3739
|
+
console.log(`
|
|
3740
|
+
Installing ${packageCount} registry skill(s)...
|
|
3741
|
+
`);
|
|
3742
|
+
const installOrder = computeInstallOrder(packages);
|
|
3743
|
+
const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
|
|
3744
|
+
for (const [fullName, entry] of entries) {
|
|
3745
|
+
const parsedName = parseRegistrySpecifier2(fullName);
|
|
3746
|
+
if (!parsedName) {
|
|
3747
|
+
console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
|
|
3748
|
+
continue;
|
|
3749
|
+
}
|
|
3750
|
+
const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
|
|
3751
|
+
console.log(`Installing ${fullName}@${entry.version}...`);
|
|
3752
|
+
let tarballBuffer;
|
|
3753
|
+
let fromCache = false;
|
|
3754
|
+
const cachedTarball = await readFromCache(cacheDir, entry.integrity);
|
|
3755
|
+
if (cachedTarball) {
|
|
3756
|
+
tarballBuffer = cachedTarball;
|
|
3757
|
+
fromCache = true;
|
|
3758
|
+
} else {
|
|
3759
|
+
const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
|
|
3760
|
+
const downloadHeaders = {};
|
|
3761
|
+
if (!isPresignedUrl && apiKey) {
|
|
3762
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
3763
|
+
}
|
|
3764
|
+
const response = await fetch(entry.resolved, {
|
|
3765
|
+
headers: downloadHeaders,
|
|
3766
|
+
redirect: "follow"
|
|
3767
|
+
});
|
|
3768
|
+
if (!response.ok) {
|
|
3769
|
+
if (response.status === 401) {
|
|
3770
|
+
if (!apiKey) {
|
|
3771
|
+
console.error(
|
|
3772
|
+
` Error: ${fullName} requires authentication. Run 'pspm login' first.`
|
|
3773
|
+
);
|
|
3774
|
+
} else {
|
|
3775
|
+
console.error(
|
|
3776
|
+
` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
|
|
3777
|
+
);
|
|
3778
|
+
}
|
|
3779
|
+
} else {
|
|
3780
|
+
console.error(
|
|
3781
|
+
` Error: Failed to download ${fullName} (${response.status})`
|
|
3782
|
+
);
|
|
3783
|
+
}
|
|
3784
|
+
continue;
|
|
3785
|
+
}
|
|
3786
|
+
tarballBuffer = Buffer.from(await response.arrayBuffer());
|
|
3787
|
+
const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
|
|
3788
|
+
if (actualIntegrity !== entry.integrity) {
|
|
3789
|
+
console.error(
|
|
3790
|
+
` Error: Checksum verification failed for ${fullName}`
|
|
3791
|
+
);
|
|
3792
|
+
if (options.frozenLockfile) {
|
|
3793
|
+
process.exit(1);
|
|
3794
|
+
}
|
|
3795
|
+
continue;
|
|
3796
|
+
}
|
|
3797
|
+
await writeToCache(cacheDir, entry.integrity, tarballBuffer);
|
|
3798
|
+
}
|
|
3799
|
+
if (entry.encryption) {
|
|
3800
|
+
const scope = entry.encryption.scope;
|
|
3801
|
+
const encKey = await getEncryptionKey(scope);
|
|
3802
|
+
if (!encKey) {
|
|
3803
|
+
console.error(
|
|
3804
|
+
` Error: Package ${fullName} is encrypted. Set the key: pspm config set-encryption-key ${scope} <passphrase>`
|
|
3805
|
+
);
|
|
3806
|
+
continue;
|
|
3807
|
+
}
|
|
3808
|
+
try {
|
|
3809
|
+
tarballBuffer = decryptBuffer(
|
|
3810
|
+
tarballBuffer,
|
|
3811
|
+
encKey,
|
|
3812
|
+
entry.encryption
|
|
3813
|
+
);
|
|
3814
|
+
} catch {
|
|
3815
|
+
console.error(
|
|
3816
|
+
` Error: Failed to decrypt ${fullName}. Wrong encryption key for scope "${scope}".`
|
|
3817
|
+
);
|
|
3818
|
+
continue;
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
const effectiveSkillName = subname ?? name;
|
|
3822
|
+
let destDir;
|
|
3823
|
+
if (ns === "org") {
|
|
3824
|
+
destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
|
|
3825
|
+
} else if (ns === "github" && subname) {
|
|
3826
|
+
destDir = join(
|
|
3827
|
+
skillsDir,
|
|
3828
|
+
"_github-registry",
|
|
3829
|
+
pkgOwner,
|
|
3830
|
+
name,
|
|
3831
|
+
subname
|
|
3832
|
+
);
|
|
3833
|
+
} else {
|
|
3834
|
+
destDir = join(skillsDir, pkgOwner, effectiveSkillName);
|
|
3835
|
+
}
|
|
3836
|
+
await rm(destDir, { recursive: true, force: true });
|
|
3837
|
+
await mkdir(destDir, { recursive: true });
|
|
3838
|
+
const tempFile = join(destDir, ".temp.tgz");
|
|
3839
|
+
await writeFile(tempFile, tarballBuffer);
|
|
3840
|
+
const { exec: exec2 } = await import('child_process');
|
|
3841
|
+
const { promisify: promisify2 } = await import('util');
|
|
3842
|
+
const execAsync = promisify2(exec2);
|
|
3843
|
+
try {
|
|
3844
|
+
await execAsync(
|
|
3845
|
+
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
3846
|
+
);
|
|
3847
|
+
} finally {
|
|
3848
|
+
await rm(tempFile, { force: true });
|
|
3849
|
+
}
|
|
3850
|
+
console.log(
|
|
3851
|
+
` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
|
|
3852
|
+
);
|
|
3853
|
+
const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
|
|
3854
|
+
installedSkills.push({
|
|
3855
|
+
name: effectiveSkillName,
|
|
3856
|
+
sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
|
|
3857
|
+
});
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
const githubPackages = lockfile?.githubPackages ?? {};
|
|
3861
|
+
const githubCount = Object.keys(githubPackages).length;
|
|
3862
|
+
if (githubCount > 0) {
|
|
3863
|
+
console.log(`
|
|
3864
|
+
Installing ${githubCount} GitHub skill(s)...
|
|
3865
|
+
`);
|
|
3866
|
+
for (const [specifier, entry] of Object.entries(githubPackages)) {
|
|
3867
|
+
const parsed = parseGitHubSpecifier2(specifier);
|
|
3868
|
+
if (!parsed) {
|
|
3869
|
+
console.warn(
|
|
3870
|
+
`Warning: Invalid GitHub specifier in lockfile: ${specifier}`
|
|
3871
|
+
);
|
|
3872
|
+
continue;
|
|
3873
|
+
}
|
|
3874
|
+
const ghEntry = entry;
|
|
3875
|
+
console.log(
|
|
3876
|
+
`Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
|
|
3877
|
+
);
|
|
3878
|
+
let tarballBuffer;
|
|
3879
|
+
let fromCache = false;
|
|
3880
|
+
const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
|
|
3881
|
+
if (cachedTarball) {
|
|
3882
|
+
tarballBuffer = cachedTarball;
|
|
3883
|
+
fromCache = true;
|
|
3884
|
+
} else {
|
|
3885
|
+
try {
|
|
3886
|
+
const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
|
|
3887
|
+
const result = await downloadGitHubPackage(specWithCommit);
|
|
3888
|
+
tarballBuffer = result.buffer;
|
|
3889
|
+
if (result.integrity !== ghEntry.integrity) {
|
|
3890
|
+
console.error(
|
|
3891
|
+
` Error: Checksum verification failed for ${specifier}`
|
|
3892
|
+
);
|
|
3893
|
+
if (options.frozenLockfile) {
|
|
3894
|
+
process.exit(1);
|
|
3895
|
+
}
|
|
3896
|
+
continue;
|
|
3897
|
+
}
|
|
3898
|
+
await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
|
|
3899
|
+
} catch (error) {
|
|
3900
|
+
if (error instanceof GitHubRateLimitError) {
|
|
3901
|
+
console.error(` Error: ${error.message}`);
|
|
3902
|
+
} else if (error instanceof GitHubPathNotFoundError) {
|
|
3903
|
+
console.error(` Error: ${error.message}`);
|
|
3904
|
+
} else if (error instanceof GitHubNotFoundError) {
|
|
3905
|
+
console.error(` Error: ${error.message}`);
|
|
3906
|
+
} else {
|
|
3907
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3908
|
+
console.error(` Error downloading ${specifier}: ${message}`);
|
|
3909
|
+
}
|
|
3910
|
+
continue;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
try {
|
|
3914
|
+
const destPath = await extractGitHubPackage(
|
|
3915
|
+
parsed,
|
|
3916
|
+
tarballBuffer,
|
|
3917
|
+
skillsDir
|
|
3918
|
+
);
|
|
3919
|
+
console.log(
|
|
3920
|
+
` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
|
|
3921
|
+
);
|
|
3922
|
+
const skillName = getGitHubSkillName2(parsed);
|
|
3923
|
+
installedSkills.push({
|
|
3924
|
+
name: skillName,
|
|
3925
|
+
sourcePath: getGitHubSkillPath(
|
|
3926
|
+
parsed.owner,
|
|
3927
|
+
parsed.repo,
|
|
3928
|
+
parsed.path
|
|
3929
|
+
)
|
|
3930
|
+
});
|
|
3931
|
+
} catch (error) {
|
|
3932
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3933
|
+
console.error(` Error extracting ${specifier}: ${message}`);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
if (installedSkills.length > 0 && agents[0] !== "none") {
|
|
3938
|
+
const globalMode = isGlobalMode();
|
|
3939
|
+
console.log(
|
|
3940
|
+
`
|
|
3941
|
+
Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
|
|
3942
|
+
);
|
|
3943
|
+
await createAgentSymlinks(installedSkills, {
|
|
3944
|
+
agents,
|
|
3945
|
+
projectRoot: globalMode ? homedir() : process.cwd(),
|
|
3946
|
+
agentConfigs,
|
|
3947
|
+
global: globalMode
|
|
3948
|
+
});
|
|
3949
|
+
console.log(" Symlinks created.");
|
|
3950
|
+
}
|
|
3951
|
+
const totalCount = packageCount + githubCount;
|
|
3952
|
+
if (totalCount === 0) {
|
|
3953
|
+
console.log("No skills to install.");
|
|
3954
|
+
} else {
|
|
3955
|
+
console.log(`
|
|
3956
|
+
All ${totalCount} skill(s) installed.`);
|
|
3957
|
+
}
|
|
3958
|
+
} catch (error) {
|
|
3959
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3960
|
+
console.error(`Error: ${message}`);
|
|
3961
|
+
process.exit(1);
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
var init_install = __esm({
|
|
3965
|
+
"src/commands/install.ts"() {
|
|
3966
|
+
init_agents();
|
|
3967
|
+
init_api_client();
|
|
3968
|
+
init_config();
|
|
3969
|
+
init_errors();
|
|
3970
|
+
init_github();
|
|
3971
|
+
init_encryption();
|
|
3972
|
+
init_lib();
|
|
3973
|
+
init_lockfile3();
|
|
3974
|
+
init_manifest3();
|
|
3975
|
+
init_symlinks();
|
|
3976
|
+
}
|
|
3977
|
+
});
|
|
3978
|
+
|
|
3979
|
+
// src/commands/access.ts
|
|
3980
|
+
init_api_client();
|
|
3981
|
+
init_config();
|
|
3982
|
+
init_lib();
|
|
3983
|
+
function isLocalSpecifier2(specifier) {
|
|
3984
|
+
return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
|
|
3985
|
+
}
|
|
3986
|
+
async function access(specifier, options) {
|
|
3987
|
+
try {
|
|
3988
|
+
const apiKey = await requireApiKey();
|
|
3989
|
+
const registryUrl = await getRegistryUrl();
|
|
3990
|
+
if (options.public && options.private) {
|
|
3991
|
+
console.error("Error: Cannot specify both --public and --private");
|
|
3992
|
+
process.exit(1);
|
|
3993
|
+
}
|
|
3994
|
+
if (!options.public && !options.private) {
|
|
3995
|
+
console.error("Error: Must specify either --public or --private");
|
|
3996
|
+
process.exit(1);
|
|
3997
|
+
}
|
|
3998
|
+
const visibility = options.public ? "public" : "private";
|
|
3999
|
+
let packageName;
|
|
4000
|
+
let packageUsername;
|
|
4001
|
+
if (specifier) {
|
|
4002
|
+
if (isGitHubSpecifier2(specifier)) {
|
|
4003
|
+
const ghSpec = parseGitHubSpecifier2(specifier);
|
|
4004
|
+
if (ghSpec) {
|
|
4005
|
+
console.error(`Error: Cannot change visibility of GitHub packages.`);
|
|
4006
|
+
console.error(
|
|
4007
|
+
` "${specifier}" is hosted on GitHub, not the PSPM registry.`
|
|
4008
|
+
);
|
|
4009
|
+
console.error(
|
|
4010
|
+
` Visibility can only be changed for packages published to the registry.`
|
|
4011
|
+
);
|
|
4012
|
+
} else {
|
|
4013
|
+
console.error(`Error: Invalid GitHub specifier "${specifier}".`);
|
|
4014
|
+
console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
|
|
4015
|
+
}
|
|
4016
|
+
process.exit(1);
|
|
4017
|
+
}
|
|
4018
|
+
if (isLocalSpecifier2(specifier)) {
|
|
4019
|
+
console.error(`Error: Cannot change visibility of local packages.`);
|
|
4020
|
+
console.error(
|
|
4021
|
+
` "${specifier}" is a local directory, not a registry package.`
|
|
4022
|
+
);
|
|
4023
|
+
console.error(
|
|
4024
|
+
` Visibility can only be changed for packages published to the registry.`
|
|
4025
|
+
);
|
|
4026
|
+
process.exit(1);
|
|
4027
|
+
}
|
|
4028
|
+
const parsed = parseRegistrySpecifier2(specifier);
|
|
4029
|
+
if (!parsed) {
|
|
4030
|
+
console.error(`Error: Invalid package specifier "${specifier}".`);
|
|
4031
|
+
console.error(
|
|
4032
|
+
` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
|
|
4033
|
+
);
|
|
4034
|
+
console.error(``);
|
|
4035
|
+
console.error(` Examples:`);
|
|
4036
|
+
console.error(` pspm access @user/myname/my-skill --public`);
|
|
4037
|
+
console.error(
|
|
4038
|
+
` pspm access --public (uses current directory's pspm.json)`
|
|
4039
|
+
);
|
|
4040
|
+
process.exit(1);
|
|
4041
|
+
}
|
|
4042
|
+
packageName = parsed.name;
|
|
4043
|
+
packageUsername = parsed.owner;
|
|
4044
|
+
} else {
|
|
4045
|
+
const { readFile: readFile10 } = await import('fs/promises');
|
|
4046
|
+
const { join: join18 } = await import('path');
|
|
4047
|
+
let manifest = null;
|
|
4048
|
+
try {
|
|
4049
|
+
const content = await readFile10(
|
|
4050
|
+
join18(process.cwd(), "pspm.json"),
|
|
4051
|
+
"utf-8"
|
|
4052
|
+
);
|
|
4053
|
+
manifest = JSON.parse(content);
|
|
4054
|
+
} catch {
|
|
4055
|
+
try {
|
|
4056
|
+
const content = await readFile10(
|
|
4057
|
+
join18(process.cwd(), "package.json"),
|
|
4058
|
+
"utf-8"
|
|
4059
|
+
);
|
|
4060
|
+
manifest = JSON.parse(content);
|
|
4061
|
+
} catch {
|
|
4062
|
+
console.error(
|
|
4063
|
+
"Error: No pspm.json or package.json found in current directory"
|
|
4064
|
+
);
|
|
4065
|
+
console.error(
|
|
4066
|
+
"Either run this command in a package directory or specify a package name"
|
|
4067
|
+
);
|
|
4068
|
+
process.exit(1);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
if (!manifest?.name) {
|
|
4072
|
+
console.error("Error: Package manifest is missing 'name' field");
|
|
4073
|
+
process.exit(1);
|
|
4074
|
+
}
|
|
4075
|
+
packageName = manifest.name;
|
|
4076
|
+
}
|
|
4077
|
+
if (!packageUsername) {
|
|
4078
|
+
const config2 = await resolveConfig();
|
|
4079
|
+
packageUsername = config2.username;
|
|
4080
|
+
}
|
|
4081
|
+
if (!packageUsername) {
|
|
4082
|
+
console.error(
|
|
3436
4083
|
"Error: Could not determine username. Please use the full specifier: @user/{username}/{name}"
|
|
3437
4084
|
);
|
|
3438
4085
|
process.exit(1);
|
|
@@ -3497,7 +4144,6 @@ async function audit(options) {
|
|
|
3497
4144
|
}
|
|
3498
4145
|
const issues = [];
|
|
3499
4146
|
const skillsDir = getSkillsDir();
|
|
3500
|
-
const projectRoot = process.cwd();
|
|
3501
4147
|
if (!options.json) {
|
|
3502
4148
|
console.log("Auditing installed skills...\n");
|
|
3503
4149
|
}
|
|
@@ -3506,7 +4152,7 @@ async function audit(options) {
|
|
|
3506
4152
|
const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
3507
4153
|
if (!match) continue;
|
|
3508
4154
|
const [, username, skillName] = match;
|
|
3509
|
-
const destDir = join(
|
|
4155
|
+
const destDir = join(skillsDir, username, skillName);
|
|
3510
4156
|
const exists = await pathExists(destDir);
|
|
3511
4157
|
if (!exists) {
|
|
3512
4158
|
issues.push({
|
|
@@ -3542,14 +4188,7 @@ async function audit(options) {
|
|
|
3542
4188
|
for (const { specifier } of githubSkills) {
|
|
3543
4189
|
const parsed = parseGitHubSpecifier2(specifier);
|
|
3544
4190
|
if (!parsed) continue;
|
|
3545
|
-
const destDir = parsed.path ? join(
|
|
3546
|
-
projectRoot,
|
|
3547
|
-
skillsDir,
|
|
3548
|
-
"_github",
|
|
3549
|
-
parsed.owner,
|
|
3550
|
-
parsed.repo,
|
|
3551
|
-
parsed.path
|
|
3552
|
-
) : join(projectRoot, skillsDir, "_github", parsed.owner, parsed.repo);
|
|
4191
|
+
const destDir = parsed.path ? join(skillsDir, "_github", parsed.owner, parsed.repo, parsed.path) : join(skillsDir, "_github", parsed.owner, parsed.repo);
|
|
3553
4192
|
const exists = await pathExists(destDir);
|
|
3554
4193
|
if (!exists) {
|
|
3555
4194
|
issues.push({
|
|
@@ -3576,7 +4215,6 @@ async function audit(options) {
|
|
|
3576
4215
|
for (const { specifier, entry } of wellKnownSkills) {
|
|
3577
4216
|
const wkEntry = entry;
|
|
3578
4217
|
const destDir = join(
|
|
3579
|
-
projectRoot,
|
|
3580
4218
|
skillsDir,
|
|
3581
4219
|
"_wellknown",
|
|
3582
4220
|
wkEntry.hostname,
|
|
@@ -3644,376 +4282,85 @@ async function audit(options) {
|
|
|
3644
4282
|
console.log(
|
|
3645
4283
|
` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
|
|
3646
4284
|
);
|
|
3647
|
-
console.log(` ${issue.message}`);
|
|
3648
|
-
}
|
|
3649
|
-
console.log();
|
|
3650
|
-
}
|
|
3651
|
-
const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
|
|
3652
|
-
console.log(
|
|
3653
|
-
`Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
|
|
3654
|
-
);
|
|
3655
|
-
if (errorCount > 0) {
|
|
3656
|
-
process.exit(1);
|
|
3657
|
-
}
|
|
3658
|
-
} catch (error) {
|
|
3659
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3660
|
-
console.error(`Error: ${message}`);
|
|
3661
|
-
process.exit(1);
|
|
3662
|
-
}
|
|
3663
|
-
}
|
|
3664
|
-
async function pathExists(path) {
|
|
3665
|
-
try {
|
|
3666
|
-
await stat(path);
|
|
3667
|
-
return true;
|
|
3668
|
-
} catch {
|
|
3669
|
-
return false;
|
|
3670
|
-
}
|
|
3671
|
-
}
|
|
3672
|
-
async function configInit(options) {
|
|
3673
|
-
try {
|
|
3674
|
-
const configPath = join(process.cwd(), ".pspmrc");
|
|
3675
|
-
try {
|
|
3676
|
-
await stat(configPath);
|
|
3677
|
-
console.error("Error: .pspmrc already exists in this directory.");
|
|
3678
|
-
process.exit(1);
|
|
3679
|
-
} catch {
|
|
3680
|
-
}
|
|
3681
|
-
const lines = ["; Project-specific PSPM configuration", ""];
|
|
3682
|
-
if (options.registry) {
|
|
3683
|
-
lines.push(`registry = ${options.registry}`);
|
|
3684
|
-
} else {
|
|
3685
|
-
lines.push("; Uncomment to use a custom registry:");
|
|
3686
|
-
lines.push("; registry = https://custom-registry.example.com");
|
|
3687
|
-
}
|
|
3688
|
-
lines.push("");
|
|
3689
|
-
await writeFile(configPath, lines.join("\n"));
|
|
3690
|
-
console.log("Created .pspmrc");
|
|
3691
|
-
console.log("");
|
|
3692
|
-
console.log("Contents:");
|
|
3693
|
-
console.log(lines.join("\n"));
|
|
3694
|
-
console.log("Note: .pspmrc should be committed to version control.");
|
|
3695
|
-
console.log("API keys should NOT be stored here - use pspm login instead.");
|
|
3696
|
-
} catch (error) {
|
|
3697
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3698
|
-
console.error(`Error: ${message}`);
|
|
3699
|
-
process.exit(1);
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
|
|
3703
|
-
// src/commands/config/show.ts
|
|
3704
|
-
init_config();
|
|
3705
|
-
async function configShow() {
|
|
3706
|
-
try {
|
|
3707
|
-
const resolved = await resolveConfig();
|
|
3708
|
-
const projectConfig = await findProjectConfig();
|
|
3709
|
-
const configPath = getConfigPath();
|
|
3710
|
-
console.log("Resolved Configuration:\n");
|
|
3711
|
-
console.log(` Registry URL: ${resolved.registryUrl}`);
|
|
3712
|
-
console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
|
|
3713
|
-
console.log(` Username: ${resolved.username || "(not set)"}`);
|
|
3714
|
-
console.log("");
|
|
3715
|
-
console.log("Config Locations:");
|
|
3716
|
-
console.log(` User config: ${configPath}`);
|
|
3717
|
-
console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
|
|
3718
|
-
console.log("");
|
|
3719
|
-
console.log("Environment Variables:");
|
|
3720
|
-
console.log(
|
|
3721
|
-
` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
|
|
3722
|
-
);
|
|
3723
|
-
console.log(
|
|
3724
|
-
` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
|
|
3725
|
-
);
|
|
3726
|
-
} catch (error) {
|
|
3727
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3728
|
-
console.error(`Error: ${message}`);
|
|
3729
|
-
process.exit(1);
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
|
|
3733
|
-
// src/commands/deprecate.ts
|
|
3734
|
-
init_api_client();
|
|
3735
|
-
init_config();
|
|
3736
|
-
init_lib();
|
|
3737
|
-
async function deprecate(specifier, message, options) {
|
|
3738
|
-
try {
|
|
3739
|
-
const apiKey = await requireApiKey();
|
|
3740
|
-
const registryUrl = await getRegistryUrl();
|
|
3741
|
-
const parsed = parseRegistrySpecifier2(specifier);
|
|
3742
|
-
if (!parsed) {
|
|
3743
|
-
console.error(
|
|
3744
|
-
`Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
|
|
3745
|
-
);
|
|
3746
|
-
process.exit(1);
|
|
3747
|
-
}
|
|
3748
|
-
const { owner, name, versionRange } = parsed;
|
|
3749
|
-
const fullName = generateRegistryIdentifier2({
|
|
3750
|
-
namespace: parsed.namespace,
|
|
3751
|
-
owner,
|
|
3752
|
-
name
|
|
3753
|
-
});
|
|
3754
|
-
if (!versionRange) {
|
|
3755
|
-
console.error(
|
|
3756
|
-
"Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
|
|
3757
|
-
);
|
|
3758
|
-
process.exit(1);
|
|
3759
|
-
}
|
|
3760
|
-
configure2({ registryUrl, apiKey });
|
|
3761
|
-
if (options.undo) {
|
|
3762
|
-
console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
|
|
3763
|
-
const response = await undeprecateSkillVersion(owner, name, versionRange);
|
|
3764
|
-
if (response.status !== 200) {
|
|
3765
|
-
console.error(
|
|
3766
|
-
`Error: ${response.error || "Failed to remove deprecation"}`
|
|
3767
|
-
);
|
|
3768
|
-
process.exit(1);
|
|
3769
|
-
}
|
|
3770
|
-
console.log(`Removed deprecation from ${fullName}@${versionRange}`);
|
|
3771
|
-
} else {
|
|
3772
|
-
if (!message) {
|
|
3773
|
-
console.error(
|
|
3774
|
-
"Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
|
|
3775
|
-
);
|
|
3776
|
-
process.exit(1);
|
|
3777
|
-
}
|
|
3778
|
-
console.log(`Deprecating ${fullName}@${versionRange}...`);
|
|
3779
|
-
const response = await deprecateSkillVersion(
|
|
3780
|
-
owner,
|
|
3781
|
-
name,
|
|
3782
|
-
versionRange,
|
|
3783
|
-
message
|
|
3784
|
-
);
|
|
3785
|
-
if (response.status !== 200) {
|
|
3786
|
-
console.error(
|
|
3787
|
-
`Error: ${response.error || "Failed to deprecate version"}`
|
|
3788
|
-
);
|
|
3789
|
-
process.exit(1);
|
|
4285
|
+
console.log(` ${issue.message}`);
|
|
3790
4286
|
}
|
|
3791
|
-
console.log(
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
4287
|
+
console.log();
|
|
4288
|
+
}
|
|
4289
|
+
const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
|
|
4290
|
+
console.log(
|
|
4291
|
+
`Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
|
|
4292
|
+
);
|
|
4293
|
+
if (errorCount > 0) {
|
|
4294
|
+
process.exit(1);
|
|
3798
4295
|
}
|
|
3799
4296
|
} catch (error) {
|
|
3800
|
-
const
|
|
3801
|
-
console.error(`Error: ${
|
|
4297
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4298
|
+
console.error(`Error: ${message}`);
|
|
3802
4299
|
process.exit(1);
|
|
3803
4300
|
}
|
|
3804
4301
|
}
|
|
3805
|
-
|
|
3806
|
-
// src/commands/init.ts
|
|
3807
|
-
init_lib();
|
|
3808
|
-
function prompt(rl, question, defaultValue) {
|
|
3809
|
-
return new Promise((resolve2) => {
|
|
3810
|
-
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
3811
|
-
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
3812
|
-
resolve2(answer.trim() || defaultValue);
|
|
3813
|
-
});
|
|
3814
|
-
});
|
|
3815
|
-
}
|
|
3816
|
-
async function readExistingPackageJson() {
|
|
3817
|
-
try {
|
|
3818
|
-
const content = await readFile(
|
|
3819
|
-
join(process.cwd(), "package.json"),
|
|
3820
|
-
"utf-8"
|
|
3821
|
-
);
|
|
3822
|
-
const pkg = JSON.parse(content);
|
|
3823
|
-
return {
|
|
3824
|
-
name: pkg.name,
|
|
3825
|
-
version: pkg.version,
|
|
3826
|
-
description: pkg.description,
|
|
3827
|
-
author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
|
|
3828
|
-
license: pkg.license
|
|
3829
|
-
};
|
|
3830
|
-
} catch {
|
|
3831
|
-
return null;
|
|
3832
|
-
}
|
|
3833
|
-
}
|
|
3834
|
-
async function getGitAuthor() {
|
|
4302
|
+
async function pathExists(path) {
|
|
3835
4303
|
try {
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
const execAsync = promisify2(exec2);
|
|
3839
|
-
const [nameResult, emailResult] = await Promise.all([
|
|
3840
|
-
execAsync("git config user.name").catch(() => ({ stdout: "" })),
|
|
3841
|
-
execAsync("git config user.email").catch(() => ({ stdout: "" }))
|
|
3842
|
-
]);
|
|
3843
|
-
const name = nameResult.stdout.trim();
|
|
3844
|
-
const email = emailResult.stdout.trim();
|
|
3845
|
-
if (name && email) {
|
|
3846
|
-
return `${name} <${email}>`;
|
|
3847
|
-
}
|
|
3848
|
-
if (name) {
|
|
3849
|
-
return name;
|
|
3850
|
-
}
|
|
3851
|
-
return null;
|
|
4304
|
+
await stat(path);
|
|
4305
|
+
return true;
|
|
3852
4306
|
} catch {
|
|
3853
|
-
return
|
|
4307
|
+
return false;
|
|
3854
4308
|
}
|
|
3855
4309
|
}
|
|
3856
|
-
function
|
|
3857
|
-
const withoutScope = name.replace(/^@[^/]+\//, "");
|
|
3858
|
-
return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
3859
|
-
}
|
|
3860
|
-
function isValidName(name) {
|
|
3861
|
-
return /^[a-z][a-z0-9_-]*$/.test(name);
|
|
3862
|
-
}
|
|
3863
|
-
function isValidVersion(version3) {
|
|
3864
|
-
return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
|
|
3865
|
-
}
|
|
3866
|
-
async function init(options) {
|
|
4310
|
+
async function configInit(options) {
|
|
3867
4311
|
try {
|
|
3868
|
-
const
|
|
3869
|
-
let exists = false;
|
|
4312
|
+
const configPath = join(process.cwd(), ".pspmrc");
|
|
3870
4313
|
try {
|
|
3871
|
-
await stat(
|
|
3872
|
-
exists
|
|
3873
|
-
} catch {
|
|
3874
|
-
}
|
|
3875
|
-
if (exists && !options.force) {
|
|
3876
|
-
console.error("Error: pspm.json already exists in this directory.");
|
|
3877
|
-
console.error("Use --force to overwrite.");
|
|
4314
|
+
await stat(configPath);
|
|
4315
|
+
console.error("Error: .pspmrc already exists in this directory.");
|
|
3878
4316
|
process.exit(1);
|
|
4317
|
+
} catch {
|
|
3879
4318
|
}
|
|
3880
|
-
const
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
options.name || existingPkg?.name || basename(process.cwd())
|
|
3884
|
-
);
|
|
3885
|
-
const defaultVersion = existingPkg?.version || "0.1.0";
|
|
3886
|
-
const defaultDescription = options.description || existingPkg?.description || "";
|
|
3887
|
-
const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
|
|
3888
|
-
const defaultLicense = existingPkg?.license || "MIT";
|
|
3889
|
-
const defaultMain = "SKILL.md";
|
|
3890
|
-
const defaultCapabilities = "";
|
|
3891
|
-
let manifest;
|
|
3892
|
-
if (options.yes) {
|
|
3893
|
-
manifest = {
|
|
3894
|
-
$schema: PSPM_SCHEMA_URL,
|
|
3895
|
-
name: defaultName,
|
|
3896
|
-
version: defaultVersion,
|
|
3897
|
-
description: defaultDescription || void 0,
|
|
3898
|
-
author: defaultAuthor || void 0,
|
|
3899
|
-
license: defaultLicense,
|
|
3900
|
-
type: "skill",
|
|
3901
|
-
capabilities: [],
|
|
3902
|
-
main: defaultMain,
|
|
3903
|
-
requirements: {
|
|
3904
|
-
pspm: ">=0.1.0"
|
|
3905
|
-
},
|
|
3906
|
-
files: [...DEFAULT_SKILL_FILES],
|
|
3907
|
-
dependencies: {},
|
|
3908
|
-
private: false
|
|
3909
|
-
};
|
|
4319
|
+
const lines = ["; Project-specific PSPM configuration", ""];
|
|
4320
|
+
if (options.registry) {
|
|
4321
|
+
lines.push(`registry = ${options.registry}`);
|
|
3910
4322
|
} else {
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
);
|
|
3914
|
-
console.log(
|
|
3915
|
-
"It only covers the most common items, and tries to guess sensible defaults."
|
|
3916
|
-
);
|
|
3917
|
-
console.log("");
|
|
3918
|
-
console.log(
|
|
3919
|
-
"See `pspm init --help` for definitive documentation on these fields"
|
|
3920
|
-
);
|
|
3921
|
-
console.log("and exactly what they do.");
|
|
3922
|
-
console.log("");
|
|
3923
|
-
console.log("Press ^C at any time to quit.");
|
|
3924
|
-
const rl = createInterface({
|
|
3925
|
-
input: process.stdin,
|
|
3926
|
-
output: process.stdout
|
|
3927
|
-
});
|
|
3928
|
-
try {
|
|
3929
|
-
let name = await prompt(rl, "skill name:", defaultName);
|
|
3930
|
-
while (!isValidName(name)) {
|
|
3931
|
-
console.log(
|
|
3932
|
-
" Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
|
|
3933
|
-
);
|
|
3934
|
-
name = await prompt(rl, "skill name:", sanitizeName(name));
|
|
3935
|
-
}
|
|
3936
|
-
let version3 = await prompt(rl, "version:", defaultVersion);
|
|
3937
|
-
while (!isValidVersion(version3)) {
|
|
3938
|
-
console.log(" Version must be valid semver (e.g., 1.0.0)");
|
|
3939
|
-
version3 = await prompt(rl, "version:", "0.1.0");
|
|
3940
|
-
}
|
|
3941
|
-
const description = await prompt(
|
|
3942
|
-
rl,
|
|
3943
|
-
"description:",
|
|
3944
|
-
defaultDescription
|
|
3945
|
-
);
|
|
3946
|
-
const main = await prompt(rl, "entry point:", defaultMain);
|
|
3947
|
-
const capabilitiesStr = await prompt(
|
|
3948
|
-
rl,
|
|
3949
|
-
"capabilities (comma-separated):",
|
|
3950
|
-
defaultCapabilities
|
|
3951
|
-
);
|
|
3952
|
-
const author = await prompt(rl, "author:", defaultAuthor);
|
|
3953
|
-
const license = await prompt(rl, "license:", defaultLicense);
|
|
3954
|
-
rl.close();
|
|
3955
|
-
const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
3956
|
-
manifest = {
|
|
3957
|
-
$schema: PSPM_SCHEMA_URL,
|
|
3958
|
-
name,
|
|
3959
|
-
version: version3,
|
|
3960
|
-
description: description || void 0,
|
|
3961
|
-
author: author || void 0,
|
|
3962
|
-
license,
|
|
3963
|
-
type: "skill",
|
|
3964
|
-
capabilities,
|
|
3965
|
-
main,
|
|
3966
|
-
requirements: {
|
|
3967
|
-
pspm: ">=0.1.0"
|
|
3968
|
-
},
|
|
3969
|
-
files: [...DEFAULT_SKILL_FILES],
|
|
3970
|
-
dependencies: {},
|
|
3971
|
-
private: false
|
|
3972
|
-
};
|
|
3973
|
-
} catch (error) {
|
|
3974
|
-
rl.close();
|
|
3975
|
-
if (error instanceof Error && error.message.includes("readline was closed")) {
|
|
3976
|
-
console.log("\nAborted.");
|
|
3977
|
-
process.exit(0);
|
|
3978
|
-
}
|
|
3979
|
-
throw error;
|
|
3980
|
-
}
|
|
4323
|
+
lines.push("; Uncomment to use a custom registry:");
|
|
4324
|
+
lines.push("; registry = https://custom-registry.example.com");
|
|
3981
4325
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
const content = JSON.stringify(manifest, null, 2);
|
|
3986
|
-
console.log("");
|
|
3987
|
-
console.log(`About to write to ${pspmJsonPath}:`);
|
|
3988
|
-
console.log("");
|
|
3989
|
-
console.log(content);
|
|
4326
|
+
lines.push("");
|
|
4327
|
+
await writeFile(configPath, lines.join("\n"));
|
|
4328
|
+
console.log("Created .pspmrc");
|
|
3990
4329
|
console.log("");
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
}
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
}
|
|
4330
|
+
console.log("Contents:");
|
|
4331
|
+
console.log(lines.join("\n"));
|
|
4332
|
+
console.log("Note: .pspmrc should be committed to version control.");
|
|
4333
|
+
console.log("API keys should NOT be stored here - use pspm login instead.");
|
|
4334
|
+
} catch (error) {
|
|
4335
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4336
|
+
console.error(`Error: ${message}`);
|
|
4337
|
+
process.exit(1);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
|
|
4341
|
+
// src/commands/config/show.ts
|
|
4342
|
+
init_config();
|
|
4343
|
+
async function configShow() {
|
|
4344
|
+
try {
|
|
4345
|
+
const resolved = await resolveConfig();
|
|
4346
|
+
const projectConfig = await findProjectConfig();
|
|
4347
|
+
const configPath = getConfigPath();
|
|
4348
|
+
console.log("Resolved Configuration:\n");
|
|
4349
|
+
console.log(` Registry URL: ${resolved.registryUrl}`);
|
|
4350
|
+
console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
|
|
4351
|
+
console.log(` Username: ${resolved.username || "(not set)"}`);
|
|
4352
|
+
console.log("");
|
|
4353
|
+
console.log("Config Locations:");
|
|
4354
|
+
console.log(` User config: ${configPath}`);
|
|
4355
|
+
console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
|
|
4356
|
+
console.log("");
|
|
4357
|
+
console.log("Environment Variables:");
|
|
4358
|
+
console.log(
|
|
4359
|
+
` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
|
|
4360
|
+
);
|
|
4361
|
+
console.log(
|
|
4362
|
+
` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
|
|
4363
|
+
);
|
|
4017
4364
|
} catch (error) {
|
|
4018
4365
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4019
4366
|
console.error(`Error: ${message}`);
|
|
@@ -4021,502 +4368,289 @@ async function init(options) {
|
|
|
4021
4368
|
}
|
|
4022
4369
|
}
|
|
4023
4370
|
|
|
4024
|
-
// src/commands/
|
|
4025
|
-
init_agents();
|
|
4371
|
+
// src/commands/deprecate.ts
|
|
4026
4372
|
init_api_client();
|
|
4027
4373
|
init_config();
|
|
4028
|
-
init_errors();
|
|
4029
|
-
init_github();
|
|
4030
4374
|
init_lib();
|
|
4031
|
-
|
|
4032
|
-
init_manifest3();
|
|
4033
|
-
init_symlinks();
|
|
4034
|
-
function getCacheFilePath(cacheDir, integrity) {
|
|
4035
|
-
const match = integrity.match(/^sha256-(.+)$/);
|
|
4036
|
-
if (!match) {
|
|
4037
|
-
throw new Error(`Invalid integrity format: ${integrity}`);
|
|
4038
|
-
}
|
|
4039
|
-
const base64Hash = match[1];
|
|
4040
|
-
const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
|
|
4041
|
-
return join(cacheDir, `sha256-${hexHash}.tgz`);
|
|
4042
|
-
}
|
|
4043
|
-
async function readFromCache(cacheDir, integrity) {
|
|
4044
|
-
try {
|
|
4045
|
-
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
4046
|
-
const data = await readFile(cachePath);
|
|
4047
|
-
const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
|
|
4048
|
-
if (actualIntegrity !== integrity) {
|
|
4049
|
-
await rm(cachePath, { force: true });
|
|
4050
|
-
return null;
|
|
4051
|
-
}
|
|
4052
|
-
return data;
|
|
4053
|
-
} catch {
|
|
4054
|
-
return null;
|
|
4055
|
-
}
|
|
4056
|
-
}
|
|
4057
|
-
async function writeToCache(cacheDir, integrity, data) {
|
|
4375
|
+
async function deprecate(specifier, message, options) {
|
|
4058
4376
|
try {
|
|
4059
|
-
|
|
4060
|
-
const
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
}
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4068
|
-
setGlobalMode2(true);
|
|
4069
|
-
}
|
|
4070
|
-
if (options.list) {
|
|
4071
|
-
const listSpecifiers = await resolveListToSpecifiers(options.list);
|
|
4072
|
-
if (listSpecifiers.length === 0) {
|
|
4073
|
-
console.log("No skills in the list to install.");
|
|
4074
|
-
return;
|
|
4377
|
+
const apiKey = await requireApiKey();
|
|
4378
|
+
const registryUrl = await getRegistryUrl();
|
|
4379
|
+
const parsed = parseRegistrySpecifier2(specifier);
|
|
4380
|
+
if (!parsed) {
|
|
4381
|
+
console.error(
|
|
4382
|
+
`Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
|
|
4383
|
+
);
|
|
4384
|
+
process.exit(1);
|
|
4075
4385
|
}
|
|
4076
|
-
const
|
|
4077
|
-
const
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
yes: options.yes,
|
|
4082
|
-
global: options.global
|
|
4083
|
-
});
|
|
4084
|
-
return;
|
|
4085
|
-
}
|
|
4086
|
-
if (specifiers.length > 0) {
|
|
4087
|
-
const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
4088
|
-
await add2(specifiers, {
|
|
4089
|
-
save: true,
|
|
4090
|
-
agent: options.agent,
|
|
4091
|
-
yes: options.yes,
|
|
4092
|
-
global: options.global
|
|
4386
|
+
const { owner, name, versionRange } = parsed;
|
|
4387
|
+
const fullName = generateRegistryIdentifier2({
|
|
4388
|
+
namespace: parsed.namespace,
|
|
4389
|
+
owner,
|
|
4390
|
+
name
|
|
4093
4391
|
});
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
|
|
4100
|
-
if (!match) {
|
|
4101
|
-
console.error(
|
|
4102
|
-
`Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
|
|
4103
|
-
);
|
|
4104
|
-
process.exit(1);
|
|
4105
|
-
}
|
|
4106
|
-
const [, ownerType, ownerName, listName] = match;
|
|
4107
|
-
const config2 = await resolveConfig();
|
|
4108
|
-
configure2({
|
|
4109
|
-
registryUrl: config2.registryUrl,
|
|
4110
|
-
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
4111
|
-
});
|
|
4112
|
-
console.log(
|
|
4113
|
-
`Fetching skill list @${ownerType}/${ownerName}/${listName}...
|
|
4114
|
-
`
|
|
4115
|
-
);
|
|
4116
|
-
const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
4117
|
-
const response = await fetchSkillList2(
|
|
4118
|
-
ownerType,
|
|
4119
|
-
ownerName,
|
|
4120
|
-
listName
|
|
4121
|
-
);
|
|
4122
|
-
if (response.status !== 200 || !response.data) {
|
|
4123
|
-
const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
|
|
4124
|
-
console.error(`Error: ${errorMsg}`);
|
|
4125
|
-
process.exit(1);
|
|
4126
|
-
}
|
|
4127
|
-
const list2 = response.data;
|
|
4128
|
-
console.log(
|
|
4129
|
-
`List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
|
|
4130
|
-
);
|
|
4131
|
-
console.log(`Skills: ${list2.items.length}
|
|
4132
|
-
`);
|
|
4133
|
-
const specifiers = [];
|
|
4134
|
-
for (const item of list2.items) {
|
|
4135
|
-
const ns = item.namespace === "org" ? "org" : "user";
|
|
4136
|
-
let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
|
|
4137
|
-
if (item.pinnedVersion) {
|
|
4138
|
-
spec += `@${item.pinnedVersion}`;
|
|
4139
|
-
}
|
|
4140
|
-
specifiers.push(spec);
|
|
4141
|
-
}
|
|
4142
|
-
return specifiers;
|
|
4143
|
-
}
|
|
4144
|
-
async function installFromLockfile(options) {
|
|
4145
|
-
try {
|
|
4146
|
-
const config2 = await resolveConfig();
|
|
4147
|
-
const registryUrl = config2.registryUrl;
|
|
4148
|
-
const apiKey = getTokenForRegistry(config2, registryUrl);
|
|
4149
|
-
const skillsDir = options.dir || getSkillsDir();
|
|
4150
|
-
const cacheDir = getCacheDir();
|
|
4151
|
-
await migrateLockfileIfNeeded();
|
|
4152
|
-
let lockfile = await readLockfile();
|
|
4153
|
-
const manifestDeps = await getDependencies();
|
|
4154
|
-
const manifestGitHubDeps = await getGitHubDependencies();
|
|
4155
|
-
const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
4156
|
-
const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
|
|
4157
|
-
const installedSkills = [];
|
|
4158
|
-
const missingDeps = [];
|
|
4159
|
-
for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
|
|
4160
|
-
if (!lockfilePackages[fullName]) {
|
|
4161
|
-
missingDeps.push({ fullName, versionRange });
|
|
4162
|
-
}
|
|
4392
|
+
if (!versionRange) {
|
|
4393
|
+
console.error(
|
|
4394
|
+
"Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
|
|
4395
|
+
);
|
|
4396
|
+
process.exit(1);
|
|
4163
4397
|
}
|
|
4164
|
-
|
|
4165
|
-
|
|
4398
|
+
configure2({ registryUrl, apiKey });
|
|
4399
|
+
if (options.undo) {
|
|
4400
|
+
console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
|
|
4401
|
+
const response = await undeprecateSkillVersion(owner, name, versionRange);
|
|
4402
|
+
if (response.status !== 200) {
|
|
4166
4403
|
console.error(
|
|
4167
|
-
|
|
4168
|
-
);
|
|
4169
|
-
|
|
4170
|
-
for (const dep of missingDeps) {
|
|
4171
|
-
console.error(` - ${dep.fullName}@${dep.versionRange}`);
|
|
4172
|
-
}
|
|
4173
|
-
process.exit(1);
|
|
4174
|
-
}
|
|
4175
|
-
console.log(`Resolving ${missingDeps.length} new dependency(ies)...
|
|
4176
|
-
`);
|
|
4177
|
-
configure2({ registryUrl, apiKey });
|
|
4178
|
-
for (const { fullName, versionRange } of missingDeps) {
|
|
4179
|
-
const parsed = parseRegistrySpecifier2(fullName);
|
|
4180
|
-
if (!parsed) {
|
|
4181
|
-
console.error(`Error: Invalid dependency specifier: ${fullName}`);
|
|
4182
|
-
continue;
|
|
4183
|
-
}
|
|
4184
|
-
const { owner, name } = parsed;
|
|
4185
|
-
console.log(`Resolving ${fullName}@${versionRange}...`);
|
|
4186
|
-
const versionsResponse = await listSkillVersions(owner, name);
|
|
4187
|
-
if (versionsResponse.status !== 200) {
|
|
4188
|
-
const errorMessage = extractApiErrorMessage(
|
|
4189
|
-
versionsResponse,
|
|
4190
|
-
`Skill ${fullName} not found`
|
|
4191
|
-
);
|
|
4192
|
-
console.error(`Error: ${errorMessage}`);
|
|
4193
|
-
continue;
|
|
4194
|
-
}
|
|
4195
|
-
const versions = versionsResponse.data;
|
|
4196
|
-
if (versions.length === 0) {
|
|
4197
|
-
console.error(`Error: Skill ${fullName} not found`);
|
|
4198
|
-
continue;
|
|
4199
|
-
}
|
|
4200
|
-
const versionStrings = versions.map(
|
|
4201
|
-
(v) => v.version
|
|
4202
|
-
);
|
|
4203
|
-
const resolved = resolveVersion2(versionRange || "*", versionStrings);
|
|
4204
|
-
if (!resolved) {
|
|
4205
|
-
console.error(
|
|
4206
|
-
`Error: No version matching "${versionRange}" for ${fullName}`
|
|
4207
|
-
);
|
|
4208
|
-
continue;
|
|
4209
|
-
}
|
|
4210
|
-
const versionResponse = await getSkillVersion(owner, name, resolved);
|
|
4211
|
-
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
4212
|
-
const errorMessage = extractApiErrorMessage(
|
|
4213
|
-
versionResponse,
|
|
4214
|
-
`Version ${resolved} not found`
|
|
4215
|
-
);
|
|
4216
|
-
console.error(`Error: ${errorMessage}`);
|
|
4217
|
-
continue;
|
|
4218
|
-
}
|
|
4219
|
-
const versionInfo = versionResponse.data;
|
|
4220
|
-
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
4221
|
-
const downloadHeaders = {};
|
|
4222
|
-
if (!isPresignedUrl && apiKey) {
|
|
4223
|
-
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
4224
|
-
}
|
|
4225
|
-
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
4226
|
-
headers: downloadHeaders,
|
|
4227
|
-
redirect: "follow"
|
|
4228
|
-
});
|
|
4229
|
-
if (!tarballResponse.ok) {
|
|
4230
|
-
console.error(
|
|
4231
|
-
`Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
|
|
4232
|
-
);
|
|
4233
|
-
continue;
|
|
4234
|
-
}
|
|
4235
|
-
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
4236
|
-
const integrity = calculateIntegrity(tarballBuffer);
|
|
4237
|
-
await addToLockfile(fullName, {
|
|
4238
|
-
version: resolved,
|
|
4239
|
-
resolved: versionInfo.downloadUrl,
|
|
4240
|
-
integrity
|
|
4241
|
-
});
|
|
4242
|
-
await writeToCache(cacheDir, integrity, tarballBuffer);
|
|
4243
|
-
console.log(` Resolved ${fullName}@${resolved}`);
|
|
4244
|
-
}
|
|
4245
|
-
lockfile = await readLockfile();
|
|
4246
|
-
}
|
|
4247
|
-
const missingGitHubDeps = [];
|
|
4248
|
-
for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
|
|
4249
|
-
if (!lockfileGitHubPackages[specifier]) {
|
|
4250
|
-
missingGitHubDeps.push({ specifier, ref });
|
|
4404
|
+
`Error: ${response.error || "Failed to remove deprecation"}`
|
|
4405
|
+
);
|
|
4406
|
+
process.exit(1);
|
|
4251
4407
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
if (
|
|
4408
|
+
console.log(`Removed deprecation from ${fullName}@${versionRange}`);
|
|
4409
|
+
} else {
|
|
4410
|
+
if (!message) {
|
|
4255
4411
|
console.error(
|
|
4256
|
-
"Error:
|
|
4412
|
+
"Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
|
|
4257
4413
|
);
|
|
4258
|
-
console.error("Missing GitHub dependencies:");
|
|
4259
|
-
for (const dep of missingGitHubDeps) {
|
|
4260
|
-
console.error(` - ${dep.specifier}@${dep.ref}`);
|
|
4261
|
-
}
|
|
4262
4414
|
process.exit(1);
|
|
4263
4415
|
}
|
|
4264
|
-
console.log(
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4416
|
+
console.log(`Deprecating ${fullName}@${versionRange}...`);
|
|
4417
|
+
const response = await deprecateSkillVersion(
|
|
4418
|
+
owner,
|
|
4419
|
+
name,
|
|
4420
|
+
versionRange,
|
|
4421
|
+
message
|
|
4268
4422
|
);
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
}
|
|
4275
|
-
parsed.ref = parsed.ref || ref;
|
|
4276
|
-
console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
|
|
4277
|
-
try {
|
|
4278
|
-
const result = await downloadGitHubPackage(parsed);
|
|
4279
|
-
await extractGitHubPackage(parsed, result.buffer, skillsDir);
|
|
4280
|
-
const entry = {
|
|
4281
|
-
version: result.commit.slice(0, 7),
|
|
4282
|
-
resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
|
|
4283
|
-
integrity: result.integrity,
|
|
4284
|
-
gitCommit: result.commit,
|
|
4285
|
-
gitRef: ref || "HEAD"
|
|
4286
|
-
};
|
|
4287
|
-
await addGitHubToLockfile(specifier, entry);
|
|
4288
|
-
await writeToCache(cacheDir, result.integrity, result.buffer);
|
|
4289
|
-
console.log(
|
|
4290
|
-
` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
|
|
4291
|
-
);
|
|
4292
|
-
} catch (error) {
|
|
4293
|
-
if (error instanceof GitHubRateLimitError) {
|
|
4294
|
-
console.error(`Error: ${error.message}`);
|
|
4295
|
-
} else if (error instanceof GitHubPathNotFoundError) {
|
|
4296
|
-
console.error(`Error: ${error.message}`);
|
|
4297
|
-
} else if (error instanceof GitHubNotFoundError) {
|
|
4298
|
-
console.error(`Error: ${error.message}`);
|
|
4299
|
-
} else {
|
|
4300
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4301
|
-
console.error(`Error resolving ${specifier}: ${message}`);
|
|
4302
|
-
}
|
|
4303
|
-
}
|
|
4423
|
+
if (response.status !== 200) {
|
|
4424
|
+
console.error(
|
|
4425
|
+
`Error: ${response.error || "Failed to deprecate version"}`
|
|
4426
|
+
);
|
|
4427
|
+
process.exit(1);
|
|
4304
4428
|
}
|
|
4305
|
-
|
|
4429
|
+
console.log(`Deprecated ${fullName}@${versionRange}`);
|
|
4430
|
+
console.log(`Message: ${message}`);
|
|
4431
|
+
console.log("");
|
|
4432
|
+
console.log(
|
|
4433
|
+
"Users installing this version will see a deprecation warning."
|
|
4434
|
+
);
|
|
4435
|
+
console.log("The package is still available for download.");
|
|
4306
4436
|
}
|
|
4307
|
-
|
|
4308
|
-
const
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4437
|
+
} catch (error) {
|
|
4438
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4439
|
+
console.error(`Error: ${errorMessage}`);
|
|
4440
|
+
process.exit(1);
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
// src/commands/init.ts
|
|
4445
|
+
init_lib();
|
|
4446
|
+
function prompt(rl, question, defaultValue) {
|
|
4447
|
+
return new Promise((resolve3) => {
|
|
4448
|
+
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
4449
|
+
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
4450
|
+
resolve3(answer.trim() || defaultValue);
|
|
4451
|
+
});
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
async function readExistingPackageJson() {
|
|
4455
|
+
try {
|
|
4456
|
+
const content = await readFile(
|
|
4457
|
+
join(process.cwd(), "package.json"),
|
|
4458
|
+
"utf-8"
|
|
4459
|
+
);
|
|
4460
|
+
const pkg = JSON.parse(content);
|
|
4461
|
+
return {
|
|
4462
|
+
name: pkg.name,
|
|
4463
|
+
version: pkg.version,
|
|
4464
|
+
description: pkg.description,
|
|
4465
|
+
author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
|
|
4466
|
+
license: pkg.license
|
|
4467
|
+
};
|
|
4468
|
+
} catch {
|
|
4469
|
+
return null;
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
async function getGitAuthor() {
|
|
4473
|
+
try {
|
|
4474
|
+
const { exec: exec2 } = await import('child_process');
|
|
4475
|
+
const { promisify: promisify2 } = await import('util');
|
|
4476
|
+
const execAsync = promisify2(exec2);
|
|
4477
|
+
const [nameResult, emailResult] = await Promise.all([
|
|
4478
|
+
execAsync("git config user.name").catch(() => ({ stdout: "" })),
|
|
4479
|
+
execAsync("git config user.email").catch(() => ({ stdout: "" }))
|
|
4480
|
+
]);
|
|
4481
|
+
const name = nameResult.stdout.trim();
|
|
4482
|
+
const email = emailResult.stdout.trim();
|
|
4483
|
+
if (name && email) {
|
|
4484
|
+
return `${name} <${email}>`;
|
|
4320
4485
|
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4486
|
+
if (name) {
|
|
4487
|
+
return name;
|
|
4488
|
+
}
|
|
4489
|
+
return null;
|
|
4490
|
+
} catch {
|
|
4491
|
+
return null;
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
function sanitizeName(name) {
|
|
4495
|
+
const withoutScope = name.replace(/^@[^/]+\//, "");
|
|
4496
|
+
return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
4497
|
+
}
|
|
4498
|
+
function isValidName(name) {
|
|
4499
|
+
return /^[a-z][a-z0-9_-]*$/.test(name);
|
|
4500
|
+
}
|
|
4501
|
+
function isValidVersion(version3) {
|
|
4502
|
+
return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
|
|
4503
|
+
}
|
|
4504
|
+
async function init(options) {
|
|
4505
|
+
try {
|
|
4506
|
+
const pspmJsonPath = join(process.cwd(), "pspm.json");
|
|
4507
|
+
let exists = false;
|
|
4508
|
+
try {
|
|
4509
|
+
await stat(pspmJsonPath);
|
|
4510
|
+
exists = true;
|
|
4511
|
+
} catch {
|
|
4512
|
+
}
|
|
4513
|
+
if (exists && !options.force) {
|
|
4514
|
+
console.error("Error: pspm.json already exists in this directory.");
|
|
4515
|
+
console.error("Use --force to overwrite.");
|
|
4516
|
+
process.exit(1);
|
|
4517
|
+
}
|
|
4518
|
+
const existingPkg = await readExistingPackageJson();
|
|
4519
|
+
const gitAuthor = await getGitAuthor();
|
|
4520
|
+
const defaultName = sanitizeName(
|
|
4521
|
+
options.name || existingPkg?.name || basename(process.cwd())
|
|
4522
|
+
);
|
|
4523
|
+
const defaultVersion = existingPkg?.version || "0.1.0";
|
|
4524
|
+
const defaultDescription = options.description || existingPkg?.description || "";
|
|
4525
|
+
const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
|
|
4526
|
+
const defaultLicense = existingPkg?.license || "MIT";
|
|
4527
|
+
const defaultMain = "SKILL.md";
|
|
4528
|
+
const defaultCapabilities = "";
|
|
4529
|
+
let manifest;
|
|
4530
|
+
if (options.yes) {
|
|
4531
|
+
manifest = {
|
|
4532
|
+
$schema: PSPM_SCHEMA_URL,
|
|
4533
|
+
name: defaultName,
|
|
4534
|
+
version: defaultVersion,
|
|
4535
|
+
description: defaultDescription || void 0,
|
|
4536
|
+
author: defaultAuthor || void 0,
|
|
4537
|
+
license: defaultLicense,
|
|
4538
|
+
type: "skill",
|
|
4539
|
+
capabilities: [],
|
|
4540
|
+
main: defaultMain,
|
|
4541
|
+
requirements: {
|
|
4542
|
+
pspm: ">=0.1.0"
|
|
4543
|
+
},
|
|
4544
|
+
files: [...DEFAULT_SKILL_FILES],
|
|
4545
|
+
dependencies: {},
|
|
4546
|
+
private: false
|
|
4547
|
+
};
|
|
4548
|
+
} else {
|
|
4549
|
+
console.log(
|
|
4550
|
+
"This utility will walk you through creating a pspm.json file."
|
|
4551
|
+
);
|
|
4552
|
+
console.log(
|
|
4553
|
+
"It only covers the most common items, and tries to guess sensible defaults."
|
|
4554
|
+
);
|
|
4555
|
+
console.log("");
|
|
4556
|
+
console.log(
|
|
4557
|
+
"See `pspm init --help` for definitive documentation on these fields"
|
|
4558
|
+
);
|
|
4559
|
+
console.log("and exactly what they do.");
|
|
4560
|
+
console.log("");
|
|
4561
|
+
console.log("Press ^C at any time to quit.");
|
|
4562
|
+
const rl = createInterface({
|
|
4563
|
+
input: process.stdin,
|
|
4564
|
+
output: process.stdout
|
|
4565
|
+
});
|
|
4566
|
+
try {
|
|
4567
|
+
let name = await prompt(rl, "skill name:", defaultName);
|
|
4568
|
+
while (!isValidName(name)) {
|
|
4569
|
+
console.log(
|
|
4570
|
+
" Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
|
|
4395
4571
|
);
|
|
4396
|
-
|
|
4397
|
-
destDir = join(skillsDir, pkgOwner, effectiveSkillName);
|
|
4572
|
+
name = await prompt(rl, "skill name:", sanitizeName(name));
|
|
4398
4573
|
}
|
|
4399
|
-
await
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
const { exec: exec2 } = await import('child_process');
|
|
4404
|
-
const { promisify: promisify2 } = await import('util');
|
|
4405
|
-
const execAsync = promisify2(exec2);
|
|
4406
|
-
try {
|
|
4407
|
-
await execAsync(
|
|
4408
|
-
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
4409
|
-
);
|
|
4410
|
-
} finally {
|
|
4411
|
-
await rm(tempFile, { force: true });
|
|
4574
|
+
let version3 = await prompt(rl, "version:", defaultVersion);
|
|
4575
|
+
while (!isValidVersion(version3)) {
|
|
4576
|
+
console.log(" Version must be valid semver (e.g., 1.0.0)");
|
|
4577
|
+
version3 = await prompt(rl, "version:", "0.1.0");
|
|
4412
4578
|
}
|
|
4413
|
-
|
|
4414
|
-
|
|
4579
|
+
const description = await prompt(
|
|
4580
|
+
rl,
|
|
4581
|
+
"description:",
|
|
4582
|
+
defaultDescription
|
|
4415
4583
|
);
|
|
4416
|
-
const
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
}
|
|
4422
|
-
}
|
|
4423
|
-
const githubPackages = lockfile?.githubPackages ?? {};
|
|
4424
|
-
const githubCount = Object.keys(githubPackages).length;
|
|
4425
|
-
if (githubCount > 0) {
|
|
4426
|
-
console.log(`
|
|
4427
|
-
Installing ${githubCount} GitHub skill(s)...
|
|
4428
|
-
`);
|
|
4429
|
-
for (const [specifier, entry] of Object.entries(githubPackages)) {
|
|
4430
|
-
const parsed = parseGitHubSpecifier2(specifier);
|
|
4431
|
-
if (!parsed) {
|
|
4432
|
-
console.warn(
|
|
4433
|
-
`Warning: Invalid GitHub specifier in lockfile: ${specifier}`
|
|
4434
|
-
);
|
|
4435
|
-
continue;
|
|
4436
|
-
}
|
|
4437
|
-
const ghEntry = entry;
|
|
4438
|
-
console.log(
|
|
4439
|
-
`Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
|
|
4584
|
+
const main = await prompt(rl, "entry point:", defaultMain);
|
|
4585
|
+
const capabilitiesStr = await prompt(
|
|
4586
|
+
rl,
|
|
4587
|
+
"capabilities (comma-separated):",
|
|
4588
|
+
defaultCapabilities
|
|
4440
4589
|
);
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
} else if (error instanceof GitHubNotFoundError) {
|
|
4468
|
-
console.error(` Error: ${error.message}`);
|
|
4469
|
-
} else {
|
|
4470
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4471
|
-
console.error(` Error downloading ${specifier}: ${message}`);
|
|
4472
|
-
}
|
|
4473
|
-
continue;
|
|
4474
|
-
}
|
|
4475
|
-
}
|
|
4476
|
-
try {
|
|
4477
|
-
const destPath = await extractGitHubPackage(
|
|
4478
|
-
parsed,
|
|
4479
|
-
tarballBuffer,
|
|
4480
|
-
skillsDir
|
|
4481
|
-
);
|
|
4482
|
-
console.log(
|
|
4483
|
-
` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
|
|
4484
|
-
);
|
|
4485
|
-
const skillName = getGitHubSkillName2(parsed);
|
|
4486
|
-
installedSkills.push({
|
|
4487
|
-
name: skillName,
|
|
4488
|
-
sourcePath: getGitHubSkillPath(
|
|
4489
|
-
parsed.owner,
|
|
4490
|
-
parsed.repo,
|
|
4491
|
-
parsed.path
|
|
4492
|
-
)
|
|
4493
|
-
});
|
|
4494
|
-
} catch (error) {
|
|
4495
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4496
|
-
console.error(` Error extracting ${specifier}: ${message}`);
|
|
4590
|
+
const author = await prompt(rl, "author:", defaultAuthor);
|
|
4591
|
+
const license = await prompt(rl, "license:", defaultLicense);
|
|
4592
|
+
rl.close();
|
|
4593
|
+
const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
4594
|
+
manifest = {
|
|
4595
|
+
$schema: PSPM_SCHEMA_URL,
|
|
4596
|
+
name,
|
|
4597
|
+
version: version3,
|
|
4598
|
+
description: description || void 0,
|
|
4599
|
+
author: author || void 0,
|
|
4600
|
+
license,
|
|
4601
|
+
type: "skill",
|
|
4602
|
+
capabilities,
|
|
4603
|
+
main,
|
|
4604
|
+
requirements: {
|
|
4605
|
+
pspm: ">=0.1.0"
|
|
4606
|
+
},
|
|
4607
|
+
files: [...DEFAULT_SKILL_FILES],
|
|
4608
|
+
dependencies: {},
|
|
4609
|
+
private: false
|
|
4610
|
+
};
|
|
4611
|
+
} catch (error) {
|
|
4612
|
+
rl.close();
|
|
4613
|
+
if (error instanceof Error && error.message.includes("readline was closed")) {
|
|
4614
|
+
console.log("\nAborted.");
|
|
4615
|
+
process.exit(0);
|
|
4497
4616
|
}
|
|
4617
|
+
throw error;
|
|
4498
4618
|
}
|
|
4499
4619
|
}
|
|
4500
|
-
if (
|
|
4501
|
-
|
|
4620
|
+
if (!manifest.description) manifest.description = void 0;
|
|
4621
|
+
if (!manifest.author) manifest.author = void 0;
|
|
4622
|
+
if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
|
|
4623
|
+
const content = JSON.stringify(manifest, null, 2);
|
|
4624
|
+
console.log("");
|
|
4625
|
+
console.log(`About to write to ${pspmJsonPath}:`);
|
|
4626
|
+
console.log("");
|
|
4627
|
+
console.log(content);
|
|
4628
|
+
console.log("");
|
|
4629
|
+
if (!options.yes) {
|
|
4630
|
+
const rl = createInterface({
|
|
4631
|
+
input: process.stdin,
|
|
4632
|
+
output: process.stdout
|
|
4633
|
+
});
|
|
4634
|
+
const confirm2 = await prompt(rl, "Is this OK?", "yes");
|
|
4635
|
+
rl.close();
|
|
4636
|
+
if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
|
|
4637
|
+
console.log("Aborted.");
|
|
4638
|
+
process.exit(0);
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
await writeFile(pspmJsonPath, `${content}
|
|
4642
|
+
`);
|
|
4643
|
+
try {
|
|
4644
|
+
await stat(join(process.cwd(), "SKILL.md"));
|
|
4645
|
+
} catch {
|
|
4502
4646
|
console.log(
|
|
4503
|
-
|
|
4504
|
-
Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
|
|
4647
|
+
"Note: Create a SKILL.md file with your skill's prompt content."
|
|
4505
4648
|
);
|
|
4506
|
-
await createAgentSymlinks(installedSkills, {
|
|
4507
|
-
agents,
|
|
4508
|
-
projectRoot: globalMode ? homedir() : process.cwd(),
|
|
4509
|
-
agentConfigs,
|
|
4510
|
-
global: globalMode
|
|
4511
|
-
});
|
|
4512
|
-
console.log(" Symlinks created.");
|
|
4513
4649
|
}
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
console.log("
|
|
4517
|
-
|
|
4518
|
-
console.log(`
|
|
4519
|
-
All ${totalCount} skill(s) installed.`);
|
|
4650
|
+
if (existingPkg) {
|
|
4651
|
+
console.log("Note: Values were derived from existing package.json.");
|
|
4652
|
+
console.log(" pspm.json is for publishing to PSPM registry.");
|
|
4653
|
+
console.log(" package.json can still be used for npm dependencies.");
|
|
4520
4654
|
}
|
|
4521
4655
|
} catch (error) {
|
|
4522
4656
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -4525,6 +4659,9 @@ All ${totalCount} skill(s) installed.`);
|
|
|
4525
4659
|
}
|
|
4526
4660
|
}
|
|
4527
4661
|
|
|
4662
|
+
// src/commands/index.ts
|
|
4663
|
+
init_install();
|
|
4664
|
+
|
|
4528
4665
|
// src/commands/link.ts
|
|
4529
4666
|
init_agents();
|
|
4530
4667
|
init_lib();
|
|
@@ -4830,8 +4967,8 @@ function startCallbackServer(expectedState) {
|
|
|
4830
4967
|
let resolveToken;
|
|
4831
4968
|
let rejectToken;
|
|
4832
4969
|
let timeoutId;
|
|
4833
|
-
const tokenPromise = new Promise((
|
|
4834
|
-
resolveToken =
|
|
4970
|
+
const tokenPromise = new Promise((resolve3, reject) => {
|
|
4971
|
+
resolveToken = resolve3;
|
|
4835
4972
|
rejectToken = reject;
|
|
4836
4973
|
});
|
|
4837
4974
|
const server = http.createServer((req, res) => {
|
|
@@ -5085,6 +5222,92 @@ async function migrate(options) {
|
|
|
5085
5222
|
process.exit(1);
|
|
5086
5223
|
}
|
|
5087
5224
|
}
|
|
5225
|
+
|
|
5226
|
+
// src/commands/notebook.ts
|
|
5227
|
+
init_config();
|
|
5228
|
+
async function fetchApi(path, options = {}) {
|
|
5229
|
+
const config2 = await resolveConfig();
|
|
5230
|
+
const apiKey = await requireApiKey();
|
|
5231
|
+
const baseUrl = config2.registryUrl.replace(/\/api\/skills\/?$/, "");
|
|
5232
|
+
const headers = {
|
|
5233
|
+
"Content-Type": "application/json",
|
|
5234
|
+
...options.headers
|
|
5235
|
+
};
|
|
5236
|
+
if (apiKey) {
|
|
5237
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
5238
|
+
}
|
|
5239
|
+
return fetch(`${baseUrl}${path}`, { ...options, headers });
|
|
5240
|
+
}
|
|
5241
|
+
async function notebookUpload(filePath, options) {
|
|
5242
|
+
const absPath = resolve(filePath);
|
|
5243
|
+
const content = readFileSync(absPath, "utf-8");
|
|
5244
|
+
const name = basename(filePath).replace(/\.anyt\.md$|\.anyt$|\.md$/, "");
|
|
5245
|
+
const body = {
|
|
5246
|
+
name,
|
|
5247
|
+
content,
|
|
5248
|
+
description: options.description,
|
|
5249
|
+
visibility: options.visibility || "private"
|
|
5250
|
+
};
|
|
5251
|
+
const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks";
|
|
5252
|
+
const response = await fetchApi(path, {
|
|
5253
|
+
method: "POST",
|
|
5254
|
+
body: JSON.stringify(body)
|
|
5255
|
+
});
|
|
5256
|
+
if (!response.ok) {
|
|
5257
|
+
const err = await response.json().catch(() => ({}));
|
|
5258
|
+
throw new Error(
|
|
5259
|
+
err.message || `Upload failed (${response.status})`
|
|
5260
|
+
);
|
|
5261
|
+
}
|
|
5262
|
+
const notebook = await response.json();
|
|
5263
|
+
console.log(
|
|
5264
|
+
`Uploaded: ${notebook.name} (${notebook.id})${options.org ? ` to org ${options.org}` : ""}`
|
|
5265
|
+
);
|
|
5266
|
+
}
|
|
5267
|
+
async function notebookList(options) {
|
|
5268
|
+
const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks/-/mine";
|
|
5269
|
+
const response = await fetchApi(path);
|
|
5270
|
+
if (!response.ok) {
|
|
5271
|
+
throw new Error(`Failed to list notebooks (${response.status})`);
|
|
5272
|
+
}
|
|
5273
|
+
const notebooks = await response.json();
|
|
5274
|
+
if (notebooks.length === 0) {
|
|
5275
|
+
console.log("No notebooks found");
|
|
5276
|
+
return;
|
|
5277
|
+
}
|
|
5278
|
+
console.log(
|
|
5279
|
+
`
|
|
5280
|
+
${"Name".padEnd(30)} ${"Cells".padEnd(8)} ${"Visibility".padEnd(12)} ${"Updated".padEnd(12)} ID`
|
|
5281
|
+
);
|
|
5282
|
+
console.log("-".repeat(90));
|
|
5283
|
+
for (const nb of notebooks) {
|
|
5284
|
+
const updated = new Date(nb.updatedAt).toLocaleDateString();
|
|
5285
|
+
console.log(
|
|
5286
|
+
`${nb.name.slice(0, 28).padEnd(30)} ${String(nb.cellCount).padEnd(8)} ${nb.visibility.padEnd(12)} ${updated.padEnd(12)} ${nb.id}`
|
|
5287
|
+
);
|
|
5288
|
+
}
|
|
5289
|
+
console.log(`
|
|
5290
|
+
${notebooks.length} notebook(s)`);
|
|
5291
|
+
}
|
|
5292
|
+
async function notebookDownload(id, output) {
|
|
5293
|
+
const response = await fetchApi(`/api/notebooks/${id}`);
|
|
5294
|
+
if (!response.ok) {
|
|
5295
|
+
throw new Error(`Notebook not found (${response.status})`);
|
|
5296
|
+
}
|
|
5297
|
+
const notebook = await response.json();
|
|
5298
|
+
const outPath = output || `${notebook.slug}.anyt.md`;
|
|
5299
|
+
writeFileSync(outPath, notebook.content, "utf-8");
|
|
5300
|
+
console.log(`Downloaded: ${notebook.name} -> ${outPath}`);
|
|
5301
|
+
}
|
|
5302
|
+
async function notebookDelete(id) {
|
|
5303
|
+
const response = await fetchApi(`/api/notebooks/${id}`, {
|
|
5304
|
+
method: "DELETE"
|
|
5305
|
+
});
|
|
5306
|
+
if (!response.ok) {
|
|
5307
|
+
throw new Error(`Failed to delete notebook (${response.status})`);
|
|
5308
|
+
}
|
|
5309
|
+
console.log(`Notebook ${id} deleted`);
|
|
5310
|
+
}
|
|
5088
5311
|
function resolveVersion3(range, availableVersions) {
|
|
5089
5312
|
const sorted = availableVersions.filter((v) => semver2.valid(v)).sort((a, b) => semver2.rcompare(a, b));
|
|
5090
5313
|
if (range === "latest") {
|
|
@@ -5101,7 +5324,7 @@ function getLatestVersion3(versions) {
|
|
|
5101
5324
|
return valid5.sort((a, b) => semver2.rcompare(a, b))[0];
|
|
5102
5325
|
}
|
|
5103
5326
|
|
|
5104
|
-
// ../../packages/
|
|
5327
|
+
// ../../packages/features/skill-registry/src/client/outdated.ts
|
|
5105
5328
|
function createOutdatedChecker(config2) {
|
|
5106
5329
|
const { registryUrl, apiKey, githubToken } = config2;
|
|
5107
5330
|
async function fetchWithAuth(url, token) {
|
|
@@ -5121,14 +5344,26 @@ function createOutdatedChecker(config2) {
|
|
|
5121
5344
|
const response = await fetchWithAuth(url, apiKey);
|
|
5122
5345
|
return await response.json();
|
|
5123
5346
|
}
|
|
5347
|
+
async function fetchGithubRegistryVersions(owner, repo, skillname) {
|
|
5348
|
+
const url = `${registryUrl}/@github/${owner}/${repo}/${skillname}/versions`;
|
|
5349
|
+
const response = await fetchWithAuth(url, apiKey);
|
|
5350
|
+
return await response.json();
|
|
5351
|
+
}
|
|
5124
5352
|
async function checkRegistryPackage(specifier, entry, versionRange) {
|
|
5125
|
-
const
|
|
5126
|
-
|
|
5353
|
+
const userMatch = specifier.match(/^@(?:user|org)\/([^/]+)\/([^/]+)$/);
|
|
5354
|
+
const githubMatch = specifier.match(/^@github\/([^/]+)\/([^/]+)\/([^/]+)$/);
|
|
5355
|
+
if (!userMatch && !githubMatch) {
|
|
5127
5356
|
throw new Error(`Invalid registry specifier: ${specifier}`);
|
|
5128
5357
|
}
|
|
5129
|
-
const
|
|
5358
|
+
const isGithubRegistry = !!githubMatch;
|
|
5359
|
+
const username = userMatch ? userMatch[1] : "";
|
|
5360
|
+
const name = userMatch ? userMatch[2] : "";
|
|
5130
5361
|
try {
|
|
5131
|
-
const versions = await
|
|
5362
|
+
const versions = isGithubRegistry && githubMatch ? await fetchGithubRegistryVersions(
|
|
5363
|
+
githubMatch[1],
|
|
5364
|
+
githubMatch[2],
|
|
5365
|
+
githubMatch[3]
|
|
5366
|
+
) : await fetchRegistryVersions(username, name);
|
|
5132
5367
|
const versionStrings = versions.map((v) => v.version);
|
|
5133
5368
|
const range = versionRange || "*";
|
|
5134
5369
|
const wanted = resolveVersion3(range, versionStrings);
|
|
@@ -5351,10 +5586,11 @@ function printTable(results) {
|
|
|
5351
5586
|
init_api_client();
|
|
5352
5587
|
init_config();
|
|
5353
5588
|
init_errors();
|
|
5589
|
+
init_encryption();
|
|
5354
5590
|
init_lib();
|
|
5355
5591
|
var exec = promisify(exec$1);
|
|
5356
5592
|
function confirm(question) {
|
|
5357
|
-
return new Promise((
|
|
5593
|
+
return new Promise((resolve3) => {
|
|
5358
5594
|
const rl = createInterface({
|
|
5359
5595
|
input: process.stdin,
|
|
5360
5596
|
output: process.stdout
|
|
@@ -5362,7 +5598,7 @@ function confirm(question) {
|
|
|
5362
5598
|
rl.question(`${question} (y/N) `, (answer) => {
|
|
5363
5599
|
rl.close();
|
|
5364
5600
|
const normalized = answer.trim().toLowerCase();
|
|
5365
|
-
|
|
5601
|
+
resolve3(normalized === "y" || normalized === "yes");
|
|
5366
5602
|
});
|
|
5367
5603
|
});
|
|
5368
5604
|
}
|
|
@@ -5472,8 +5708,10 @@ async function publishCommand(options) {
|
|
|
5472
5708
|
}
|
|
5473
5709
|
console.log("");
|
|
5474
5710
|
}
|
|
5711
|
+
const nameParts = manifest.name.split("/");
|
|
5712
|
+
const bareName = nameParts[nameParts.length - 1];
|
|
5475
5713
|
const packageJson2 = {
|
|
5476
|
-
name:
|
|
5714
|
+
name: bareName,
|
|
5477
5715
|
version: manifest.version,
|
|
5478
5716
|
description: manifest.description,
|
|
5479
5717
|
files: manifest.files,
|
|
@@ -5589,19 +5827,53 @@ async function publishCommand(options) {
|
|
|
5589
5827
|
process.exit(0);
|
|
5590
5828
|
}
|
|
5591
5829
|
console.log("");
|
|
5830
|
+
let finalTarballBase64 = tarballBase64;
|
|
5831
|
+
if (options.access === "private" || options.access === "team") {
|
|
5832
|
+
const config2 = await Promise.resolve().then(() => (init_config(), config_exports)).then((m) => m.resolveConfig());
|
|
5833
|
+
const namespace2 = options.org ? "org" : "user";
|
|
5834
|
+
const owner2 = options.org ?? config2.username;
|
|
5835
|
+
if (!owner2) {
|
|
5836
|
+
console.error(
|
|
5837
|
+
"Error: Cannot determine package owner. Run 'pspm login' first."
|
|
5838
|
+
);
|
|
5839
|
+
process.exit(1);
|
|
5840
|
+
}
|
|
5841
|
+
const scope = getEncryptionScope(namespace2, owner2);
|
|
5842
|
+
const encryptionKey = await getEncryptionKey(scope);
|
|
5843
|
+
if (encryptionKey) {
|
|
5844
|
+
console.log(`pspm notice Encrypting package (scope: ${scope})`);
|
|
5845
|
+
const { encrypted, metadata } = encryptBuffer(
|
|
5846
|
+
tarballBuffer,
|
|
5847
|
+
encryptionKey,
|
|
5848
|
+
scope
|
|
5849
|
+
);
|
|
5850
|
+
finalTarballBase64 = encrypted.toString("base64");
|
|
5851
|
+
packageJson2.encryption = metadata;
|
|
5852
|
+
console.log(
|
|
5853
|
+
"pspm notice Package encrypted with client-side encryption"
|
|
5854
|
+
);
|
|
5855
|
+
} else {
|
|
5856
|
+
console.log(
|
|
5857
|
+
`pspm warn No encryption key found for scope "${scope}". Publishing without encryption.`
|
|
5858
|
+
);
|
|
5859
|
+
console.log(
|
|
5860
|
+
`pspm warn To encrypt, run: pspm config set-encryption-key ${scope} <your-passphrase>`
|
|
5861
|
+
);
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5592
5864
|
console.log(`pspm notice Publishing to ${registryUrl} with tag latest`);
|
|
5593
5865
|
configure2({ registryUrl, apiKey });
|
|
5594
5866
|
let response;
|
|
5595
5867
|
if (options.org) {
|
|
5596
5868
|
response = await publishOrgSkill(options.org, {
|
|
5597
5869
|
manifest: packageJson2,
|
|
5598
|
-
tarballBase64,
|
|
5870
|
+
tarballBase64: finalTarballBase64,
|
|
5599
5871
|
visibility: options.access
|
|
5600
5872
|
});
|
|
5601
5873
|
} else {
|
|
5602
5874
|
response = await publishSkill({
|
|
5603
5875
|
manifest: packageJson2,
|
|
5604
|
-
tarballBase64,
|
|
5876
|
+
tarballBase64: finalTarballBase64,
|
|
5605
5877
|
visibility: options.access
|
|
5606
5878
|
});
|
|
5607
5879
|
}
|
|
@@ -5627,7 +5899,9 @@ async function publishCommand(options) {
|
|
|
5627
5899
|
`+ @${namespace}/${owner}/${result.skill.name}@${result.version.version}`
|
|
5628
5900
|
);
|
|
5629
5901
|
console.log(`Checksum: ${result.version.checksum}`);
|
|
5630
|
-
console.log(
|
|
5902
|
+
console.log(
|
|
5903
|
+
`Visibility: ${visibilityIcon} ${visibility}${packageJson2.encryption ? " (encrypted)" : ""}`
|
|
5904
|
+
);
|
|
5631
5905
|
if (visibility === "public") {
|
|
5632
5906
|
console.log(
|
|
5633
5907
|
"Note: Public packages cannot be made private. This is irreversible."
|
|
@@ -5678,8 +5952,8 @@ async function removeRegistry(specifier, agents, agentConfigs) {
|
|
|
5678
5952
|
console.error(`Error: Invalid skill specifier: ${specifier}`);
|
|
5679
5953
|
process.exit(1);
|
|
5680
5954
|
}
|
|
5681
|
-
const { namespace, owner, name } = parsed;
|
|
5682
|
-
const fullName = `@${namespace}/${owner}/${name}`;
|
|
5955
|
+
const { namespace, owner, name, subname } = parsed;
|
|
5956
|
+
const fullName = namespace === "github" && subname ? `@github/${owner}/${name}/${subname}` : `@${namespace}/${owner}/${name}`;
|
|
5683
5957
|
console.log(`Removing ${fullName}...`);
|
|
5684
5958
|
const removedFromLockfile = await removeFromLockfile(fullName);
|
|
5685
5959
|
const removedFromManifest = await removeDependency(fullName);
|
|
@@ -5687,13 +5961,21 @@ async function removeRegistry(specifier, agents, agentConfigs) {
|
|
|
5687
5961
|
console.error(`Error: ${fullName} not found in lockfile or pspm.json`);
|
|
5688
5962
|
process.exit(1);
|
|
5689
5963
|
}
|
|
5690
|
-
|
|
5964
|
+
const symlinkName = subname ?? name;
|
|
5965
|
+
await removeAgentSymlinks(symlinkName, {
|
|
5691
5966
|
agents,
|
|
5692
5967
|
projectRoot: process.cwd(),
|
|
5693
5968
|
agentConfigs
|
|
5694
5969
|
});
|
|
5695
5970
|
const skillsDir = getSkillsDir();
|
|
5696
|
-
|
|
5971
|
+
let destDir;
|
|
5972
|
+
if (namespace === "github" && subname) {
|
|
5973
|
+
destDir = join(skillsDir, "_github-registry", owner, name, subname);
|
|
5974
|
+
} else if (namespace === "org") {
|
|
5975
|
+
destDir = join(skillsDir, "_org", owner, name);
|
|
5976
|
+
} else {
|
|
5977
|
+
destDir = join(skillsDir, owner, name);
|
|
5978
|
+
}
|
|
5697
5979
|
try {
|
|
5698
5980
|
await rm(destDir, { recursive: true, force: true });
|
|
5699
5981
|
} catch {
|
|
@@ -5733,7 +6015,9 @@ async function removeByShortName(shortName, agents, agentConfigs) {
|
|
|
5733
6015
|
const registrySkills = await listLockfileSkills();
|
|
5734
6016
|
const foundRegistry = registrySkills.find((s) => {
|
|
5735
6017
|
const parsed = parseRegistrySpecifier2(s.name);
|
|
5736
|
-
|
|
6018
|
+
if (!parsed) return false;
|
|
6019
|
+
const effectiveName = parsed.subname ?? parsed.name;
|
|
6020
|
+
return effectiveName === shortName;
|
|
5737
6021
|
});
|
|
5738
6022
|
if (foundRegistry) {
|
|
5739
6023
|
await removeRegistry(foundRegistry.name, agents, agentConfigs);
|
|
@@ -5835,6 +6119,396 @@ function formatDownloads(count) {
|
|
|
5835
6119
|
return String(count);
|
|
5836
6120
|
}
|
|
5837
6121
|
|
|
6122
|
+
// src/commands/skill-list.ts
|
|
6123
|
+
init_api_client();
|
|
6124
|
+
init_config();
|
|
6125
|
+
function parseListSpecifier(specifier) {
|
|
6126
|
+
const match = specifier.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
|
|
6127
|
+
if (!match) return null;
|
|
6128
|
+
return {
|
|
6129
|
+
ownerType: match[1],
|
|
6130
|
+
ownerName: match[2],
|
|
6131
|
+
listName: match[3]
|
|
6132
|
+
};
|
|
6133
|
+
}
|
|
6134
|
+
function extractErrorMessage(text) {
|
|
6135
|
+
try {
|
|
6136
|
+
const json = JSON.parse(text);
|
|
6137
|
+
if (typeof json.message === "string") return json.message;
|
|
6138
|
+
if (typeof json.error === "string") return json.error;
|
|
6139
|
+
if (typeof json.error?.message === "string") return json.error.message;
|
|
6140
|
+
} catch {
|
|
6141
|
+
}
|
|
6142
|
+
return text;
|
|
6143
|
+
}
|
|
6144
|
+
async function getBaseUrl() {
|
|
6145
|
+
const config2 = await resolveConfig();
|
|
6146
|
+
return config2.registryUrl.replace(/\/api\/skills\/?$/, "");
|
|
6147
|
+
}
|
|
6148
|
+
async function getAuthHeaders() {
|
|
6149
|
+
const apiKey = await requireApiKey();
|
|
6150
|
+
return {
|
|
6151
|
+
"Content-Type": "application/json",
|
|
6152
|
+
Authorization: `Bearer ${apiKey}`
|
|
6153
|
+
};
|
|
6154
|
+
}
|
|
6155
|
+
async function getOptionalAuthHeaders() {
|
|
6156
|
+
const config2 = await resolveConfig();
|
|
6157
|
+
const apiKey = getTokenForRegistry(config2, config2.registryUrl);
|
|
6158
|
+
const headers = {
|
|
6159
|
+
"Content-Type": "application/json"
|
|
6160
|
+
};
|
|
6161
|
+
if (apiKey) {
|
|
6162
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
6163
|
+
}
|
|
6164
|
+
return headers;
|
|
6165
|
+
}
|
|
6166
|
+
async function skillListList(options) {
|
|
6167
|
+
const baseUrl = await getBaseUrl();
|
|
6168
|
+
const config2 = await resolveConfig();
|
|
6169
|
+
let path;
|
|
6170
|
+
if (options.org) {
|
|
6171
|
+
path = `/api/skill-lists/lists/@org/${options.org}`;
|
|
6172
|
+
} else {
|
|
6173
|
+
if (!config2.username) {
|
|
6174
|
+
console.error("Error: Not logged in. Run `pspm login` first.");
|
|
6175
|
+
process.exit(1);
|
|
6176
|
+
}
|
|
6177
|
+
path = `/api/skill-lists/lists/@user/${config2.username}`;
|
|
6178
|
+
}
|
|
6179
|
+
const headers = await getOptionalAuthHeaders();
|
|
6180
|
+
const response = await fetch(`${baseUrl}${path}`, { headers });
|
|
6181
|
+
if (!response.ok) {
|
|
6182
|
+
const errorText = await response.text();
|
|
6183
|
+
console.error(
|
|
6184
|
+
`Error: Failed to list skill lists (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6185
|
+
);
|
|
6186
|
+
process.exit(1);
|
|
6187
|
+
}
|
|
6188
|
+
const lists = await response.json();
|
|
6189
|
+
if (options.json) {
|
|
6190
|
+
console.log(JSON.stringify(lists, null, 2));
|
|
6191
|
+
return;
|
|
6192
|
+
}
|
|
6193
|
+
if (lists.length === 0) {
|
|
6194
|
+
console.log("No skill lists found.");
|
|
6195
|
+
return;
|
|
6196
|
+
}
|
|
6197
|
+
const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
|
|
6198
|
+
console.log(`
|
|
6199
|
+
Skill lists for ${ownerLabel}:
|
|
6200
|
+
`);
|
|
6201
|
+
console.log(
|
|
6202
|
+
`${"Name".padEnd(35)} ${"Skills".padEnd(8)} ${"Visibility".padEnd(12)} Description`
|
|
6203
|
+
);
|
|
6204
|
+
console.log("-".repeat(90));
|
|
6205
|
+
for (const list2 of lists) {
|
|
6206
|
+
const desc = list2.description ? list2.description.slice(0, 30) : "";
|
|
6207
|
+
console.log(
|
|
6208
|
+
`${list2.name.slice(0, 33).padEnd(35)} ${String(list2.itemCount).padEnd(8)} ${list2.visibility.padEnd(12)} ${desc}`
|
|
6209
|
+
);
|
|
6210
|
+
}
|
|
6211
|
+
console.log(`
|
|
6212
|
+
${lists.length} list(s)`);
|
|
6213
|
+
}
|
|
6214
|
+
async function skillListCreate(name, options) {
|
|
6215
|
+
const baseUrl = await getBaseUrl();
|
|
6216
|
+
const headers = await getAuthHeaders();
|
|
6217
|
+
const config2 = await resolveConfig();
|
|
6218
|
+
const visibility = options.visibility || "private";
|
|
6219
|
+
if (visibility !== "public" && visibility !== "private") {
|
|
6220
|
+
console.error('Error: --visibility must be "public" or "private"');
|
|
6221
|
+
process.exit(1);
|
|
6222
|
+
}
|
|
6223
|
+
let path;
|
|
6224
|
+
if (options.org) {
|
|
6225
|
+
path = `/api/skill-lists/lists/@org/${options.org}`;
|
|
6226
|
+
} else {
|
|
6227
|
+
if (!config2.username) {
|
|
6228
|
+
console.error("Error: Not logged in. Run `pspm login` first.");
|
|
6229
|
+
process.exit(1);
|
|
6230
|
+
}
|
|
6231
|
+
path = `/api/skill-lists/lists/@user/${config2.username}`;
|
|
6232
|
+
}
|
|
6233
|
+
const body = { name, visibility };
|
|
6234
|
+
if (options.description) {
|
|
6235
|
+
body.description = options.description;
|
|
6236
|
+
}
|
|
6237
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6238
|
+
method: "POST",
|
|
6239
|
+
headers,
|
|
6240
|
+
body: JSON.stringify(body)
|
|
6241
|
+
});
|
|
6242
|
+
if (!response.ok) {
|
|
6243
|
+
const errorText = await response.text();
|
|
6244
|
+
console.error(
|
|
6245
|
+
`Error: Failed to create list (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6246
|
+
);
|
|
6247
|
+
process.exit(1);
|
|
6248
|
+
}
|
|
6249
|
+
const list2 = await response.json();
|
|
6250
|
+
const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
|
|
6251
|
+
console.log(`Created list: ${ownerLabel}/${list2.name} (${list2.visibility})`);
|
|
6252
|
+
console.log(
|
|
6253
|
+
`
|
|
6254
|
+
Install command: pspm skill-list install ${ownerLabel}/${list2.name}`
|
|
6255
|
+
);
|
|
6256
|
+
}
|
|
6257
|
+
async function skillListShow(specifier, options) {
|
|
6258
|
+
const parsed = parseListSpecifier(specifier);
|
|
6259
|
+
if (!parsed) {
|
|
6260
|
+
console.error(
|
|
6261
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6262
|
+
);
|
|
6263
|
+
process.exit(1);
|
|
6264
|
+
}
|
|
6265
|
+
const config2 = await resolveConfig();
|
|
6266
|
+
configure2({
|
|
6267
|
+
registryUrl: config2.registryUrl,
|
|
6268
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
6269
|
+
});
|
|
6270
|
+
const response = await fetchSkillList(
|
|
6271
|
+
parsed.ownerType,
|
|
6272
|
+
parsed.ownerName,
|
|
6273
|
+
parsed.listName
|
|
6274
|
+
);
|
|
6275
|
+
if (response.status !== 200 || !response.data) {
|
|
6276
|
+
const errorMsg = response.status === 404 ? `List "${specifier}" not found or is private.` : response.error || "Failed to fetch list";
|
|
6277
|
+
console.error(`Error: ${errorMsg}`);
|
|
6278
|
+
process.exit(1);
|
|
6279
|
+
}
|
|
6280
|
+
const list2 = response.data;
|
|
6281
|
+
if (options.json) {
|
|
6282
|
+
console.log(JSON.stringify(list2, null, 2));
|
|
6283
|
+
return;
|
|
6284
|
+
}
|
|
6285
|
+
console.log(`
|
|
6286
|
+
${list2.name}`);
|
|
6287
|
+
if (list2.description) {
|
|
6288
|
+
console.log(` ${list2.description}`);
|
|
6289
|
+
}
|
|
6290
|
+
console.log(` Visibility: ${list2.visibility}`);
|
|
6291
|
+
console.log(` Owner: @${list2.ownerType}/${list2.ownerName}`);
|
|
6292
|
+
console.log(` Skills: ${list2.items.length}`);
|
|
6293
|
+
if (list2.items.length > 0) {
|
|
6294
|
+
console.log("");
|
|
6295
|
+
for (const item of list2.items) {
|
|
6296
|
+
const ns = item.namespace === "org" ? "org" : "user";
|
|
6297
|
+
const spec = `@${ns}/${item.ownerName}/${item.skillName}`;
|
|
6298
|
+
const ver = item.pinnedVersion ? `@${item.pinnedVersion}` : "";
|
|
6299
|
+
console.log(` - ${spec}${ver}`);
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6302
|
+
console.log(`
|
|
6303
|
+
Install all: pspm skill-list install ${specifier}`);
|
|
6304
|
+
}
|
|
6305
|
+
async function skillListDelete(specifier) {
|
|
6306
|
+
const parsed = parseListSpecifier(specifier);
|
|
6307
|
+
if (!parsed) {
|
|
6308
|
+
console.error(
|
|
6309
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6310
|
+
);
|
|
6311
|
+
process.exit(1);
|
|
6312
|
+
}
|
|
6313
|
+
const baseUrl = await getBaseUrl();
|
|
6314
|
+
const headers = await getAuthHeaders();
|
|
6315
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
|
|
6316
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6317
|
+
method: "DELETE",
|
|
6318
|
+
headers
|
|
6319
|
+
});
|
|
6320
|
+
if (!response.ok) {
|
|
6321
|
+
const errorText = await response.text();
|
|
6322
|
+
if (response.status === 404) {
|
|
6323
|
+
console.error(`Error: List "${specifier}" not found.`);
|
|
6324
|
+
} else {
|
|
6325
|
+
console.error(
|
|
6326
|
+
`Error: Failed to delete list (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6327
|
+
);
|
|
6328
|
+
}
|
|
6329
|
+
process.exit(1);
|
|
6330
|
+
}
|
|
6331
|
+
console.log(`Deleted list: ${specifier}`);
|
|
6332
|
+
}
|
|
6333
|
+
async function skillListUpdate(specifier, options) {
|
|
6334
|
+
const parsed = parseListSpecifier(specifier);
|
|
6335
|
+
if (!parsed) {
|
|
6336
|
+
console.error(
|
|
6337
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6338
|
+
);
|
|
6339
|
+
process.exit(1);
|
|
6340
|
+
}
|
|
6341
|
+
if (!options.description && !options.visibility) {
|
|
6342
|
+
console.error(
|
|
6343
|
+
"Error: Provide at least one of --description or --visibility"
|
|
6344
|
+
);
|
|
6345
|
+
process.exit(1);
|
|
6346
|
+
}
|
|
6347
|
+
if (options.visibility && options.visibility !== "public" && options.visibility !== "private") {
|
|
6348
|
+
console.error('Error: --visibility must be "public" or "private"');
|
|
6349
|
+
process.exit(1);
|
|
6350
|
+
}
|
|
6351
|
+
const baseUrl = await getBaseUrl();
|
|
6352
|
+
const headers = await getAuthHeaders();
|
|
6353
|
+
const body = {};
|
|
6354
|
+
if (options.description !== void 0) {
|
|
6355
|
+
body.description = options.description;
|
|
6356
|
+
}
|
|
6357
|
+
if (options.visibility) {
|
|
6358
|
+
body.visibility = options.visibility;
|
|
6359
|
+
}
|
|
6360
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
|
|
6361
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6362
|
+
method: "PUT",
|
|
6363
|
+
headers,
|
|
6364
|
+
body: JSON.stringify(body)
|
|
6365
|
+
});
|
|
6366
|
+
if (!response.ok) {
|
|
6367
|
+
const errorText = await response.text();
|
|
6368
|
+
console.error(
|
|
6369
|
+
`Error: Failed to update list (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6370
|
+
);
|
|
6371
|
+
process.exit(1);
|
|
6372
|
+
}
|
|
6373
|
+
console.log(`Updated list: ${specifier}`);
|
|
6374
|
+
}
|
|
6375
|
+
async function skillListAddSkill(specifier, skillSpecifiers, options) {
|
|
6376
|
+
const parsed = parseListSpecifier(specifier);
|
|
6377
|
+
if (!parsed) {
|
|
6378
|
+
console.error(
|
|
6379
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6380
|
+
);
|
|
6381
|
+
process.exit(1);
|
|
6382
|
+
}
|
|
6383
|
+
const baseUrl = await getBaseUrl();
|
|
6384
|
+
const headers = await getAuthHeaders();
|
|
6385
|
+
const config2 = await resolveConfig();
|
|
6386
|
+
configure2({
|
|
6387
|
+
registryUrl: config2.registryUrl,
|
|
6388
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
6389
|
+
});
|
|
6390
|
+
for (const skillSpec of skillSpecifiers) {
|
|
6391
|
+
const skillId = await resolveSkillId(baseUrl, headers, skillSpec);
|
|
6392
|
+
if (!skillId) {
|
|
6393
|
+
console.error(`Error: Skill "${skillSpec}" not found.`);
|
|
6394
|
+
continue;
|
|
6395
|
+
}
|
|
6396
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items`;
|
|
6397
|
+
const addBody = { skillId };
|
|
6398
|
+
if (options.note) {
|
|
6399
|
+
addBody.note = options.note;
|
|
6400
|
+
}
|
|
6401
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6402
|
+
method: "POST",
|
|
6403
|
+
headers,
|
|
6404
|
+
body: JSON.stringify(addBody)
|
|
6405
|
+
});
|
|
6406
|
+
if (!response.ok) {
|
|
6407
|
+
const errorText = await response.text();
|
|
6408
|
+
if (response.status === 409) {
|
|
6409
|
+
console.log(`Already in list: ${skillSpec}`);
|
|
6410
|
+
} else {
|
|
6411
|
+
console.error(
|
|
6412
|
+
`Error: Failed to add "${skillSpec}" (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6413
|
+
);
|
|
6414
|
+
}
|
|
6415
|
+
continue;
|
|
6416
|
+
}
|
|
6417
|
+
console.log(`Added: ${skillSpec}`);
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6420
|
+
async function skillListRemoveSkill(specifier, skillSpecifier) {
|
|
6421
|
+
const parsed = parseListSpecifier(specifier);
|
|
6422
|
+
if (!parsed) {
|
|
6423
|
+
console.error(
|
|
6424
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6425
|
+
);
|
|
6426
|
+
process.exit(1);
|
|
6427
|
+
}
|
|
6428
|
+
const config2 = await resolveConfig();
|
|
6429
|
+
configure2({
|
|
6430
|
+
registryUrl: config2.registryUrl,
|
|
6431
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
6432
|
+
});
|
|
6433
|
+
const listResponse = await fetchSkillList(
|
|
6434
|
+
parsed.ownerType,
|
|
6435
|
+
parsed.ownerName,
|
|
6436
|
+
parsed.listName
|
|
6437
|
+
);
|
|
6438
|
+
if (listResponse.status !== 200 || !listResponse.data) {
|
|
6439
|
+
console.error(`Error: List "${specifier}" not found.`);
|
|
6440
|
+
process.exit(1);
|
|
6441
|
+
}
|
|
6442
|
+
const item = listResponse.data.items.find((i) => {
|
|
6443
|
+
const ns = i.namespace === "org" ? "org" : "user";
|
|
6444
|
+
const fullSpec = `@${ns}/${i.ownerName}/${i.skillName}`;
|
|
6445
|
+
return fullSpec === skillSpecifier || i.skillName === skillSpecifier || `${i.ownerName}/${i.skillName}` === skillSpecifier;
|
|
6446
|
+
});
|
|
6447
|
+
if (!item) {
|
|
6448
|
+
console.error(
|
|
6449
|
+
`Error: Skill "${skillSpecifier}" not found in list "${specifier}".`
|
|
6450
|
+
);
|
|
6451
|
+
process.exit(1);
|
|
6452
|
+
}
|
|
6453
|
+
const baseUrl = await getBaseUrl();
|
|
6454
|
+
const headers = await getAuthHeaders();
|
|
6455
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items/${item.id}`;
|
|
6456
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6457
|
+
method: "DELETE",
|
|
6458
|
+
headers
|
|
6459
|
+
});
|
|
6460
|
+
if (!response.ok) {
|
|
6461
|
+
const errorText = await response.text();
|
|
6462
|
+
console.error(
|
|
6463
|
+
`Error: Failed to remove skill (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6464
|
+
);
|
|
6465
|
+
process.exit(1);
|
|
6466
|
+
}
|
|
6467
|
+
console.log(`Removed: ${skillSpecifier} from ${specifier}`);
|
|
6468
|
+
}
|
|
6469
|
+
async function skillListInstall(specifier, options) {
|
|
6470
|
+
const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
6471
|
+
await install2([], {
|
|
6472
|
+
list: specifier,
|
|
6473
|
+
agent: options.agent,
|
|
6474
|
+
yes: options.yes,
|
|
6475
|
+
global: options.global,
|
|
6476
|
+
dir: options.dir
|
|
6477
|
+
});
|
|
6478
|
+
}
|
|
6479
|
+
async function resolveSkillId(baseUrl, headers, specifier) {
|
|
6480
|
+
const match = specifier.match(/^@(user|org|github)\/(.+)$/);
|
|
6481
|
+
let search2;
|
|
6482
|
+
let namespace;
|
|
6483
|
+
if (match) {
|
|
6484
|
+
namespace = match[1];
|
|
6485
|
+
const parts = match[2].split("/");
|
|
6486
|
+
search2 = parts[parts.length - 1];
|
|
6487
|
+
} else {
|
|
6488
|
+
search2 = specifier;
|
|
6489
|
+
}
|
|
6490
|
+
const params = new URLSearchParams({ search: search2, limit: "5" });
|
|
6491
|
+
if (namespace) {
|
|
6492
|
+
params.set("namespace", namespace);
|
|
6493
|
+
}
|
|
6494
|
+
const response = await fetch(`${baseUrl}/api/skills/-/explore?${params}`, {
|
|
6495
|
+
headers
|
|
6496
|
+
});
|
|
6497
|
+
if (!response.ok) return null;
|
|
6498
|
+
const data = await response.json();
|
|
6499
|
+
if (!data.skills || data.skills.length === 0) return null;
|
|
6500
|
+
if (match) {
|
|
6501
|
+
const parts = match[2].split("/");
|
|
6502
|
+
const skillName = parts[parts.length - 1];
|
|
6503
|
+
const ownerName = parts.length >= 2 ? parts[parts.length - 2] : void 0;
|
|
6504
|
+
const exact = data.skills.find(
|
|
6505
|
+
(s) => s.name === skillName && (!ownerName || s.username === ownerName) && (!namespace || s.namespace === namespace)
|
|
6506
|
+
);
|
|
6507
|
+
if (exact) return exact.id;
|
|
6508
|
+
}
|
|
6509
|
+
return data.skills[0].id;
|
|
6510
|
+
}
|
|
6511
|
+
|
|
5838
6512
|
// src/commands/unpublish.ts
|
|
5839
6513
|
init_api_client();
|
|
5840
6514
|
init_config();
|
|
@@ -6225,6 +6899,28 @@ configCmd.command("init").description("Create a .pspmrc file in the current dire
|
|
|
6225
6899
|
registry: options.registry
|
|
6226
6900
|
});
|
|
6227
6901
|
});
|
|
6902
|
+
configCmd.command("set-encryption-key <scope> <passphrase>").description(
|
|
6903
|
+
"Set encryption key for a scope (e.g., pspm config set-encryption-key @user/alice my-secret)"
|
|
6904
|
+
).action(async (scope, passphrase) => {
|
|
6905
|
+
const { setEncryptionKey: setEncryptionKey2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6906
|
+
await setEncryptionKey2(scope, passphrase);
|
|
6907
|
+
console.log(`Encryption key set for scope "${scope}"`);
|
|
6908
|
+
});
|
|
6909
|
+
configCmd.command("remove-encryption-key <scope>").description("Remove encryption key for a scope").action(async (scope) => {
|
|
6910
|
+
const { removeEncryptionKey: removeEncryptionKey2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6911
|
+
await removeEncryptionKey2(scope);
|
|
6912
|
+
console.log(`Encryption key removed for scope "${scope}"`);
|
|
6913
|
+
});
|
|
6914
|
+
configCmd.command("get-encryption-key <scope>").description("Check if an encryption key is set for a scope").action(async (scope) => {
|
|
6915
|
+
const { getEncryptionKey: getEncryptionKey2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6916
|
+
const key = await getEncryptionKey2(scope);
|
|
6917
|
+
if (key) {
|
|
6918
|
+
const masked = `${key.substring(0, 4)}***`;
|
|
6919
|
+
console.log(`Encryption key for "${scope}": ${masked} (set)`);
|
|
6920
|
+
} else {
|
|
6921
|
+
console.log(`No encryption key set for "${scope}"`);
|
|
6922
|
+
}
|
|
6923
|
+
});
|
|
6228
6924
|
program.command("upgrade").description("Upgrade pspm to the latest version").action(async () => {
|
|
6229
6925
|
await upgrade();
|
|
6230
6926
|
});
|
|
@@ -6367,6 +7063,73 @@ program.command("deprecate <specifier> [message]").description(
|
|
|
6367
7063
|
).option("--undo", "Remove deprecation status").action(async (specifier, message, options) => {
|
|
6368
7064
|
await deprecate(specifier, message, { undo: options.undo });
|
|
6369
7065
|
});
|
|
7066
|
+
var skillListCmd = program.command("skill-list").description("Manage skill lists (collections of skills)");
|
|
7067
|
+
skillListCmd.command("list").description("List your skill lists").option("--org <orgname>", "List organization skill lists").option("--json", "Output as JSON").action(async (options) => {
|
|
7068
|
+
await skillListList({ org: options.org, json: options.json });
|
|
7069
|
+
});
|
|
7070
|
+
skillListCmd.command("create <name>").description("Create a new skill list").option("-d, --description <description>", "List description").option(
|
|
7071
|
+
"--visibility <visibility>",
|
|
7072
|
+
"Visibility: private or public",
|
|
7073
|
+
"private"
|
|
7074
|
+
).option("--org <orgname>", "Create under an organization").action(async (name, options) => {
|
|
7075
|
+
await skillListCreate(name, {
|
|
7076
|
+
description: options.description,
|
|
7077
|
+
visibility: options.visibility,
|
|
7078
|
+
org: options.org
|
|
7079
|
+
});
|
|
7080
|
+
});
|
|
7081
|
+
skillListCmd.command("show <specifier>").description("Show skill list details (e.g. @user/alice/my-tools)").option("--json", "Output as JSON").action(async (specifier, options) => {
|
|
7082
|
+
await skillListShow(specifier, { json: options.json });
|
|
7083
|
+
});
|
|
7084
|
+
skillListCmd.command("delete <specifier>").description("Delete a skill list").action(async (specifier) => {
|
|
7085
|
+
await skillListDelete(specifier);
|
|
7086
|
+
});
|
|
7087
|
+
skillListCmd.command("update <specifier>").description("Update skill list metadata").option("-d, --description <description>", "New description").option("--visibility <visibility>", "New visibility: private or public").action(async (specifier, options) => {
|
|
7088
|
+
await skillListUpdate(specifier, {
|
|
7089
|
+
description: options.description,
|
|
7090
|
+
visibility: options.visibility
|
|
7091
|
+
});
|
|
7092
|
+
});
|
|
7093
|
+
skillListCmd.command("add-skill <specifier> <skills...>").description(
|
|
7094
|
+
"Add skills to a list (e.g. pspm skill-list add-skill @user/me/my-list @user/alice/tool)"
|
|
7095
|
+
).option("--note <note>", "Note for the added skill").action(async (specifier, skills, options) => {
|
|
7096
|
+
await skillListAddSkill(specifier, skills, {
|
|
7097
|
+
note: options.note
|
|
7098
|
+
});
|
|
7099
|
+
});
|
|
7100
|
+
skillListCmd.command("remove-skill <specifier> <skill>").description("Remove a skill from a list").action(async (specifier, skill) => {
|
|
7101
|
+
await skillListRemoveSkill(specifier, skill);
|
|
7102
|
+
});
|
|
7103
|
+
skillListCmd.command("install <specifier>").description(
|
|
7104
|
+
"Install all skills from a list (e.g. pspm skill-list install @user/alice/my-tools)"
|
|
7105
|
+
).option(
|
|
7106
|
+
"--agent <agents>",
|
|
7107
|
+
'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
|
|
7108
|
+
).option("--dir <path>", "Install skills to a specific directory").option("-g, --global", "Install to user home directory instead of project").option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifier, options) => {
|
|
7109
|
+
await skillListInstall(specifier, {
|
|
7110
|
+
agent: options.agent,
|
|
7111
|
+
yes: options.yes,
|
|
7112
|
+
global: options.global,
|
|
7113
|
+
dir: options.dir
|
|
7114
|
+
});
|
|
7115
|
+
});
|
|
7116
|
+
var notebookCmd = program.command("notebook").description("Manage AnyT notebooks");
|
|
7117
|
+
notebookCmd.command("upload <file>").description("Upload a .anyt notebook").option("--org <orgname>", "Upload to an organization").option(
|
|
7118
|
+
"--visibility <visibility>",
|
|
7119
|
+
"Visibility: private, team, or public",
|
|
7120
|
+
"private"
|
|
7121
|
+
).option("--description <description>", "Notebook description").action(async (file, options) => {
|
|
7122
|
+
await notebookUpload(file, options);
|
|
7123
|
+
});
|
|
7124
|
+
notebookCmd.command("list").description("List notebooks").option("--org <orgname>", "List organization notebooks").action(async (options) => {
|
|
7125
|
+
await notebookList(options);
|
|
7126
|
+
});
|
|
7127
|
+
notebookCmd.command("download <id>").description("Download a notebook by ID").option("-o, --output <path>", "Output file path").action(async (id, options) => {
|
|
7128
|
+
await notebookDownload(id, options.output);
|
|
7129
|
+
});
|
|
7130
|
+
notebookCmd.command("delete <id>").description("Delete a notebook by ID").action(async (id) => {
|
|
7131
|
+
await notebookDelete(id);
|
|
7132
|
+
});
|
|
6370
7133
|
await program.parseAsync();
|
|
6371
7134
|
var executedCommand = program.args[0];
|
|
6372
7135
|
if (executedCommand !== "upgrade") {
|