@anytio/pspm 0.10.0 → 0.12.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 +42 -1
- package/CLI_GUIDE.md +4 -0
- package/README.md +8 -0
- package/dist/index.js +1780 -1027
- package/dist/index.js.map +1 -1
- package/package.json +10 -8
package/dist/index.js
CHANGED
|
@@ -8,7 +8,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';
|
|
@@ -37,6 +37,9 @@ function getConfig() {
|
|
|
37
37
|
}
|
|
38
38
|
return config;
|
|
39
39
|
}
|
|
40
|
+
function isConfigured() {
|
|
41
|
+
return config !== null;
|
|
42
|
+
}
|
|
40
43
|
async function customFetch(url, options) {
|
|
41
44
|
const { baseUrl, apiKey } = getConfig();
|
|
42
45
|
const fullUrl = `${baseUrl}${url}`;
|
|
@@ -66,18 +69,42 @@ async function customFetch(url, options) {
|
|
|
66
69
|
headers: response.headers
|
|
67
70
|
};
|
|
68
71
|
}
|
|
69
|
-
var config;
|
|
72
|
+
var config, SDKError;
|
|
70
73
|
var init_fetcher = __esm({
|
|
71
74
|
"src/sdk/fetcher.ts"() {
|
|
72
75
|
config = null;
|
|
76
|
+
SDKError = class extends Error {
|
|
77
|
+
constructor(message, status, body) {
|
|
78
|
+
super(message);
|
|
79
|
+
this.status = status;
|
|
80
|
+
this.body = body;
|
|
81
|
+
this.name = "SDKError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
73
84
|
}
|
|
74
85
|
});
|
|
75
86
|
|
|
76
87
|
// src/sdk/generated/index.ts
|
|
77
|
-
var getMeUrl, me, getExplorePublicSkillsUrl, explorePublicSkills, getDeleteSkillUrl, deleteSkill, getListSkillVersionsUrl, listSkillVersions, getGetSkillVersionUrl, getSkillVersion, getDeleteSkillVersionUrl, deleteSkillVersion, getPublishSkillUrl, publishSkill, getListOrgSkillVersionsUrl, listOrgSkillVersions, getGetOrgSkillVersionUrl, getOrgSkillVersion, getPublishOrgSkillUrl, publishOrgSkill;
|
|
88
|
+
var getExchangeCliTokenUrl, exchangeCliToken, getMeUrl, me, getExplorePublicSkillsUrl, explorePublicSkills, getListMySkillsUrl, listMySkills, getListUserSkillsUrl, listUserSkills, getGetSkillUrl, getSkill, getDeleteSkillUrl, deleteSkill, getListSkillVersionsUrl, listSkillVersions, getGetSkillVersionUrl, getSkillVersion, getDeleteSkillVersionUrl, deleteSkillVersion, getPublishSkillUrl, publishSkill, getListOrgSkillVersionsUrl, listOrgSkillVersions, getGetOrgSkillVersionUrl, getOrgSkillVersion, getPublishOrgSkillUrl, publishOrgSkill;
|
|
78
89
|
var init_generated = __esm({
|
|
79
90
|
"src/sdk/generated/index.ts"() {
|
|
80
91
|
init_fetcher();
|
|
92
|
+
getExchangeCliTokenUrl = () => {
|
|
93
|
+
return `/api/api-keys/cli-token-exchange`;
|
|
94
|
+
};
|
|
95
|
+
exchangeCliToken = async (exchangeCliTokenInput, options) => {
|
|
96
|
+
return customFetch(
|
|
97
|
+
getExchangeCliTokenUrl(),
|
|
98
|
+
{
|
|
99
|
+
...options,
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: { "Content-Type": "application/json", ...options?.headers },
|
|
102
|
+
body: JSON.stringify(
|
|
103
|
+
exchangeCliTokenInput
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
};
|
|
81
108
|
getMeUrl = () => {
|
|
82
109
|
return `/api/skills/-/me`;
|
|
83
110
|
};
|
|
@@ -109,6 +136,42 @@ var init_generated = __esm({
|
|
|
109
136
|
}
|
|
110
137
|
);
|
|
111
138
|
};
|
|
139
|
+
getListMySkillsUrl = () => {
|
|
140
|
+
return `/api/skills/-/mine`;
|
|
141
|
+
};
|
|
142
|
+
listMySkills = async (options) => {
|
|
143
|
+
return customFetch(
|
|
144
|
+
getListMySkillsUrl(),
|
|
145
|
+
{
|
|
146
|
+
...options,
|
|
147
|
+
method: "GET"
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
getListUserSkillsUrl = (username) => {
|
|
152
|
+
return `/api/skills/@user/${username}`;
|
|
153
|
+
};
|
|
154
|
+
listUserSkills = async (username, options) => {
|
|
155
|
+
return customFetch(
|
|
156
|
+
getListUserSkillsUrl(username),
|
|
157
|
+
{
|
|
158
|
+
...options,
|
|
159
|
+
method: "GET"
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
getGetSkillUrl = (username, name) => {
|
|
164
|
+
return `/api/skills/@user/${username}/${name}`;
|
|
165
|
+
};
|
|
166
|
+
getSkill = async (username, name, options) => {
|
|
167
|
+
return customFetch(
|
|
168
|
+
getGetSkillUrl(username, name),
|
|
169
|
+
{
|
|
170
|
+
...options,
|
|
171
|
+
method: "GET"
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
};
|
|
112
175
|
getDeleteSkillUrl = (username, name) => {
|
|
113
176
|
return `/api/skills/@user/${username}/${name}`;
|
|
114
177
|
};
|
|
@@ -217,6 +280,33 @@ var init_generated = __esm({
|
|
|
217
280
|
});
|
|
218
281
|
|
|
219
282
|
// src/api-client.ts
|
|
283
|
+
var api_client_exports = {};
|
|
284
|
+
__export(api_client_exports, {
|
|
285
|
+
SDKError: () => SDKError,
|
|
286
|
+
changeSkillAccess: () => changeSkillAccess,
|
|
287
|
+
configure: () => configure2,
|
|
288
|
+
deleteSkill: () => deleteSkill,
|
|
289
|
+
deleteSkillVersion: () => deleteSkillVersion,
|
|
290
|
+
deprecateSkillVersion: () => deprecateSkillVersion,
|
|
291
|
+
exchangeCliToken: () => exchangeCliToken,
|
|
292
|
+
fetchSkillList: () => fetchSkillList,
|
|
293
|
+
getConfig: () => getConfig,
|
|
294
|
+
getGithubSkillVersion: () => getGithubSkillVersion,
|
|
295
|
+
getOrgSkillVersion: () => getOrgSkillVersion,
|
|
296
|
+
getSkill: () => getSkill,
|
|
297
|
+
getSkillVersion: () => getSkillVersion,
|
|
298
|
+
isConfigured: () => isConfigured,
|
|
299
|
+
listGithubSkillVersions: () => listGithubSkillVersions,
|
|
300
|
+
listMySkills: () => listMySkills,
|
|
301
|
+
listOrgSkillVersions: () => listOrgSkillVersions,
|
|
302
|
+
listSkillVersions: () => listSkillVersions,
|
|
303
|
+
listUserSkills: () => listUserSkills,
|
|
304
|
+
me: () => me,
|
|
305
|
+
publishOrgSkill: () => publishOrgSkill,
|
|
306
|
+
publishSkill: () => publishSkill,
|
|
307
|
+
undeprecateSkillVersion: () => undeprecateSkillVersion,
|
|
308
|
+
whoamiRequest: () => whoamiRequest
|
|
309
|
+
});
|
|
220
310
|
function registryUrlToBaseUrl(registryUrl) {
|
|
221
311
|
return registryUrl.replace(/\/api\/skills\/?$/, "");
|
|
222
312
|
}
|
|
@@ -298,6 +388,32 @@ async function undeprecateSkillVersion(username, skillName, version3) {
|
|
|
298
388
|
};
|
|
299
389
|
}
|
|
300
390
|
}
|
|
391
|
+
async function fetchSkillList(ownerType, ownerName, listName) {
|
|
392
|
+
const config2 = getConfig();
|
|
393
|
+
try {
|
|
394
|
+
const response = await fetch(
|
|
395
|
+
`${config2.baseUrl}/api/skill-lists/lists/@${ownerType}/${ownerName}/${listName}`,
|
|
396
|
+
{
|
|
397
|
+
method: "GET",
|
|
398
|
+
headers: {
|
|
399
|
+
"Content-Type": "application/json",
|
|
400
|
+
...config2.apiKey ? { Authorization: `Bearer ${config2.apiKey}` } : {}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
if (!response.ok) {
|
|
405
|
+
const error = await response.text();
|
|
406
|
+
return { status: response.status, error };
|
|
407
|
+
}
|
|
408
|
+
const data = await response.json();
|
|
409
|
+
return { status: response.status, data };
|
|
410
|
+
} catch (error) {
|
|
411
|
+
return {
|
|
412
|
+
status: 500,
|
|
413
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
301
417
|
async function listGithubSkillVersions(owner, repo, name) {
|
|
302
418
|
const config2 = getConfig();
|
|
303
419
|
try {
|
|
@@ -852,7 +968,9 @@ function validateManifest(manifest) {
|
|
|
852
968
|
if (!manifest.version) {
|
|
853
969
|
return { valid: false, error: "Manifest must have a 'version' field" };
|
|
854
970
|
}
|
|
855
|
-
|
|
971
|
+
const parts = manifest.name.split("/");
|
|
972
|
+
const bareName = parts[parts.length - 1];
|
|
973
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(bareName)) {
|
|
856
974
|
return {
|
|
857
975
|
valid: false,
|
|
858
976
|
error: "Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores"
|
|
@@ -3213,690 +3331,834 @@ var init_add = __esm({
|
|
|
3213
3331
|
}
|
|
3214
3332
|
});
|
|
3215
3333
|
|
|
3216
|
-
// src/commands/
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3334
|
+
// src/commands/install.ts
|
|
3335
|
+
var install_exports = {};
|
|
3336
|
+
__export(install_exports, {
|
|
3337
|
+
install: () => install
|
|
3338
|
+
});
|
|
3339
|
+
function getCacheFilePath(cacheDir, integrity) {
|
|
3340
|
+
const match = integrity.match(/^sha256-(.+)$/);
|
|
3341
|
+
if (!match) {
|
|
3342
|
+
throw new Error(`Invalid integrity format: ${integrity}`);
|
|
3343
|
+
}
|
|
3344
|
+
const base64Hash = match[1];
|
|
3345
|
+
const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
|
|
3346
|
+
return join(cacheDir, `sha256-${hexHash}.tgz`);
|
|
3222
3347
|
}
|
|
3223
|
-
async function
|
|
3348
|
+
async function readFromCache(cacheDir, integrity) {
|
|
3224
3349
|
try {
|
|
3225
|
-
const
|
|
3226
|
-
const
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3350
|
+
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
3351
|
+
const data = await readFile(cachePath);
|
|
3352
|
+
const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
|
|
3353
|
+
if (actualIntegrity !== integrity) {
|
|
3354
|
+
await rm(cachePath, { force: true });
|
|
3355
|
+
return null;
|
|
3230
3356
|
}
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3357
|
+
return data;
|
|
3358
|
+
} catch {
|
|
3359
|
+
return null;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
async function writeToCache(cacheDir, integrity, data) {
|
|
3363
|
+
try {
|
|
3364
|
+
await mkdir(cacheDir, { recursive: true });
|
|
3365
|
+
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
3366
|
+
await writeFile(cachePath, data);
|
|
3367
|
+
} catch {
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
async function install(specifiers, options) {
|
|
3371
|
+
if (options.global) {
|
|
3372
|
+
const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3373
|
+
setGlobalMode2(true);
|
|
3374
|
+
}
|
|
3375
|
+
if (options.list) {
|
|
3376
|
+
const listSpecifiers = await resolveListToSpecifiers(options.list);
|
|
3377
|
+
if (listSpecifiers.length === 0) {
|
|
3378
|
+
console.log("No skills in the list to install.");
|
|
3379
|
+
return;
|
|
3234
3380
|
}
|
|
3235
|
-
const
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3381
|
+
const allSpecifiers = [...specifiers, ...listSpecifiers];
|
|
3382
|
+
const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3383
|
+
await add2(allSpecifiers, {
|
|
3384
|
+
save: true,
|
|
3385
|
+
agent: options.agent,
|
|
3386
|
+
yes: options.yes,
|
|
3387
|
+
global: options.global
|
|
3388
|
+
});
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
if (specifiers.length > 0) {
|
|
3392
|
+
const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
3393
|
+
await add2(specifiers, {
|
|
3394
|
+
save: true,
|
|
3395
|
+
agent: options.agent,
|
|
3396
|
+
yes: options.yes,
|
|
3397
|
+
global: options.global
|
|
3398
|
+
});
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
await installFromLockfile(options);
|
|
3402
|
+
}
|
|
3403
|
+
async function resolveListToSpecifiers(listSpec) {
|
|
3404
|
+
const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
|
|
3405
|
+
if (!match) {
|
|
3406
|
+
console.error(
|
|
3407
|
+
`Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
|
|
3408
|
+
);
|
|
3409
|
+
process.exit(1);
|
|
3410
|
+
}
|
|
3411
|
+
const [, ownerType, ownerName, listName] = match;
|
|
3412
|
+
const config2 = await resolveConfig();
|
|
3413
|
+
configure2({
|
|
3414
|
+
registryUrl: config2.registryUrl,
|
|
3415
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
3416
|
+
});
|
|
3417
|
+
console.log(
|
|
3418
|
+
`Fetching skill list @${ownerType}/${ownerName}/${listName}...
|
|
3419
|
+
`
|
|
3420
|
+
);
|
|
3421
|
+
const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
3422
|
+
const response = await fetchSkillList2(
|
|
3423
|
+
ownerType,
|
|
3424
|
+
ownerName,
|
|
3425
|
+
listName
|
|
3426
|
+
);
|
|
3427
|
+
if (response.status !== 200 || !response.data) {
|
|
3428
|
+
const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
|
|
3429
|
+
console.error(`Error: ${errorMsg}`);
|
|
3430
|
+
process.exit(1);
|
|
3431
|
+
}
|
|
3432
|
+
const list2 = response.data;
|
|
3433
|
+
console.log(
|
|
3434
|
+
`List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
|
|
3435
|
+
);
|
|
3436
|
+
console.log(`Skills: ${list2.items.length}
|
|
3437
|
+
`);
|
|
3438
|
+
const specifiers = [];
|
|
3439
|
+
for (const item of list2.items) {
|
|
3440
|
+
const ns = item.namespace === "org" ? "org" : "user";
|
|
3441
|
+
let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
|
|
3442
|
+
if (item.pinnedVersion) {
|
|
3443
|
+
spec += `@${item.pinnedVersion}`;
|
|
3444
|
+
}
|
|
3445
|
+
specifiers.push(spec);
|
|
3446
|
+
}
|
|
3447
|
+
return specifiers;
|
|
3448
|
+
}
|
|
3449
|
+
async function installFromLockfile(options) {
|
|
3450
|
+
try {
|
|
3451
|
+
const config2 = await resolveConfig();
|
|
3452
|
+
const registryUrl = config2.registryUrl;
|
|
3453
|
+
const apiKey = getTokenForRegistry(config2, registryUrl);
|
|
3454
|
+
const skillsDir = options.dir || getSkillsDir();
|
|
3455
|
+
const cacheDir = getCacheDir();
|
|
3456
|
+
await migrateLockfileIfNeeded();
|
|
3457
|
+
let lockfile = await readLockfile();
|
|
3458
|
+
const manifestDeps = await getDependencies();
|
|
3459
|
+
const manifestGitHubDeps = await getGitHubDependencies();
|
|
3460
|
+
const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
3461
|
+
const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
|
|
3462
|
+
const installedSkills = [];
|
|
3463
|
+
const missingDeps = [];
|
|
3464
|
+
for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
|
|
3465
|
+
if (!lockfilePackages[fullName]) {
|
|
3466
|
+
missingDeps.push({ fullName, versionRange });
|
|
3264
3467
|
}
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
console.error(
|
|
3269
|
-
` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
|
|
3270
|
-
);
|
|
3271
|
-
console.error(``);
|
|
3272
|
-
console.error(` Examples:`);
|
|
3273
|
-
console.error(` pspm access @user/myname/my-skill --public`);
|
|
3468
|
+
}
|
|
3469
|
+
if (missingDeps.length > 0) {
|
|
3470
|
+
if (options.frozenLockfile) {
|
|
3274
3471
|
console.error(
|
|
3275
|
-
|
|
3276
|
-
);
|
|
3277
|
-
process.exit(1);
|
|
3278
|
-
}
|
|
3279
|
-
packageName = parsed.name;
|
|
3280
|
-
packageUsername = parsed.owner;
|
|
3281
|
-
} else {
|
|
3282
|
-
const { readFile: readFile10 } = await import('fs/promises');
|
|
3283
|
-
const { join: join18 } = await import('path');
|
|
3284
|
-
let manifest = null;
|
|
3285
|
-
try {
|
|
3286
|
-
const content = await readFile10(
|
|
3287
|
-
join18(process.cwd(), "pspm.json"),
|
|
3288
|
-
"utf-8"
|
|
3472
|
+
"Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
3289
3473
|
);
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
const content = await readFile10(
|
|
3294
|
-
join18(process.cwd(), "package.json"),
|
|
3295
|
-
"utf-8"
|
|
3296
|
-
);
|
|
3297
|
-
manifest = JSON.parse(content);
|
|
3298
|
-
} catch {
|
|
3299
|
-
console.error(
|
|
3300
|
-
"Error: No pspm.json or package.json found in current directory"
|
|
3301
|
-
);
|
|
3302
|
-
console.error(
|
|
3303
|
-
"Either run this command in a package directory or specify a package name"
|
|
3304
|
-
);
|
|
3305
|
-
process.exit(1);
|
|
3474
|
+
console.error("Missing dependencies:");
|
|
3475
|
+
for (const dep of missingDeps) {
|
|
3476
|
+
console.error(` - ${dep.fullName}@${dep.versionRange}`);
|
|
3306
3477
|
}
|
|
3307
|
-
}
|
|
3308
|
-
if (!manifest?.name) {
|
|
3309
|
-
console.error("Error: Package manifest is missing 'name' field");
|
|
3310
3478
|
process.exit(1);
|
|
3311
3479
|
}
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
const
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
console.log("");
|
|
3340
|
-
console.log(
|
|
3341
|
-
"Note: This action is irreversible. Public packages cannot be made private."
|
|
3342
|
-
);
|
|
3343
|
-
}
|
|
3344
|
-
} catch (error) {
|
|
3345
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3346
|
-
console.error(`Error: ${message}`);
|
|
3347
|
-
process.exit(1);
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
// src/commands/index.ts
|
|
3352
|
-
init_add();
|
|
3353
|
-
|
|
3354
|
-
// src/commands/audit.ts
|
|
3355
|
-
init_config();
|
|
3356
|
-
init_lib();
|
|
3357
|
-
init_lockfile3();
|
|
3358
|
-
async function audit(options) {
|
|
3359
|
-
try {
|
|
3360
|
-
const lockfile = await readLockfile();
|
|
3361
|
-
if (!lockfile) {
|
|
3362
|
-
if (options.json) {
|
|
3363
|
-
console.log(
|
|
3364
|
-
JSON.stringify({
|
|
3365
|
-
ok: false,
|
|
3366
|
-
issues: [
|
|
3367
|
-
{
|
|
3368
|
-
name: "-",
|
|
3369
|
-
source: "-",
|
|
3370
|
-
severity: "error",
|
|
3371
|
-
type: "no-lockfile",
|
|
3372
|
-
message: "No lockfile found. Run 'pspm install' first."
|
|
3373
|
-
}
|
|
3374
|
-
]
|
|
3375
|
-
})
|
|
3480
|
+
console.log(`Resolving ${missingDeps.length} new dependency(ies)...
|
|
3481
|
+
`);
|
|
3482
|
+
configure2({ registryUrl, apiKey });
|
|
3483
|
+
for (const { fullName, versionRange } of missingDeps) {
|
|
3484
|
+
const parsed = parseRegistrySpecifier2(fullName);
|
|
3485
|
+
if (!parsed) {
|
|
3486
|
+
console.error(`Error: Invalid dependency specifier: ${fullName}`);
|
|
3487
|
+
continue;
|
|
3488
|
+
}
|
|
3489
|
+
const { owner, name } = parsed;
|
|
3490
|
+
console.log(`Resolving ${fullName}@${versionRange}...`);
|
|
3491
|
+
const versionsResponse = await listSkillVersions(owner, name);
|
|
3492
|
+
if (versionsResponse.status !== 200) {
|
|
3493
|
+
const errorMessage = extractApiErrorMessage(
|
|
3494
|
+
versionsResponse,
|
|
3495
|
+
`Skill ${fullName} not found`
|
|
3496
|
+
);
|
|
3497
|
+
console.error(`Error: ${errorMessage}`);
|
|
3498
|
+
continue;
|
|
3499
|
+
}
|
|
3500
|
+
const versions = versionsResponse.data;
|
|
3501
|
+
if (versions.length === 0) {
|
|
3502
|
+
console.error(`Error: Skill ${fullName} not found`);
|
|
3503
|
+
continue;
|
|
3504
|
+
}
|
|
3505
|
+
const versionStrings = versions.map(
|
|
3506
|
+
(v) => v.version
|
|
3376
3507
|
);
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
});
|
|
3403
|
-
continue;
|
|
3404
|
-
}
|
|
3405
|
-
if (entry.deprecated) {
|
|
3406
|
-
issues.push({
|
|
3407
|
-
name: fullName,
|
|
3408
|
-
source: "registry",
|
|
3409
|
-
severity: "warning",
|
|
3410
|
-
type: "deprecated",
|
|
3411
|
-
message: `Deprecated: ${entry.deprecated}`
|
|
3508
|
+
const resolved = resolveVersion2(versionRange || "*", versionStrings);
|
|
3509
|
+
if (!resolved) {
|
|
3510
|
+
console.error(
|
|
3511
|
+
`Error: No version matching "${versionRange}" for ${fullName}`
|
|
3512
|
+
);
|
|
3513
|
+
continue;
|
|
3514
|
+
}
|
|
3515
|
+
const versionResponse = await getSkillVersion(owner, name, resolved);
|
|
3516
|
+
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
3517
|
+
const errorMessage = extractApiErrorMessage(
|
|
3518
|
+
versionResponse,
|
|
3519
|
+
`Version ${resolved} not found`
|
|
3520
|
+
);
|
|
3521
|
+
console.error(`Error: ${errorMessage}`);
|
|
3522
|
+
continue;
|
|
3523
|
+
}
|
|
3524
|
+
const versionInfo = versionResponse.data;
|
|
3525
|
+
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
3526
|
+
const downloadHeaders = {};
|
|
3527
|
+
if (!isPresignedUrl && apiKey) {
|
|
3528
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
3529
|
+
}
|
|
3530
|
+
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
3531
|
+
headers: downloadHeaders,
|
|
3532
|
+
redirect: "follow"
|
|
3412
3533
|
});
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3534
|
+
if (!tarballResponse.ok) {
|
|
3535
|
+
console.error(
|
|
3536
|
+
`Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
|
|
3537
|
+
);
|
|
3538
|
+
continue;
|
|
3539
|
+
}
|
|
3540
|
+
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
3541
|
+
const integrity = calculateIntegrity(tarballBuffer);
|
|
3542
|
+
await addToLockfile(fullName, {
|
|
3543
|
+
version: resolved,
|
|
3544
|
+
resolved: versionInfo.downloadUrl,
|
|
3545
|
+
integrity
|
|
3422
3546
|
});
|
|
3547
|
+
await writeToCache(cacheDir, integrity, tarballBuffer);
|
|
3548
|
+
console.log(` Resolved ${fullName}@${resolved}`);
|
|
3423
3549
|
}
|
|
3550
|
+
lockfile = await readLockfile();
|
|
3424
3551
|
}
|
|
3425
|
-
const
|
|
3426
|
-
for (const
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
const destDir = parsed.path ? join(
|
|
3430
|
-
projectRoot,
|
|
3431
|
-
skillsDir,
|
|
3432
|
-
"_github",
|
|
3433
|
-
parsed.owner,
|
|
3434
|
-
parsed.repo,
|
|
3435
|
-
parsed.path
|
|
3436
|
-
) : join(projectRoot, skillsDir, "_github", parsed.owner, parsed.repo);
|
|
3437
|
-
const exists = await pathExists(destDir);
|
|
3438
|
-
if (!exists) {
|
|
3439
|
-
issues.push({
|
|
3440
|
-
name: specifier,
|
|
3441
|
-
source: "github",
|
|
3442
|
-
severity: "error",
|
|
3443
|
-
type: "missing",
|
|
3444
|
-
message: `Not installed on disk. Run 'pspm install' to restore.`
|
|
3445
|
-
});
|
|
3446
|
-
continue;
|
|
3447
|
-
}
|
|
3448
|
-
const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
|
|
3449
|
-
if (!skillMdExists) {
|
|
3450
|
-
issues.push({
|
|
3451
|
-
name: specifier,
|
|
3452
|
-
source: "github",
|
|
3453
|
-
severity: "warning",
|
|
3454
|
-
type: "integrity",
|
|
3455
|
-
message: "Missing SKILL.md in installed directory. Package may be corrupted."
|
|
3456
|
-
});
|
|
3552
|
+
const missingGitHubDeps = [];
|
|
3553
|
+
for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
|
|
3554
|
+
if (!lockfileGitHubPackages[specifier]) {
|
|
3555
|
+
missingGitHubDeps.push({ specifier, ref });
|
|
3457
3556
|
}
|
|
3458
3557
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
const exists = await pathExists(destDir);
|
|
3470
|
-
if (!exists) {
|
|
3471
|
-
issues.push({
|
|
3472
|
-
name: specifier,
|
|
3473
|
-
source: "well-known",
|
|
3474
|
-
severity: "error",
|
|
3475
|
-
type: "missing",
|
|
3476
|
-
message: `Not installed on disk. Run 'pspm install' to restore.`
|
|
3477
|
-
});
|
|
3478
|
-
continue;
|
|
3479
|
-
}
|
|
3480
|
-
const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
|
|
3481
|
-
if (!skillMdExists) {
|
|
3482
|
-
issues.push({
|
|
3483
|
-
name: specifier,
|
|
3484
|
-
source: "well-known",
|
|
3485
|
-
severity: "warning",
|
|
3486
|
-
type: "integrity",
|
|
3487
|
-
message: "Missing SKILL.md in installed directory. Package may be corrupted."
|
|
3488
|
-
});
|
|
3558
|
+
if (missingGitHubDeps.length > 0) {
|
|
3559
|
+
if (options.frozenLockfile) {
|
|
3560
|
+
console.error(
|
|
3561
|
+
"Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
3562
|
+
);
|
|
3563
|
+
console.error("Missing GitHub dependencies:");
|
|
3564
|
+
for (const dep of missingGitHubDeps) {
|
|
3565
|
+
console.error(` - ${dep.specifier}@${dep.ref}`);
|
|
3566
|
+
}
|
|
3567
|
+
process.exit(1);
|
|
3489
3568
|
}
|
|
3490
|
-
}
|
|
3491
|
-
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
3492
|
-
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
3493
|
-
if (options.json) {
|
|
3494
3569
|
console.log(
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
totalPackages: registrySkills.length + githubSkills.length + wellKnownSkills.length,
|
|
3499
|
-
issues
|
|
3500
|
-
},
|
|
3501
|
-
null,
|
|
3502
|
-
2
|
|
3503
|
-
)
|
|
3570
|
+
`
|
|
3571
|
+
Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
|
|
3572
|
+
`
|
|
3504
3573
|
);
|
|
3505
|
-
|
|
3506
|
-
|
|
3574
|
+
for (const { specifier, ref } of missingGitHubDeps) {
|
|
3575
|
+
const parsed = parseGitHubSpecifier2(specifier);
|
|
3576
|
+
if (!parsed) {
|
|
3577
|
+
console.error(`Error: Invalid GitHub specifier: ${specifier}`);
|
|
3578
|
+
continue;
|
|
3579
|
+
}
|
|
3580
|
+
parsed.ref = parsed.ref || ref;
|
|
3581
|
+
console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
|
|
3582
|
+
try {
|
|
3583
|
+
const result = await downloadGitHubPackage(parsed);
|
|
3584
|
+
await extractGitHubPackage(parsed, result.buffer, skillsDir);
|
|
3585
|
+
const entry = {
|
|
3586
|
+
version: result.commit.slice(0, 7),
|
|
3587
|
+
resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
|
|
3588
|
+
integrity: result.integrity,
|
|
3589
|
+
gitCommit: result.commit,
|
|
3590
|
+
gitRef: ref || "HEAD"
|
|
3591
|
+
};
|
|
3592
|
+
await addGitHubToLockfile(specifier, entry);
|
|
3593
|
+
await writeToCache(cacheDir, result.integrity, result.buffer);
|
|
3594
|
+
console.log(
|
|
3595
|
+
` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
|
|
3596
|
+
);
|
|
3597
|
+
} catch (error) {
|
|
3598
|
+
if (error instanceof GitHubRateLimitError) {
|
|
3599
|
+
console.error(`Error: ${error.message}`);
|
|
3600
|
+
} else if (error instanceof GitHubPathNotFoundError) {
|
|
3601
|
+
console.error(`Error: ${error.message}`);
|
|
3602
|
+
} else if (error instanceof GitHubNotFoundError) {
|
|
3603
|
+
console.error(`Error: ${error.message}`);
|
|
3604
|
+
} else {
|
|
3605
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3606
|
+
console.error(`Error resolving ${specifier}: ${message}`);
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
lockfile = await readLockfile();
|
|
3507
3611
|
}
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3612
|
+
const manifest = await readManifest();
|
|
3613
|
+
const agentConfigs = manifest?.agents;
|
|
3614
|
+
let agents;
|
|
3615
|
+
if (options.agent) {
|
|
3616
|
+
agents = parseAgentArg(options.agent);
|
|
3617
|
+
} else if (manifest) {
|
|
3618
|
+
agents = parseAgentArg(void 0);
|
|
3619
|
+
} else if (options.yes) {
|
|
3620
|
+
agents = parseAgentArg(void 0);
|
|
3621
|
+
} else {
|
|
3622
|
+
console.log("\nNo pspm.json found. Let's set up your project.\n");
|
|
3623
|
+
agents = await promptForAgents();
|
|
3624
|
+
console.log();
|
|
3512
3625
|
}
|
|
3513
|
-
const
|
|
3514
|
-
const
|
|
3515
|
-
if (
|
|
3516
|
-
console.log(
|
|
3517
|
-
|
|
3626
|
+
const packages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
3627
|
+
const packageCount = Object.keys(packages).length;
|
|
3628
|
+
if (packageCount > 0) {
|
|
3629
|
+
console.log(`
|
|
3630
|
+
Installing ${packageCount} registry skill(s)...
|
|
3631
|
+
`);
|
|
3632
|
+
const installOrder = computeInstallOrder(packages);
|
|
3633
|
+
const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
|
|
3634
|
+
for (const [fullName, entry] of entries) {
|
|
3635
|
+
const parsedName = parseRegistrySpecifier2(fullName);
|
|
3636
|
+
if (!parsedName) {
|
|
3637
|
+
console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
|
|
3638
|
+
continue;
|
|
3639
|
+
}
|
|
3640
|
+
const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
|
|
3641
|
+
console.log(`Installing ${fullName}@${entry.version}...`);
|
|
3642
|
+
let tarballBuffer;
|
|
3643
|
+
let fromCache = false;
|
|
3644
|
+
const cachedTarball = await readFromCache(cacheDir, entry.integrity);
|
|
3645
|
+
if (cachedTarball) {
|
|
3646
|
+
tarballBuffer = cachedTarball;
|
|
3647
|
+
fromCache = true;
|
|
3648
|
+
} else {
|
|
3649
|
+
const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
|
|
3650
|
+
const downloadHeaders = {};
|
|
3651
|
+
if (!isPresignedUrl && apiKey) {
|
|
3652
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
3653
|
+
}
|
|
3654
|
+
const response = await fetch(entry.resolved, {
|
|
3655
|
+
headers: downloadHeaders,
|
|
3656
|
+
redirect: "follow"
|
|
3657
|
+
});
|
|
3658
|
+
if (!response.ok) {
|
|
3659
|
+
if (response.status === 401) {
|
|
3660
|
+
if (!apiKey) {
|
|
3661
|
+
console.error(
|
|
3662
|
+
` Error: ${fullName} requires authentication. Run 'pspm login' first.`
|
|
3663
|
+
);
|
|
3664
|
+
} else {
|
|
3665
|
+
console.error(
|
|
3666
|
+
` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
|
|
3667
|
+
);
|
|
3668
|
+
}
|
|
3669
|
+
} else {
|
|
3670
|
+
console.error(
|
|
3671
|
+
` Error: Failed to download ${fullName} (${response.status})`
|
|
3672
|
+
);
|
|
3673
|
+
}
|
|
3674
|
+
continue;
|
|
3675
|
+
}
|
|
3676
|
+
tarballBuffer = Buffer.from(await response.arrayBuffer());
|
|
3677
|
+
const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
|
|
3678
|
+
if (actualIntegrity !== entry.integrity) {
|
|
3679
|
+
console.error(
|
|
3680
|
+
` Error: Checksum verification failed for ${fullName}`
|
|
3681
|
+
);
|
|
3682
|
+
if (options.frozenLockfile) {
|
|
3683
|
+
process.exit(1);
|
|
3684
|
+
}
|
|
3685
|
+
continue;
|
|
3686
|
+
}
|
|
3687
|
+
await writeToCache(cacheDir, entry.integrity, tarballBuffer);
|
|
3688
|
+
}
|
|
3689
|
+
const effectiveSkillName = subname ?? name;
|
|
3690
|
+
let destDir;
|
|
3691
|
+
if (ns === "org") {
|
|
3692
|
+
destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
|
|
3693
|
+
} else if (ns === "github" && subname) {
|
|
3694
|
+
destDir = join(
|
|
3695
|
+
skillsDir,
|
|
3696
|
+
"_github-registry",
|
|
3697
|
+
pkgOwner,
|
|
3698
|
+
name,
|
|
3699
|
+
subname
|
|
3700
|
+
);
|
|
3701
|
+
} else {
|
|
3702
|
+
destDir = join(skillsDir, pkgOwner, effectiveSkillName);
|
|
3703
|
+
}
|
|
3704
|
+
await rm(destDir, { recursive: true, force: true });
|
|
3705
|
+
await mkdir(destDir, { recursive: true });
|
|
3706
|
+
const tempFile = join(destDir, ".temp.tgz");
|
|
3707
|
+
await writeFile(tempFile, tarballBuffer);
|
|
3708
|
+
const { exec: exec2 } = await import('child_process');
|
|
3709
|
+
const { promisify: promisify2 } = await import('util');
|
|
3710
|
+
const execAsync = promisify2(exec2);
|
|
3711
|
+
try {
|
|
3712
|
+
await execAsync(
|
|
3713
|
+
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
3714
|
+
);
|
|
3715
|
+
} finally {
|
|
3716
|
+
await rm(tempFile, { force: true });
|
|
3717
|
+
}
|
|
3518
3718
|
console.log(
|
|
3519
|
-
`
|
|
3719
|
+
` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
|
|
3520
3720
|
);
|
|
3521
|
-
|
|
3721
|
+
const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
|
|
3722
|
+
installedSkills.push({
|
|
3723
|
+
name: effectiveSkillName,
|
|
3724
|
+
sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
|
|
3725
|
+
});
|
|
3522
3726
|
}
|
|
3523
|
-
console.log();
|
|
3524
3727
|
}
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3728
|
+
const githubPackages = lockfile?.githubPackages ?? {};
|
|
3729
|
+
const githubCount = Object.keys(githubPackages).length;
|
|
3730
|
+
if (githubCount > 0) {
|
|
3731
|
+
console.log(`
|
|
3732
|
+
Installing ${githubCount} GitHub skill(s)...
|
|
3733
|
+
`);
|
|
3734
|
+
for (const [specifier, entry] of Object.entries(githubPackages)) {
|
|
3735
|
+
const parsed = parseGitHubSpecifier2(specifier);
|
|
3736
|
+
if (!parsed) {
|
|
3737
|
+
console.warn(
|
|
3738
|
+
`Warning: Invalid GitHub specifier in lockfile: ${specifier}`
|
|
3739
|
+
);
|
|
3740
|
+
continue;
|
|
3741
|
+
}
|
|
3742
|
+
const ghEntry = entry;
|
|
3528
3743
|
console.log(
|
|
3529
|
-
`
|
|
3744
|
+
`Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
|
|
3530
3745
|
);
|
|
3531
|
-
|
|
3746
|
+
let tarballBuffer;
|
|
3747
|
+
let fromCache = false;
|
|
3748
|
+
const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
|
|
3749
|
+
if (cachedTarball) {
|
|
3750
|
+
tarballBuffer = cachedTarball;
|
|
3751
|
+
fromCache = true;
|
|
3752
|
+
} else {
|
|
3753
|
+
try {
|
|
3754
|
+
const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
|
|
3755
|
+
const result = await downloadGitHubPackage(specWithCommit);
|
|
3756
|
+
tarballBuffer = result.buffer;
|
|
3757
|
+
if (result.integrity !== ghEntry.integrity) {
|
|
3758
|
+
console.error(
|
|
3759
|
+
` Error: Checksum verification failed for ${specifier}`
|
|
3760
|
+
);
|
|
3761
|
+
if (options.frozenLockfile) {
|
|
3762
|
+
process.exit(1);
|
|
3763
|
+
}
|
|
3764
|
+
continue;
|
|
3765
|
+
}
|
|
3766
|
+
await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
|
|
3767
|
+
} catch (error) {
|
|
3768
|
+
if (error instanceof GitHubRateLimitError) {
|
|
3769
|
+
console.error(` Error: ${error.message}`);
|
|
3770
|
+
} else if (error instanceof GitHubPathNotFoundError) {
|
|
3771
|
+
console.error(` Error: ${error.message}`);
|
|
3772
|
+
} else if (error instanceof GitHubNotFoundError) {
|
|
3773
|
+
console.error(` Error: ${error.message}`);
|
|
3774
|
+
} else {
|
|
3775
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3776
|
+
console.error(` Error downloading ${specifier}: ${message}`);
|
|
3777
|
+
}
|
|
3778
|
+
continue;
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
try {
|
|
3782
|
+
const destPath = await extractGitHubPackage(
|
|
3783
|
+
parsed,
|
|
3784
|
+
tarballBuffer,
|
|
3785
|
+
skillsDir
|
|
3786
|
+
);
|
|
3787
|
+
console.log(
|
|
3788
|
+
` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
|
|
3789
|
+
);
|
|
3790
|
+
const skillName = getGitHubSkillName2(parsed);
|
|
3791
|
+
installedSkills.push({
|
|
3792
|
+
name: skillName,
|
|
3793
|
+
sourcePath: getGitHubSkillPath(
|
|
3794
|
+
parsed.owner,
|
|
3795
|
+
parsed.repo,
|
|
3796
|
+
parsed.path
|
|
3797
|
+
)
|
|
3798
|
+
});
|
|
3799
|
+
} catch (error) {
|
|
3800
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3801
|
+
console.error(` Error extracting ${specifier}: ${message}`);
|
|
3802
|
+
}
|
|
3532
3803
|
}
|
|
3533
|
-
console.log();
|
|
3534
|
-
}
|
|
3535
|
-
const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
|
|
3536
|
-
console.log(
|
|
3537
|
-
`Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
|
|
3538
|
-
);
|
|
3539
|
-
if (errorCount > 0) {
|
|
3540
|
-
process.exit(1);
|
|
3541
3804
|
}
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
}
|
|
3556
|
-
async function configInit(options) {
|
|
3557
|
-
try {
|
|
3558
|
-
const configPath = join(process.cwd(), ".pspmrc");
|
|
3559
|
-
try {
|
|
3560
|
-
await stat(configPath);
|
|
3561
|
-
console.error("Error: .pspmrc already exists in this directory.");
|
|
3562
|
-
process.exit(1);
|
|
3563
|
-
} catch {
|
|
3805
|
+
if (installedSkills.length > 0 && agents[0] !== "none") {
|
|
3806
|
+
const globalMode = isGlobalMode();
|
|
3807
|
+
console.log(
|
|
3808
|
+
`
|
|
3809
|
+
Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
|
|
3810
|
+
);
|
|
3811
|
+
await createAgentSymlinks(installedSkills, {
|
|
3812
|
+
agents,
|
|
3813
|
+
projectRoot: globalMode ? homedir() : process.cwd(),
|
|
3814
|
+
agentConfigs,
|
|
3815
|
+
global: globalMode
|
|
3816
|
+
});
|
|
3817
|
+
console.log(" Symlinks created.");
|
|
3564
3818
|
}
|
|
3565
|
-
const
|
|
3566
|
-
if (
|
|
3567
|
-
|
|
3819
|
+
const totalCount = packageCount + githubCount;
|
|
3820
|
+
if (totalCount === 0) {
|
|
3821
|
+
console.log("No skills to install.");
|
|
3568
3822
|
} else {
|
|
3569
|
-
|
|
3570
|
-
|
|
3823
|
+
console.log(`
|
|
3824
|
+
All ${totalCount} skill(s) installed.`);
|
|
3571
3825
|
}
|
|
3572
|
-
lines.push("");
|
|
3573
|
-
await writeFile(configPath, lines.join("\n"));
|
|
3574
|
-
console.log("Created .pspmrc");
|
|
3575
|
-
console.log("");
|
|
3576
|
-
console.log("Contents:");
|
|
3577
|
-
console.log(lines.join("\n"));
|
|
3578
|
-
console.log("Note: .pspmrc should be committed to version control.");
|
|
3579
|
-
console.log("API keys should NOT be stored here - use pspm login instead.");
|
|
3580
3826
|
} catch (error) {
|
|
3581
3827
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3582
3828
|
console.error(`Error: ${message}`);
|
|
3583
3829
|
process.exit(1);
|
|
3584
3830
|
}
|
|
3585
3831
|
}
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
console.log(` Username: ${resolved.username || "(not set)"}`);
|
|
3598
|
-
console.log("");
|
|
3599
|
-
console.log("Config Locations:");
|
|
3600
|
-
console.log(` User config: ${configPath}`);
|
|
3601
|
-
console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
|
|
3602
|
-
console.log("");
|
|
3603
|
-
console.log("Environment Variables:");
|
|
3604
|
-
console.log(
|
|
3605
|
-
` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
|
|
3606
|
-
);
|
|
3607
|
-
console.log(
|
|
3608
|
-
` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
|
|
3609
|
-
);
|
|
3610
|
-
} catch (error) {
|
|
3611
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3612
|
-
console.error(`Error: ${message}`);
|
|
3613
|
-
process.exit(1);
|
|
3832
|
+
var init_install = __esm({
|
|
3833
|
+
"src/commands/install.ts"() {
|
|
3834
|
+
init_agents();
|
|
3835
|
+
init_api_client();
|
|
3836
|
+
init_config();
|
|
3837
|
+
init_errors();
|
|
3838
|
+
init_github();
|
|
3839
|
+
init_lib();
|
|
3840
|
+
init_lockfile3();
|
|
3841
|
+
init_manifest3();
|
|
3842
|
+
init_symlinks();
|
|
3614
3843
|
}
|
|
3615
|
-
}
|
|
3844
|
+
});
|
|
3616
3845
|
|
|
3617
|
-
// src/commands/
|
|
3846
|
+
// src/commands/access.ts
|
|
3618
3847
|
init_api_client();
|
|
3619
3848
|
init_config();
|
|
3620
3849
|
init_lib();
|
|
3621
|
-
|
|
3850
|
+
function isLocalSpecifier2(specifier) {
|
|
3851
|
+
return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
|
|
3852
|
+
}
|
|
3853
|
+
async function access(specifier, options) {
|
|
3622
3854
|
try {
|
|
3623
3855
|
const apiKey = await requireApiKey();
|
|
3624
3856
|
const registryUrl = await getRegistryUrl();
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
console.error(
|
|
3628
|
-
`Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
|
|
3629
|
-
);
|
|
3857
|
+
if (options.public && options.private) {
|
|
3858
|
+
console.error("Error: Cannot specify both --public and --private");
|
|
3630
3859
|
process.exit(1);
|
|
3631
3860
|
}
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
namespace: parsed.namespace,
|
|
3635
|
-
owner,
|
|
3636
|
-
name
|
|
3637
|
-
});
|
|
3638
|
-
if (!versionRange) {
|
|
3639
|
-
console.error(
|
|
3640
|
-
"Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
|
|
3641
|
-
);
|
|
3861
|
+
if (!options.public && !options.private) {
|
|
3862
|
+
console.error("Error: Must specify either --public or --private");
|
|
3642
3863
|
process.exit(1);
|
|
3643
3864
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
if (
|
|
3865
|
+
const visibility = options.public ? "public" : "private";
|
|
3866
|
+
let packageName;
|
|
3867
|
+
let packageUsername;
|
|
3868
|
+
if (specifier) {
|
|
3869
|
+
if (isGitHubSpecifier2(specifier)) {
|
|
3870
|
+
const ghSpec = parseGitHubSpecifier2(specifier);
|
|
3871
|
+
if (ghSpec) {
|
|
3872
|
+
console.error(`Error: Cannot change visibility of GitHub packages.`);
|
|
3873
|
+
console.error(
|
|
3874
|
+
` "${specifier}" is hosted on GitHub, not the PSPM registry.`
|
|
3875
|
+
);
|
|
3876
|
+
console.error(
|
|
3877
|
+
` Visibility can only be changed for packages published to the registry.`
|
|
3878
|
+
);
|
|
3879
|
+
} else {
|
|
3880
|
+
console.error(`Error: Invalid GitHub specifier "${specifier}".`);
|
|
3881
|
+
console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
|
|
3882
|
+
}
|
|
3883
|
+
process.exit(1);
|
|
3884
|
+
}
|
|
3885
|
+
if (isLocalSpecifier2(specifier)) {
|
|
3886
|
+
console.error(`Error: Cannot change visibility of local packages.`);
|
|
3649
3887
|
console.error(
|
|
3650
|
-
`
|
|
3888
|
+
` "${specifier}" is a local directory, not a registry package.`
|
|
3889
|
+
);
|
|
3890
|
+
console.error(
|
|
3891
|
+
` Visibility can only be changed for packages published to the registry.`
|
|
3651
3892
|
);
|
|
3652
3893
|
process.exit(1);
|
|
3653
3894
|
}
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3895
|
+
const parsed = parseRegistrySpecifier2(specifier);
|
|
3896
|
+
if (!parsed) {
|
|
3897
|
+
console.error(`Error: Invalid package specifier "${specifier}".`);
|
|
3657
3898
|
console.error(
|
|
3658
|
-
|
|
3899
|
+
` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
|
|
3900
|
+
);
|
|
3901
|
+
console.error(``);
|
|
3902
|
+
console.error(` Examples:`);
|
|
3903
|
+
console.error(` pspm access @user/myname/my-skill --public`);
|
|
3904
|
+
console.error(
|
|
3905
|
+
` pspm access --public (uses current directory's pspm.json)`
|
|
3659
3906
|
);
|
|
3660
3907
|
process.exit(1);
|
|
3661
3908
|
}
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3909
|
+
packageName = parsed.name;
|
|
3910
|
+
packageUsername = parsed.owner;
|
|
3911
|
+
} else {
|
|
3912
|
+
const { readFile: readFile10 } = await import('fs/promises');
|
|
3913
|
+
const { join: join18 } = await import('path');
|
|
3914
|
+
let manifest = null;
|
|
3915
|
+
try {
|
|
3916
|
+
const content = await readFile10(
|
|
3917
|
+
join18(process.cwd(), "pspm.json"),
|
|
3918
|
+
"utf-8"
|
|
3672
3919
|
);
|
|
3920
|
+
manifest = JSON.parse(content);
|
|
3921
|
+
} catch {
|
|
3922
|
+
try {
|
|
3923
|
+
const content = await readFile10(
|
|
3924
|
+
join18(process.cwd(), "package.json"),
|
|
3925
|
+
"utf-8"
|
|
3926
|
+
);
|
|
3927
|
+
manifest = JSON.parse(content);
|
|
3928
|
+
} catch {
|
|
3929
|
+
console.error(
|
|
3930
|
+
"Error: No pspm.json or package.json found in current directory"
|
|
3931
|
+
);
|
|
3932
|
+
console.error(
|
|
3933
|
+
"Either run this command in a package directory or specify a package name"
|
|
3934
|
+
);
|
|
3935
|
+
process.exit(1);
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
if (!manifest?.name) {
|
|
3939
|
+
console.error("Error: Package manifest is missing 'name' field");
|
|
3673
3940
|
process.exit(1);
|
|
3674
3941
|
}
|
|
3675
|
-
|
|
3676
|
-
|
|
3942
|
+
packageName = manifest.name;
|
|
3943
|
+
}
|
|
3944
|
+
if (!packageUsername) {
|
|
3945
|
+
const config2 = await resolveConfig();
|
|
3946
|
+
packageUsername = config2.username;
|
|
3947
|
+
}
|
|
3948
|
+
if (!packageUsername) {
|
|
3949
|
+
console.error(
|
|
3950
|
+
"Error: Could not determine username. Please use the full specifier: @user/{username}/{name}"
|
|
3951
|
+
);
|
|
3952
|
+
process.exit(1);
|
|
3953
|
+
}
|
|
3954
|
+
configure2({ registryUrl, apiKey });
|
|
3955
|
+
console.log(`Setting ${packageName} to ${visibility}...`);
|
|
3956
|
+
const response = await changeSkillAccess(packageUsername, packageName, {
|
|
3957
|
+
visibility
|
|
3958
|
+
});
|
|
3959
|
+
if (response.status !== 200 || !response.data) {
|
|
3960
|
+
const errorMessage = response.error ?? "Failed to change visibility";
|
|
3961
|
+
console.error(`Error: ${errorMessage}`);
|
|
3962
|
+
process.exit(1);
|
|
3963
|
+
}
|
|
3964
|
+
const result = response.data;
|
|
3965
|
+
console.log(
|
|
3966
|
+
`+ @${result.namespace ?? "user"}/${result.username}/${result.name} is now ${result.visibility}`
|
|
3967
|
+
);
|
|
3968
|
+
if (visibility === "public") {
|
|
3677
3969
|
console.log("");
|
|
3678
3970
|
console.log(
|
|
3679
|
-
"
|
|
3971
|
+
"Note: This action is irreversible. Public packages cannot be made private."
|
|
3680
3972
|
);
|
|
3681
|
-
console.log("The package is still available for download.");
|
|
3682
3973
|
}
|
|
3683
3974
|
} catch (error) {
|
|
3684
|
-
const
|
|
3685
|
-
console.error(`Error: ${
|
|
3975
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3976
|
+
console.error(`Error: ${message}`);
|
|
3686
3977
|
process.exit(1);
|
|
3687
3978
|
}
|
|
3688
3979
|
}
|
|
3689
3980
|
|
|
3690
|
-
// src/commands/
|
|
3981
|
+
// src/commands/index.ts
|
|
3982
|
+
init_add();
|
|
3983
|
+
|
|
3984
|
+
// src/commands/audit.ts
|
|
3985
|
+
init_config();
|
|
3691
3986
|
init_lib();
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
3695
|
-
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
3696
|
-
resolve2(answer.trim() || defaultValue);
|
|
3697
|
-
});
|
|
3698
|
-
});
|
|
3699
|
-
}
|
|
3700
|
-
async function readExistingPackageJson() {
|
|
3987
|
+
init_lockfile3();
|
|
3988
|
+
async function audit(options) {
|
|
3701
3989
|
try {
|
|
3702
|
-
const
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
}
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
const [nameResult, emailResult] = await Promise.all([
|
|
3724
|
-
execAsync("git config user.name").catch(() => ({ stdout: "" })),
|
|
3725
|
-
execAsync("git config user.email").catch(() => ({ stdout: "" }))
|
|
3726
|
-
]);
|
|
3727
|
-
const name = nameResult.stdout.trim();
|
|
3728
|
-
const email = emailResult.stdout.trim();
|
|
3729
|
-
if (name && email) {
|
|
3730
|
-
return `${name} <${email}>`;
|
|
3990
|
+
const lockfile = await readLockfile();
|
|
3991
|
+
if (!lockfile) {
|
|
3992
|
+
if (options.json) {
|
|
3993
|
+
console.log(
|
|
3994
|
+
JSON.stringify({
|
|
3995
|
+
ok: false,
|
|
3996
|
+
issues: [
|
|
3997
|
+
{
|
|
3998
|
+
name: "-",
|
|
3999
|
+
source: "-",
|
|
4000
|
+
severity: "error",
|
|
4001
|
+
type: "no-lockfile",
|
|
4002
|
+
message: "No lockfile found. Run 'pspm install' first."
|
|
4003
|
+
}
|
|
4004
|
+
]
|
|
4005
|
+
})
|
|
4006
|
+
);
|
|
4007
|
+
} else {
|
|
4008
|
+
console.error("No lockfile found. Run 'pspm install' to create one.");
|
|
4009
|
+
}
|
|
4010
|
+
process.exit(1);
|
|
3731
4011
|
}
|
|
3732
|
-
|
|
3733
|
-
|
|
4012
|
+
const issues = [];
|
|
4013
|
+
const skillsDir = getSkillsDir();
|
|
4014
|
+
if (!options.json) {
|
|
4015
|
+
console.log("Auditing installed skills...\n");
|
|
3734
4016
|
}
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
}
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
4017
|
+
const registrySkills = await listLockfileSkills();
|
|
4018
|
+
for (const { name: fullName, entry } of registrySkills) {
|
|
4019
|
+
const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
4020
|
+
if (!match) continue;
|
|
4021
|
+
const [, username, skillName] = match;
|
|
4022
|
+
const destDir = join(skillsDir, username, skillName);
|
|
4023
|
+
const exists = await pathExists(destDir);
|
|
4024
|
+
if (!exists) {
|
|
4025
|
+
issues.push({
|
|
4026
|
+
name: fullName,
|
|
4027
|
+
source: "registry",
|
|
4028
|
+
severity: "error",
|
|
4029
|
+
type: "missing",
|
|
4030
|
+
message: `Not installed on disk. Run 'pspm install' to restore.`
|
|
4031
|
+
});
|
|
4032
|
+
continue;
|
|
4033
|
+
}
|
|
4034
|
+
if (entry.deprecated) {
|
|
4035
|
+
issues.push({
|
|
4036
|
+
name: fullName,
|
|
4037
|
+
source: "registry",
|
|
4038
|
+
severity: "warning",
|
|
4039
|
+
type: "deprecated",
|
|
4040
|
+
message: `Deprecated: ${entry.deprecated}`
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
|
|
4044
|
+
if (!skillMdExists) {
|
|
4045
|
+
issues.push({
|
|
4046
|
+
name: fullName,
|
|
4047
|
+
source: "registry",
|
|
4048
|
+
severity: "warning",
|
|
4049
|
+
type: "integrity",
|
|
4050
|
+
message: "Missing SKILL.md in installed directory. Package may be corrupted."
|
|
4051
|
+
});
|
|
4052
|
+
}
|
|
3758
4053
|
}
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
4054
|
+
const githubSkills = await listLockfileGitHubPackages();
|
|
4055
|
+
for (const { specifier } of githubSkills) {
|
|
4056
|
+
const parsed = parseGitHubSpecifier2(specifier);
|
|
4057
|
+
if (!parsed) continue;
|
|
4058
|
+
const destDir = parsed.path ? join(skillsDir, "_github", parsed.owner, parsed.repo, parsed.path) : join(skillsDir, "_github", parsed.owner, parsed.repo);
|
|
4059
|
+
const exists = await pathExists(destDir);
|
|
4060
|
+
if (!exists) {
|
|
4061
|
+
issues.push({
|
|
4062
|
+
name: specifier,
|
|
4063
|
+
source: "github",
|
|
4064
|
+
severity: "error",
|
|
4065
|
+
type: "missing",
|
|
4066
|
+
message: `Not installed on disk. Run 'pspm install' to restore.`
|
|
4067
|
+
});
|
|
4068
|
+
continue;
|
|
4069
|
+
}
|
|
4070
|
+
const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
|
|
4071
|
+
if (!skillMdExists) {
|
|
4072
|
+
issues.push({
|
|
4073
|
+
name: specifier,
|
|
4074
|
+
source: "github",
|
|
4075
|
+
severity: "warning",
|
|
4076
|
+
type: "integrity",
|
|
4077
|
+
message: "Missing SKILL.md in installed directory. Package may be corrupted."
|
|
4078
|
+
});
|
|
4079
|
+
}
|
|
3763
4080
|
}
|
|
3764
|
-
const
|
|
3765
|
-
const
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
const defaultLicense = existingPkg?.license || "MIT";
|
|
3773
|
-
const defaultMain = "SKILL.md";
|
|
3774
|
-
const defaultCapabilities = "";
|
|
3775
|
-
let manifest;
|
|
3776
|
-
if (options.yes) {
|
|
3777
|
-
manifest = {
|
|
3778
|
-
$schema: PSPM_SCHEMA_URL,
|
|
3779
|
-
name: defaultName,
|
|
3780
|
-
version: defaultVersion,
|
|
3781
|
-
description: defaultDescription || void 0,
|
|
3782
|
-
author: defaultAuthor || void 0,
|
|
3783
|
-
license: defaultLicense,
|
|
3784
|
-
type: "skill",
|
|
3785
|
-
capabilities: [],
|
|
3786
|
-
main: defaultMain,
|
|
3787
|
-
requirements: {
|
|
3788
|
-
pspm: ">=0.1.0"
|
|
3789
|
-
},
|
|
3790
|
-
files: [...DEFAULT_SKILL_FILES],
|
|
3791
|
-
dependencies: {},
|
|
3792
|
-
private: false
|
|
3793
|
-
};
|
|
3794
|
-
} else {
|
|
3795
|
-
console.log(
|
|
3796
|
-
"This utility will walk you through creating a pspm.json file."
|
|
3797
|
-
);
|
|
3798
|
-
console.log(
|
|
3799
|
-
"It only covers the most common items, and tries to guess sensible defaults."
|
|
3800
|
-
);
|
|
3801
|
-
console.log("");
|
|
3802
|
-
console.log(
|
|
3803
|
-
"See `pspm init --help` for definitive documentation on these fields"
|
|
4081
|
+
const wellKnownSkills = await listLockfileWellKnownPackages();
|
|
4082
|
+
for (const { specifier, entry } of wellKnownSkills) {
|
|
4083
|
+
const wkEntry = entry;
|
|
4084
|
+
const destDir = join(
|
|
4085
|
+
skillsDir,
|
|
4086
|
+
"_wellknown",
|
|
4087
|
+
wkEntry.hostname,
|
|
4088
|
+
wkEntry.name
|
|
3804
4089
|
);
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
}
|
|
3825
|
-
const description = await prompt(
|
|
3826
|
-
rl,
|
|
3827
|
-
"description:",
|
|
3828
|
-
defaultDescription
|
|
3829
|
-
);
|
|
3830
|
-
const main = await prompt(rl, "entry point:", defaultMain);
|
|
3831
|
-
const capabilitiesStr = await prompt(
|
|
3832
|
-
rl,
|
|
3833
|
-
"capabilities (comma-separated):",
|
|
3834
|
-
defaultCapabilities
|
|
3835
|
-
);
|
|
3836
|
-
const author = await prompt(rl, "author:", defaultAuthor);
|
|
3837
|
-
const license = await prompt(rl, "license:", defaultLicense);
|
|
3838
|
-
rl.close();
|
|
3839
|
-
const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
3840
|
-
manifest = {
|
|
3841
|
-
$schema: PSPM_SCHEMA_URL,
|
|
3842
|
-
name,
|
|
3843
|
-
version: version3,
|
|
3844
|
-
description: description || void 0,
|
|
3845
|
-
author: author || void 0,
|
|
3846
|
-
license,
|
|
3847
|
-
type: "skill",
|
|
3848
|
-
capabilities,
|
|
3849
|
-
main,
|
|
3850
|
-
requirements: {
|
|
3851
|
-
pspm: ">=0.1.0"
|
|
3852
|
-
},
|
|
3853
|
-
files: [...DEFAULT_SKILL_FILES],
|
|
3854
|
-
dependencies: {},
|
|
3855
|
-
private: false
|
|
3856
|
-
};
|
|
3857
|
-
} catch (error) {
|
|
3858
|
-
rl.close();
|
|
3859
|
-
if (error instanceof Error && error.message.includes("readline was closed")) {
|
|
3860
|
-
console.log("\nAborted.");
|
|
3861
|
-
process.exit(0);
|
|
3862
|
-
}
|
|
3863
|
-
throw error;
|
|
4090
|
+
const exists = await pathExists(destDir);
|
|
4091
|
+
if (!exists) {
|
|
4092
|
+
issues.push({
|
|
4093
|
+
name: specifier,
|
|
4094
|
+
source: "well-known",
|
|
4095
|
+
severity: "error",
|
|
4096
|
+
type: "missing",
|
|
4097
|
+
message: `Not installed on disk. Run 'pspm install' to restore.`
|
|
4098
|
+
});
|
|
4099
|
+
continue;
|
|
4100
|
+
}
|
|
4101
|
+
const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
|
|
4102
|
+
if (!skillMdExists) {
|
|
4103
|
+
issues.push({
|
|
4104
|
+
name: specifier,
|
|
4105
|
+
source: "well-known",
|
|
4106
|
+
severity: "warning",
|
|
4107
|
+
type: "integrity",
|
|
4108
|
+
message: "Missing SKILL.md in installed directory. Package may be corrupted."
|
|
4109
|
+
});
|
|
3864
4110
|
}
|
|
3865
4111
|
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
if (
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
4112
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
4113
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
4114
|
+
if (options.json) {
|
|
4115
|
+
console.log(
|
|
4116
|
+
JSON.stringify(
|
|
4117
|
+
{
|
|
4118
|
+
ok: errorCount === 0,
|
|
4119
|
+
totalPackages: registrySkills.length + githubSkills.length + wellKnownSkills.length,
|
|
4120
|
+
issues
|
|
4121
|
+
},
|
|
4122
|
+
null,
|
|
4123
|
+
2
|
|
4124
|
+
)
|
|
4125
|
+
);
|
|
4126
|
+
if (errorCount > 0) process.exit(1);
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
if (issues.length === 0) {
|
|
4130
|
+
const totalPackages2 = registrySkills.length + githubSkills.length + wellKnownSkills.length;
|
|
4131
|
+
console.log(`Audited ${totalPackages2} package(s). No issues found.`);
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
4135
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
4136
|
+
if (errors.length > 0) {
|
|
4137
|
+
console.log("Errors:");
|
|
4138
|
+
for (const issue of errors) {
|
|
4139
|
+
console.log(
|
|
4140
|
+
` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
|
|
4141
|
+
);
|
|
4142
|
+
console.log(` ${issue.message}`);
|
|
3885
4143
|
}
|
|
4144
|
+
console.log();
|
|
3886
4145
|
}
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
4146
|
+
if (warnings.length > 0) {
|
|
4147
|
+
console.log("Warnings:");
|
|
4148
|
+
for (const issue of warnings) {
|
|
4149
|
+
console.log(
|
|
4150
|
+
` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
|
|
4151
|
+
);
|
|
4152
|
+
console.log(` ${issue.message}`);
|
|
4153
|
+
}
|
|
4154
|
+
console.log();
|
|
3895
4155
|
}
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
4156
|
+
const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
|
|
4157
|
+
console.log(
|
|
4158
|
+
`Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
|
|
4159
|
+
);
|
|
4160
|
+
if (errorCount > 0) {
|
|
4161
|
+
process.exit(1);
|
|
3900
4162
|
}
|
|
3901
4163
|
} catch (error) {
|
|
3902
4164
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -3904,441 +4166,358 @@ async function init(options) {
|
|
|
3904
4166
|
process.exit(1);
|
|
3905
4167
|
}
|
|
3906
4168
|
}
|
|
3907
|
-
|
|
3908
|
-
// src/commands/install.ts
|
|
3909
|
-
init_agents();
|
|
3910
|
-
init_api_client();
|
|
3911
|
-
init_config();
|
|
3912
|
-
init_errors();
|
|
3913
|
-
init_github();
|
|
3914
|
-
init_lib();
|
|
3915
|
-
init_lockfile3();
|
|
3916
|
-
init_manifest3();
|
|
3917
|
-
init_symlinks();
|
|
3918
|
-
function getCacheFilePath(cacheDir, integrity) {
|
|
3919
|
-
const match = integrity.match(/^sha256-(.+)$/);
|
|
3920
|
-
if (!match) {
|
|
3921
|
-
throw new Error(`Invalid integrity format: ${integrity}`);
|
|
3922
|
-
}
|
|
3923
|
-
const base64Hash = match[1];
|
|
3924
|
-
const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
|
|
3925
|
-
return join(cacheDir, `sha256-${hexHash}.tgz`);
|
|
3926
|
-
}
|
|
3927
|
-
async function readFromCache(cacheDir, integrity) {
|
|
4169
|
+
async function pathExists(path) {
|
|
3928
4170
|
try {
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
|
|
3932
|
-
if (actualIntegrity !== integrity) {
|
|
3933
|
-
await rm(cachePath, { force: true });
|
|
3934
|
-
return null;
|
|
3935
|
-
}
|
|
3936
|
-
return data;
|
|
4171
|
+
await stat(path);
|
|
4172
|
+
return true;
|
|
3937
4173
|
} catch {
|
|
3938
|
-
return
|
|
4174
|
+
return false;
|
|
3939
4175
|
}
|
|
3940
4176
|
}
|
|
3941
|
-
async function
|
|
4177
|
+
async function configInit(options) {
|
|
3942
4178
|
try {
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
}
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
4179
|
+
const configPath = join(process.cwd(), ".pspmrc");
|
|
4180
|
+
try {
|
|
4181
|
+
await stat(configPath);
|
|
4182
|
+
console.error("Error: .pspmrc already exists in this directory.");
|
|
4183
|
+
process.exit(1);
|
|
4184
|
+
} catch {
|
|
4185
|
+
}
|
|
4186
|
+
const lines = ["; Project-specific PSPM configuration", ""];
|
|
4187
|
+
if (options.registry) {
|
|
4188
|
+
lines.push(`registry = ${options.registry}`);
|
|
4189
|
+
} else {
|
|
4190
|
+
lines.push("; Uncomment to use a custom registry:");
|
|
4191
|
+
lines.push("; registry = https://custom-registry.example.com");
|
|
4192
|
+
}
|
|
4193
|
+
lines.push("");
|
|
4194
|
+
await writeFile(configPath, lines.join("\n"));
|
|
4195
|
+
console.log("Created .pspmrc");
|
|
4196
|
+
console.log("");
|
|
4197
|
+
console.log("Contents:");
|
|
4198
|
+
console.log(lines.join("\n"));
|
|
4199
|
+
console.log("Note: .pspmrc should be committed to version control.");
|
|
4200
|
+
console.log("API keys should NOT be stored here - use pspm login instead.");
|
|
4201
|
+
} catch (error) {
|
|
4202
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4203
|
+
console.error(`Error: ${message}`);
|
|
4204
|
+
process.exit(1);
|
|
3963
4205
|
}
|
|
3964
|
-
await installFromLockfile(options);
|
|
3965
4206
|
}
|
|
3966
|
-
|
|
4207
|
+
|
|
4208
|
+
// src/commands/config/show.ts
|
|
4209
|
+
init_config();
|
|
4210
|
+
async function configShow() {
|
|
3967
4211
|
try {
|
|
3968
|
-
const
|
|
3969
|
-
const
|
|
3970
|
-
const
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
const versionsResponse = await listSkillVersions(owner, name);
|
|
4009
|
-
if (versionsResponse.status !== 200) {
|
|
4010
|
-
const errorMessage = extractApiErrorMessage(
|
|
4011
|
-
versionsResponse,
|
|
4012
|
-
`Skill ${fullName} not found`
|
|
4013
|
-
);
|
|
4014
|
-
console.error(`Error: ${errorMessage}`);
|
|
4015
|
-
continue;
|
|
4016
|
-
}
|
|
4017
|
-
const versions = versionsResponse.data;
|
|
4018
|
-
if (versions.length === 0) {
|
|
4019
|
-
console.error(`Error: Skill ${fullName} not found`);
|
|
4020
|
-
continue;
|
|
4021
|
-
}
|
|
4022
|
-
const versionStrings = versions.map(
|
|
4023
|
-
(v) => v.version
|
|
4024
|
-
);
|
|
4025
|
-
const resolved = resolveVersion2(versionRange || "*", versionStrings);
|
|
4026
|
-
if (!resolved) {
|
|
4027
|
-
console.error(
|
|
4028
|
-
`Error: No version matching "${versionRange}" for ${fullName}`
|
|
4029
|
-
);
|
|
4030
|
-
continue;
|
|
4031
|
-
}
|
|
4032
|
-
const versionResponse = await getSkillVersion(owner, name, resolved);
|
|
4033
|
-
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
4034
|
-
const errorMessage = extractApiErrorMessage(
|
|
4035
|
-
versionResponse,
|
|
4036
|
-
`Version ${resolved} not found`
|
|
4037
|
-
);
|
|
4038
|
-
console.error(`Error: ${errorMessage}`);
|
|
4039
|
-
continue;
|
|
4040
|
-
}
|
|
4041
|
-
const versionInfo = versionResponse.data;
|
|
4042
|
-
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
4043
|
-
const downloadHeaders = {};
|
|
4044
|
-
if (!isPresignedUrl && apiKey) {
|
|
4045
|
-
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
4046
|
-
}
|
|
4047
|
-
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
4048
|
-
headers: downloadHeaders,
|
|
4049
|
-
redirect: "follow"
|
|
4050
|
-
});
|
|
4051
|
-
if (!tarballResponse.ok) {
|
|
4052
|
-
console.error(
|
|
4053
|
-
`Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
|
|
4054
|
-
);
|
|
4055
|
-
continue;
|
|
4056
|
-
}
|
|
4057
|
-
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
4058
|
-
const integrity = calculateIntegrity(tarballBuffer);
|
|
4059
|
-
await addToLockfile(fullName, {
|
|
4060
|
-
version: resolved,
|
|
4061
|
-
resolved: versionInfo.downloadUrl,
|
|
4062
|
-
integrity
|
|
4063
|
-
});
|
|
4064
|
-
await writeToCache(cacheDir, integrity, tarballBuffer);
|
|
4065
|
-
console.log(` Resolved ${fullName}@${resolved}`);
|
|
4066
|
-
}
|
|
4067
|
-
lockfile = await readLockfile();
|
|
4212
|
+
const resolved = await resolveConfig();
|
|
4213
|
+
const projectConfig = await findProjectConfig();
|
|
4214
|
+
const configPath = getConfigPath();
|
|
4215
|
+
console.log("Resolved Configuration:\n");
|
|
4216
|
+
console.log(` Registry URL: ${resolved.registryUrl}`);
|
|
4217
|
+
console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
|
|
4218
|
+
console.log(` Username: ${resolved.username || "(not set)"}`);
|
|
4219
|
+
console.log("");
|
|
4220
|
+
console.log("Config Locations:");
|
|
4221
|
+
console.log(` User config: ${configPath}`);
|
|
4222
|
+
console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
|
|
4223
|
+
console.log("");
|
|
4224
|
+
console.log("Environment Variables:");
|
|
4225
|
+
console.log(
|
|
4226
|
+
` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
|
|
4227
|
+
);
|
|
4228
|
+
console.log(
|
|
4229
|
+
` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
|
|
4230
|
+
);
|
|
4231
|
+
} catch (error) {
|
|
4232
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4233
|
+
console.error(`Error: ${message}`);
|
|
4234
|
+
process.exit(1);
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
// src/commands/deprecate.ts
|
|
4239
|
+
init_api_client();
|
|
4240
|
+
init_config();
|
|
4241
|
+
init_lib();
|
|
4242
|
+
async function deprecate(specifier, message, options) {
|
|
4243
|
+
try {
|
|
4244
|
+
const apiKey = await requireApiKey();
|
|
4245
|
+
const registryUrl = await getRegistryUrl();
|
|
4246
|
+
const parsed = parseRegistrySpecifier2(specifier);
|
|
4247
|
+
if (!parsed) {
|
|
4248
|
+
console.error(
|
|
4249
|
+
`Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
|
|
4250
|
+
);
|
|
4251
|
+
process.exit(1);
|
|
4068
4252
|
}
|
|
4069
|
-
const
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4253
|
+
const { owner, name, versionRange } = parsed;
|
|
4254
|
+
const fullName = generateRegistryIdentifier2({
|
|
4255
|
+
namespace: parsed.namespace,
|
|
4256
|
+
owner,
|
|
4257
|
+
name
|
|
4258
|
+
});
|
|
4259
|
+
if (!versionRange) {
|
|
4260
|
+
console.error(
|
|
4261
|
+
"Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
|
|
4262
|
+
);
|
|
4263
|
+
process.exit(1);
|
|
4074
4264
|
}
|
|
4075
|
-
|
|
4076
|
-
|
|
4265
|
+
configure2({ registryUrl, apiKey });
|
|
4266
|
+
if (options.undo) {
|
|
4267
|
+
console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
|
|
4268
|
+
const response = await undeprecateSkillVersion(owner, name, versionRange);
|
|
4269
|
+
if (response.status !== 200) {
|
|
4077
4270
|
console.error(
|
|
4078
|
-
|
|
4271
|
+
`Error: ${response.error || "Failed to remove deprecation"}`
|
|
4079
4272
|
);
|
|
4080
|
-
console.error("Missing GitHub dependencies:");
|
|
4081
|
-
for (const dep of missingGitHubDeps) {
|
|
4082
|
-
console.error(` - ${dep.specifier}@${dep.ref}`);
|
|
4083
|
-
}
|
|
4084
4273
|
process.exit(1);
|
|
4085
4274
|
}
|
|
4086
|
-
console.log(
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4275
|
+
console.log(`Removed deprecation from ${fullName}@${versionRange}`);
|
|
4276
|
+
} else {
|
|
4277
|
+
if (!message) {
|
|
4278
|
+
console.error(
|
|
4279
|
+
"Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
|
|
4280
|
+
);
|
|
4281
|
+
process.exit(1);
|
|
4282
|
+
}
|
|
4283
|
+
console.log(`Deprecating ${fullName}@${versionRange}...`);
|
|
4284
|
+
const response = await deprecateSkillVersion(
|
|
4285
|
+
owner,
|
|
4286
|
+
name,
|
|
4287
|
+
versionRange,
|
|
4288
|
+
message
|
|
4090
4289
|
);
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
}
|
|
4097
|
-
parsed.ref = parsed.ref || ref;
|
|
4098
|
-
console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
|
|
4099
|
-
try {
|
|
4100
|
-
const result = await downloadGitHubPackage(parsed);
|
|
4101
|
-
await extractGitHubPackage(parsed, result.buffer, skillsDir);
|
|
4102
|
-
const entry = {
|
|
4103
|
-
version: result.commit.slice(0, 7),
|
|
4104
|
-
resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
|
|
4105
|
-
integrity: result.integrity,
|
|
4106
|
-
gitCommit: result.commit,
|
|
4107
|
-
gitRef: ref || "HEAD"
|
|
4108
|
-
};
|
|
4109
|
-
await addGitHubToLockfile(specifier, entry);
|
|
4110
|
-
await writeToCache(cacheDir, result.integrity, result.buffer);
|
|
4111
|
-
console.log(
|
|
4112
|
-
` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
|
|
4113
|
-
);
|
|
4114
|
-
} catch (error) {
|
|
4115
|
-
if (error instanceof GitHubRateLimitError) {
|
|
4116
|
-
console.error(`Error: ${error.message}`);
|
|
4117
|
-
} else if (error instanceof GitHubPathNotFoundError) {
|
|
4118
|
-
console.error(`Error: ${error.message}`);
|
|
4119
|
-
} else if (error instanceof GitHubNotFoundError) {
|
|
4120
|
-
console.error(`Error: ${error.message}`);
|
|
4121
|
-
} else {
|
|
4122
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4123
|
-
console.error(`Error resolving ${specifier}: ${message}`);
|
|
4124
|
-
}
|
|
4125
|
-
}
|
|
4290
|
+
if (response.status !== 200) {
|
|
4291
|
+
console.error(
|
|
4292
|
+
`Error: ${response.error || "Failed to deprecate version"}`
|
|
4293
|
+
);
|
|
4294
|
+
process.exit(1);
|
|
4126
4295
|
}
|
|
4127
|
-
|
|
4296
|
+
console.log(`Deprecated ${fullName}@${versionRange}`);
|
|
4297
|
+
console.log(`Message: ${message}`);
|
|
4298
|
+
console.log("");
|
|
4299
|
+
console.log(
|
|
4300
|
+
"Users installing this version will see a deprecation warning."
|
|
4301
|
+
);
|
|
4302
|
+
console.log("The package is still available for download.");
|
|
4128
4303
|
}
|
|
4129
|
-
|
|
4130
|
-
const
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4304
|
+
} catch (error) {
|
|
4305
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4306
|
+
console.error(`Error: ${errorMessage}`);
|
|
4307
|
+
process.exit(1);
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
// src/commands/init.ts
|
|
4312
|
+
init_lib();
|
|
4313
|
+
function prompt(rl, question, defaultValue) {
|
|
4314
|
+
return new Promise((resolve3) => {
|
|
4315
|
+
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
4316
|
+
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
4317
|
+
resolve3(answer.trim() || defaultValue);
|
|
4318
|
+
});
|
|
4319
|
+
});
|
|
4320
|
+
}
|
|
4321
|
+
async function readExistingPackageJson() {
|
|
4322
|
+
try {
|
|
4323
|
+
const content = await readFile(
|
|
4324
|
+
join(process.cwd(), "package.json"),
|
|
4325
|
+
"utf-8"
|
|
4326
|
+
);
|
|
4327
|
+
const pkg = JSON.parse(content);
|
|
4328
|
+
return {
|
|
4329
|
+
name: pkg.name,
|
|
4330
|
+
version: pkg.version,
|
|
4331
|
+
description: pkg.description,
|
|
4332
|
+
author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
|
|
4333
|
+
license: pkg.license
|
|
4334
|
+
};
|
|
4335
|
+
} catch {
|
|
4336
|
+
return null;
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
async function getGitAuthor() {
|
|
4340
|
+
try {
|
|
4341
|
+
const { exec: exec2 } = await import('child_process');
|
|
4342
|
+
const { promisify: promisify2 } = await import('util');
|
|
4343
|
+
const execAsync = promisify2(exec2);
|
|
4344
|
+
const [nameResult, emailResult] = await Promise.all([
|
|
4345
|
+
execAsync("git config user.name").catch(() => ({ stdout: "" })),
|
|
4346
|
+
execAsync("git config user.email").catch(() => ({ stdout: "" }))
|
|
4347
|
+
]);
|
|
4348
|
+
const name = nameResult.stdout.trim();
|
|
4349
|
+
const email = emailResult.stdout.trim();
|
|
4350
|
+
if (name && email) {
|
|
4351
|
+
return `${name} <${email}>`;
|
|
4142
4352
|
}
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4353
|
+
if (name) {
|
|
4354
|
+
return name;
|
|
4355
|
+
}
|
|
4356
|
+
return null;
|
|
4357
|
+
} catch {
|
|
4358
|
+
return null;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
function sanitizeName(name) {
|
|
4362
|
+
const withoutScope = name.replace(/^@[^/]+\//, "");
|
|
4363
|
+
return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
4364
|
+
}
|
|
4365
|
+
function isValidName(name) {
|
|
4366
|
+
return /^[a-z][a-z0-9_-]*$/.test(name);
|
|
4367
|
+
}
|
|
4368
|
+
function isValidVersion(version3) {
|
|
4369
|
+
return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
|
|
4370
|
+
}
|
|
4371
|
+
async function init(options) {
|
|
4372
|
+
try {
|
|
4373
|
+
const pspmJsonPath = join(process.cwd(), "pspm.json");
|
|
4374
|
+
let exists = false;
|
|
4375
|
+
try {
|
|
4376
|
+
await stat(pspmJsonPath);
|
|
4377
|
+
exists = true;
|
|
4378
|
+
} catch {
|
|
4379
|
+
}
|
|
4380
|
+
if (exists && !options.force) {
|
|
4381
|
+
console.error("Error: pspm.json already exists in this directory.");
|
|
4382
|
+
console.error("Use --force to overwrite.");
|
|
4383
|
+
process.exit(1);
|
|
4384
|
+
}
|
|
4385
|
+
const existingPkg = await readExistingPackageJson();
|
|
4386
|
+
const gitAuthor = await getGitAuthor();
|
|
4387
|
+
const defaultName = sanitizeName(
|
|
4388
|
+
options.name || existingPkg?.name || basename(process.cwd())
|
|
4389
|
+
);
|
|
4390
|
+
const defaultVersion = existingPkg?.version || "0.1.0";
|
|
4391
|
+
const defaultDescription = options.description || existingPkg?.description || "";
|
|
4392
|
+
const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
|
|
4393
|
+
const defaultLicense = existingPkg?.license || "MIT";
|
|
4394
|
+
const defaultMain = "SKILL.md";
|
|
4395
|
+
const defaultCapabilities = "";
|
|
4396
|
+
let manifest;
|
|
4397
|
+
if (options.yes) {
|
|
4398
|
+
manifest = {
|
|
4399
|
+
$schema: PSPM_SCHEMA_URL,
|
|
4400
|
+
name: defaultName,
|
|
4401
|
+
version: defaultVersion,
|
|
4402
|
+
description: defaultDescription || void 0,
|
|
4403
|
+
author: defaultAuthor || void 0,
|
|
4404
|
+
license: defaultLicense,
|
|
4405
|
+
type: "skill",
|
|
4406
|
+
capabilities: [],
|
|
4407
|
+
main: defaultMain,
|
|
4408
|
+
requirements: {
|
|
4409
|
+
pspm: ">=0.1.0"
|
|
4410
|
+
},
|
|
4411
|
+
files: [...DEFAULT_SKILL_FILES],
|
|
4412
|
+
dependencies: {},
|
|
4413
|
+
private: false
|
|
4414
|
+
};
|
|
4415
|
+
} else {
|
|
4416
|
+
console.log(
|
|
4417
|
+
"This utility will walk you through creating a pspm.json file."
|
|
4418
|
+
);
|
|
4419
|
+
console.log(
|
|
4420
|
+
"It only covers the most common items, and tries to guess sensible defaults."
|
|
4421
|
+
);
|
|
4422
|
+
console.log("");
|
|
4423
|
+
console.log(
|
|
4424
|
+
"See `pspm init --help` for definitive documentation on these fields"
|
|
4425
|
+
);
|
|
4426
|
+
console.log("and exactly what they do.");
|
|
4427
|
+
console.log("");
|
|
4428
|
+
console.log("Press ^C at any time to quit.");
|
|
4429
|
+
const rl = createInterface({
|
|
4430
|
+
input: process.stdin,
|
|
4431
|
+
output: process.stdout
|
|
4432
|
+
});
|
|
4433
|
+
try {
|
|
4434
|
+
let name = await prompt(rl, "skill name:", defaultName);
|
|
4435
|
+
while (!isValidName(name)) {
|
|
4436
|
+
console.log(
|
|
4437
|
+
" Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
|
|
4217
4438
|
);
|
|
4218
|
-
|
|
4219
|
-
destDir = join(skillsDir, pkgOwner, effectiveSkillName);
|
|
4439
|
+
name = await prompt(rl, "skill name:", sanitizeName(name));
|
|
4220
4440
|
}
|
|
4221
|
-
await
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
const { exec: exec2 } = await import('child_process');
|
|
4226
|
-
const { promisify: promisify2 } = await import('util');
|
|
4227
|
-
const execAsync = promisify2(exec2);
|
|
4228
|
-
try {
|
|
4229
|
-
await execAsync(
|
|
4230
|
-
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
4231
|
-
);
|
|
4232
|
-
} finally {
|
|
4233
|
-
await rm(tempFile, { force: true });
|
|
4441
|
+
let version3 = await prompt(rl, "version:", defaultVersion);
|
|
4442
|
+
while (!isValidVersion(version3)) {
|
|
4443
|
+
console.log(" Version must be valid semver (e.g., 1.0.0)");
|
|
4444
|
+
version3 = await prompt(rl, "version:", "0.1.0");
|
|
4234
4445
|
}
|
|
4235
|
-
|
|
4236
|
-
|
|
4446
|
+
const description = await prompt(
|
|
4447
|
+
rl,
|
|
4448
|
+
"description:",
|
|
4449
|
+
defaultDescription
|
|
4237
4450
|
);
|
|
4238
|
-
const
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
}
|
|
4244
|
-
}
|
|
4245
|
-
const githubPackages = lockfile?.githubPackages ?? {};
|
|
4246
|
-
const githubCount = Object.keys(githubPackages).length;
|
|
4247
|
-
if (githubCount > 0) {
|
|
4248
|
-
console.log(`
|
|
4249
|
-
Installing ${githubCount} GitHub skill(s)...
|
|
4250
|
-
`);
|
|
4251
|
-
for (const [specifier, entry] of Object.entries(githubPackages)) {
|
|
4252
|
-
const parsed = parseGitHubSpecifier2(specifier);
|
|
4253
|
-
if (!parsed) {
|
|
4254
|
-
console.warn(
|
|
4255
|
-
`Warning: Invalid GitHub specifier in lockfile: ${specifier}`
|
|
4256
|
-
);
|
|
4257
|
-
continue;
|
|
4258
|
-
}
|
|
4259
|
-
const ghEntry = entry;
|
|
4260
|
-
console.log(
|
|
4261
|
-
`Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
|
|
4451
|
+
const main = await prompt(rl, "entry point:", defaultMain);
|
|
4452
|
+
const capabilitiesStr = await prompt(
|
|
4453
|
+
rl,
|
|
4454
|
+
"capabilities (comma-separated):",
|
|
4455
|
+
defaultCapabilities
|
|
4262
4456
|
);
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
} else if (error instanceof GitHubNotFoundError) {
|
|
4290
|
-
console.error(` Error: ${error.message}`);
|
|
4291
|
-
} else {
|
|
4292
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4293
|
-
console.error(` Error downloading ${specifier}: ${message}`);
|
|
4294
|
-
}
|
|
4295
|
-
continue;
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4298
|
-
try {
|
|
4299
|
-
const destPath = await extractGitHubPackage(
|
|
4300
|
-
parsed,
|
|
4301
|
-
tarballBuffer,
|
|
4302
|
-
skillsDir
|
|
4303
|
-
);
|
|
4304
|
-
console.log(
|
|
4305
|
-
` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
|
|
4306
|
-
);
|
|
4307
|
-
const skillName = getGitHubSkillName2(parsed);
|
|
4308
|
-
installedSkills.push({
|
|
4309
|
-
name: skillName,
|
|
4310
|
-
sourcePath: getGitHubSkillPath(
|
|
4311
|
-
parsed.owner,
|
|
4312
|
-
parsed.repo,
|
|
4313
|
-
parsed.path
|
|
4314
|
-
)
|
|
4315
|
-
});
|
|
4316
|
-
} catch (error) {
|
|
4317
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4318
|
-
console.error(` Error extracting ${specifier}: ${message}`);
|
|
4457
|
+
const author = await prompt(rl, "author:", defaultAuthor);
|
|
4458
|
+
const license = await prompt(rl, "license:", defaultLicense);
|
|
4459
|
+
rl.close();
|
|
4460
|
+
const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
4461
|
+
manifest = {
|
|
4462
|
+
$schema: PSPM_SCHEMA_URL,
|
|
4463
|
+
name,
|
|
4464
|
+
version: version3,
|
|
4465
|
+
description: description || void 0,
|
|
4466
|
+
author: author || void 0,
|
|
4467
|
+
license,
|
|
4468
|
+
type: "skill",
|
|
4469
|
+
capabilities,
|
|
4470
|
+
main,
|
|
4471
|
+
requirements: {
|
|
4472
|
+
pspm: ">=0.1.0"
|
|
4473
|
+
},
|
|
4474
|
+
files: [...DEFAULT_SKILL_FILES],
|
|
4475
|
+
dependencies: {},
|
|
4476
|
+
private: false
|
|
4477
|
+
};
|
|
4478
|
+
} catch (error) {
|
|
4479
|
+
rl.close();
|
|
4480
|
+
if (error instanceof Error && error.message.includes("readline was closed")) {
|
|
4481
|
+
console.log("\nAborted.");
|
|
4482
|
+
process.exit(0);
|
|
4319
4483
|
}
|
|
4484
|
+
throw error;
|
|
4320
4485
|
}
|
|
4321
4486
|
}
|
|
4322
|
-
if (
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4487
|
+
if (!manifest.description) manifest.description = void 0;
|
|
4488
|
+
if (!manifest.author) manifest.author = void 0;
|
|
4489
|
+
if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
|
|
4490
|
+
const content = JSON.stringify(manifest, null, 2);
|
|
4491
|
+
console.log("");
|
|
4492
|
+
console.log(`About to write to ${pspmJsonPath}:`);
|
|
4493
|
+
console.log("");
|
|
4494
|
+
console.log(content);
|
|
4495
|
+
console.log("");
|
|
4496
|
+
if (!options.yes) {
|
|
4497
|
+
const rl = createInterface({
|
|
4498
|
+
input: process.stdin,
|
|
4499
|
+
output: process.stdout
|
|
4333
4500
|
});
|
|
4334
|
-
|
|
4501
|
+
const confirm2 = await prompt(rl, "Is this OK?", "yes");
|
|
4502
|
+
rl.close();
|
|
4503
|
+
if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
|
|
4504
|
+
console.log("Aborted.");
|
|
4505
|
+
process.exit(0);
|
|
4506
|
+
}
|
|
4335
4507
|
}
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4508
|
+
await writeFile(pspmJsonPath, `${content}
|
|
4509
|
+
`);
|
|
4510
|
+
try {
|
|
4511
|
+
await stat(join(process.cwd(), "SKILL.md"));
|
|
4512
|
+
} catch {
|
|
4513
|
+
console.log(
|
|
4514
|
+
"Note: Create a SKILL.md file with your skill's prompt content."
|
|
4515
|
+
);
|
|
4516
|
+
}
|
|
4517
|
+
if (existingPkg) {
|
|
4518
|
+
console.log("Note: Values were derived from existing package.json.");
|
|
4519
|
+
console.log(" pspm.json is for publishing to PSPM registry.");
|
|
4520
|
+
console.log(" package.json can still be used for npm dependencies.");
|
|
4342
4521
|
}
|
|
4343
4522
|
} catch (error) {
|
|
4344
4523
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -4347,6 +4526,9 @@ All ${totalCount} skill(s) installed.`);
|
|
|
4347
4526
|
}
|
|
4348
4527
|
}
|
|
4349
4528
|
|
|
4529
|
+
// src/commands/index.ts
|
|
4530
|
+
init_install();
|
|
4531
|
+
|
|
4350
4532
|
// src/commands/link.ts
|
|
4351
4533
|
init_agents();
|
|
4352
4534
|
init_lib();
|
|
@@ -4652,8 +4834,8 @@ function startCallbackServer(expectedState) {
|
|
|
4652
4834
|
let resolveToken;
|
|
4653
4835
|
let rejectToken;
|
|
4654
4836
|
let timeoutId;
|
|
4655
|
-
const tokenPromise = new Promise((
|
|
4656
|
-
resolveToken =
|
|
4837
|
+
const tokenPromise = new Promise((resolve3, reject) => {
|
|
4838
|
+
resolveToken = resolve3;
|
|
4657
4839
|
rejectToken = reject;
|
|
4658
4840
|
});
|
|
4659
4841
|
const server = http.createServer((req, res) => {
|
|
@@ -4907,6 +5089,92 @@ async function migrate(options) {
|
|
|
4907
5089
|
process.exit(1);
|
|
4908
5090
|
}
|
|
4909
5091
|
}
|
|
5092
|
+
|
|
5093
|
+
// src/commands/notebook.ts
|
|
5094
|
+
init_config();
|
|
5095
|
+
async function fetchApi(path, options = {}) {
|
|
5096
|
+
const config2 = await resolveConfig();
|
|
5097
|
+
const apiKey = await requireApiKey();
|
|
5098
|
+
const baseUrl = config2.registryUrl.replace(/\/api\/skills\/?$/, "");
|
|
5099
|
+
const headers = {
|
|
5100
|
+
"Content-Type": "application/json",
|
|
5101
|
+
...options.headers
|
|
5102
|
+
};
|
|
5103
|
+
if (apiKey) {
|
|
5104
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
5105
|
+
}
|
|
5106
|
+
return fetch(`${baseUrl}${path}`, { ...options, headers });
|
|
5107
|
+
}
|
|
5108
|
+
async function notebookUpload(filePath, options) {
|
|
5109
|
+
const absPath = resolve(filePath);
|
|
5110
|
+
const content = readFileSync(absPath, "utf-8");
|
|
5111
|
+
const name = basename(filePath).replace(/\.anyt\.md$|\.anyt$|\.md$/, "");
|
|
5112
|
+
const body = {
|
|
5113
|
+
name,
|
|
5114
|
+
content,
|
|
5115
|
+
description: options.description,
|
|
5116
|
+
visibility: options.visibility || "private"
|
|
5117
|
+
};
|
|
5118
|
+
const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks";
|
|
5119
|
+
const response = await fetchApi(path, {
|
|
5120
|
+
method: "POST",
|
|
5121
|
+
body: JSON.stringify(body)
|
|
5122
|
+
});
|
|
5123
|
+
if (!response.ok) {
|
|
5124
|
+
const err = await response.json().catch(() => ({}));
|
|
5125
|
+
throw new Error(
|
|
5126
|
+
err.message || `Upload failed (${response.status})`
|
|
5127
|
+
);
|
|
5128
|
+
}
|
|
5129
|
+
const notebook = await response.json();
|
|
5130
|
+
console.log(
|
|
5131
|
+
`Uploaded: ${notebook.name} (${notebook.id})${options.org ? ` to org ${options.org}` : ""}`
|
|
5132
|
+
);
|
|
5133
|
+
}
|
|
5134
|
+
async function notebookList(options) {
|
|
5135
|
+
const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks/-/mine";
|
|
5136
|
+
const response = await fetchApi(path);
|
|
5137
|
+
if (!response.ok) {
|
|
5138
|
+
throw new Error(`Failed to list notebooks (${response.status})`);
|
|
5139
|
+
}
|
|
5140
|
+
const notebooks = await response.json();
|
|
5141
|
+
if (notebooks.length === 0) {
|
|
5142
|
+
console.log("No notebooks found");
|
|
5143
|
+
return;
|
|
5144
|
+
}
|
|
5145
|
+
console.log(
|
|
5146
|
+
`
|
|
5147
|
+
${"Name".padEnd(30)} ${"Cells".padEnd(8)} ${"Visibility".padEnd(12)} ${"Updated".padEnd(12)} ID`
|
|
5148
|
+
);
|
|
5149
|
+
console.log("-".repeat(90));
|
|
5150
|
+
for (const nb of notebooks) {
|
|
5151
|
+
const updated = new Date(nb.updatedAt).toLocaleDateString();
|
|
5152
|
+
console.log(
|
|
5153
|
+
`${nb.name.slice(0, 28).padEnd(30)} ${String(nb.cellCount).padEnd(8)} ${nb.visibility.padEnd(12)} ${updated.padEnd(12)} ${nb.id}`
|
|
5154
|
+
);
|
|
5155
|
+
}
|
|
5156
|
+
console.log(`
|
|
5157
|
+
${notebooks.length} notebook(s)`);
|
|
5158
|
+
}
|
|
5159
|
+
async function notebookDownload(id, output) {
|
|
5160
|
+
const response = await fetchApi(`/api/notebooks/${id}`);
|
|
5161
|
+
if (!response.ok) {
|
|
5162
|
+
throw new Error(`Notebook not found (${response.status})`);
|
|
5163
|
+
}
|
|
5164
|
+
const notebook = await response.json();
|
|
5165
|
+
const outPath = output || `${notebook.slug}.anyt.md`;
|
|
5166
|
+
writeFileSync(outPath, notebook.content, "utf-8");
|
|
5167
|
+
console.log(`Downloaded: ${notebook.name} -> ${outPath}`);
|
|
5168
|
+
}
|
|
5169
|
+
async function notebookDelete(id) {
|
|
5170
|
+
const response = await fetchApi(`/api/notebooks/${id}`, {
|
|
5171
|
+
method: "DELETE"
|
|
5172
|
+
});
|
|
5173
|
+
if (!response.ok) {
|
|
5174
|
+
throw new Error(`Failed to delete notebook (${response.status})`);
|
|
5175
|
+
}
|
|
5176
|
+
console.log(`Notebook ${id} deleted`);
|
|
5177
|
+
}
|
|
4910
5178
|
function resolveVersion3(range, availableVersions) {
|
|
4911
5179
|
const sorted = availableVersions.filter((v) => semver2.valid(v)).sort((a, b) => semver2.rcompare(a, b));
|
|
4912
5180
|
if (range === "latest") {
|
|
@@ -4923,7 +5191,7 @@ function getLatestVersion3(versions) {
|
|
|
4923
5191
|
return valid5.sort((a, b) => semver2.rcompare(a, b))[0];
|
|
4924
5192
|
}
|
|
4925
5193
|
|
|
4926
|
-
// ../../packages/
|
|
5194
|
+
// ../../packages/features/skill-registry/src/client/outdated.ts
|
|
4927
5195
|
function createOutdatedChecker(config2) {
|
|
4928
5196
|
const { registryUrl, apiKey, githubToken } = config2;
|
|
4929
5197
|
async function fetchWithAuth(url, token) {
|
|
@@ -4943,14 +5211,26 @@ function createOutdatedChecker(config2) {
|
|
|
4943
5211
|
const response = await fetchWithAuth(url, apiKey);
|
|
4944
5212
|
return await response.json();
|
|
4945
5213
|
}
|
|
5214
|
+
async function fetchGithubRegistryVersions(owner, repo, skillname) {
|
|
5215
|
+
const url = `${registryUrl}/@github/${owner}/${repo}/${skillname}/versions`;
|
|
5216
|
+
const response = await fetchWithAuth(url, apiKey);
|
|
5217
|
+
return await response.json();
|
|
5218
|
+
}
|
|
4946
5219
|
async function checkRegistryPackage(specifier, entry, versionRange) {
|
|
4947
|
-
const
|
|
4948
|
-
|
|
5220
|
+
const userMatch = specifier.match(/^@(?:user|org)\/([^/]+)\/([^/]+)$/);
|
|
5221
|
+
const githubMatch = specifier.match(/^@github\/([^/]+)\/([^/]+)\/([^/]+)$/);
|
|
5222
|
+
if (!userMatch && !githubMatch) {
|
|
4949
5223
|
throw new Error(`Invalid registry specifier: ${specifier}`);
|
|
4950
5224
|
}
|
|
4951
|
-
const
|
|
5225
|
+
const isGithubRegistry = !!githubMatch;
|
|
5226
|
+
const username = userMatch ? userMatch[1] : "";
|
|
5227
|
+
const name = userMatch ? userMatch[2] : "";
|
|
4952
5228
|
try {
|
|
4953
|
-
const versions = await
|
|
5229
|
+
const versions = isGithubRegistry && githubMatch ? await fetchGithubRegistryVersions(
|
|
5230
|
+
githubMatch[1],
|
|
5231
|
+
githubMatch[2],
|
|
5232
|
+
githubMatch[3]
|
|
5233
|
+
) : await fetchRegistryVersions(username, name);
|
|
4954
5234
|
const versionStrings = versions.map((v) => v.version);
|
|
4955
5235
|
const range = versionRange || "*";
|
|
4956
5236
|
const wanted = resolveVersion3(range, versionStrings);
|
|
@@ -5176,7 +5456,7 @@ init_errors();
|
|
|
5176
5456
|
init_lib();
|
|
5177
5457
|
var exec = promisify(exec$1);
|
|
5178
5458
|
function confirm(question) {
|
|
5179
|
-
return new Promise((
|
|
5459
|
+
return new Promise((resolve3) => {
|
|
5180
5460
|
const rl = createInterface({
|
|
5181
5461
|
input: process.stdin,
|
|
5182
5462
|
output: process.stdout
|
|
@@ -5184,7 +5464,7 @@ function confirm(question) {
|
|
|
5184
5464
|
rl.question(`${question} (y/N) `, (answer) => {
|
|
5185
5465
|
rl.close();
|
|
5186
5466
|
const normalized = answer.trim().toLowerCase();
|
|
5187
|
-
|
|
5467
|
+
resolve3(normalized === "y" || normalized === "yes");
|
|
5188
5468
|
});
|
|
5189
5469
|
});
|
|
5190
5470
|
}
|
|
@@ -5294,8 +5574,10 @@ async function publishCommand(options) {
|
|
|
5294
5574
|
}
|
|
5295
5575
|
console.log("");
|
|
5296
5576
|
}
|
|
5577
|
+
const nameParts = manifest.name.split("/");
|
|
5578
|
+
const bareName = nameParts[nameParts.length - 1];
|
|
5297
5579
|
const packageJson2 = {
|
|
5298
|
-
name:
|
|
5580
|
+
name: bareName,
|
|
5299
5581
|
version: manifest.version,
|
|
5300
5582
|
description: manifest.description,
|
|
5301
5583
|
files: manifest.files,
|
|
@@ -5500,8 +5782,8 @@ async function removeRegistry(specifier, agents, agentConfigs) {
|
|
|
5500
5782
|
console.error(`Error: Invalid skill specifier: ${specifier}`);
|
|
5501
5783
|
process.exit(1);
|
|
5502
5784
|
}
|
|
5503
|
-
const { namespace, owner, name } = parsed;
|
|
5504
|
-
const fullName = `@${namespace}/${owner}/${name}`;
|
|
5785
|
+
const { namespace, owner, name, subname } = parsed;
|
|
5786
|
+
const fullName = namespace === "github" && subname ? `@github/${owner}/${name}/${subname}` : `@${namespace}/${owner}/${name}`;
|
|
5505
5787
|
console.log(`Removing ${fullName}...`);
|
|
5506
5788
|
const removedFromLockfile = await removeFromLockfile(fullName);
|
|
5507
5789
|
const removedFromManifest = await removeDependency(fullName);
|
|
@@ -5509,13 +5791,21 @@ async function removeRegistry(specifier, agents, agentConfigs) {
|
|
|
5509
5791
|
console.error(`Error: ${fullName} not found in lockfile or pspm.json`);
|
|
5510
5792
|
process.exit(1);
|
|
5511
5793
|
}
|
|
5512
|
-
|
|
5794
|
+
const symlinkName = subname ?? name;
|
|
5795
|
+
await removeAgentSymlinks(symlinkName, {
|
|
5513
5796
|
agents,
|
|
5514
5797
|
projectRoot: process.cwd(),
|
|
5515
5798
|
agentConfigs
|
|
5516
5799
|
});
|
|
5517
5800
|
const skillsDir = getSkillsDir();
|
|
5518
|
-
|
|
5801
|
+
let destDir;
|
|
5802
|
+
if (namespace === "github" && subname) {
|
|
5803
|
+
destDir = join(skillsDir, "_github-registry", owner, name, subname);
|
|
5804
|
+
} else if (namespace === "org") {
|
|
5805
|
+
destDir = join(skillsDir, "_org", owner, name);
|
|
5806
|
+
} else {
|
|
5807
|
+
destDir = join(skillsDir, owner, name);
|
|
5808
|
+
}
|
|
5519
5809
|
try {
|
|
5520
5810
|
await rm(destDir, { recursive: true, force: true });
|
|
5521
5811
|
} catch {
|
|
@@ -5555,7 +5845,9 @@ async function removeByShortName(shortName, agents, agentConfigs) {
|
|
|
5555
5845
|
const registrySkills = await listLockfileSkills();
|
|
5556
5846
|
const foundRegistry = registrySkills.find((s) => {
|
|
5557
5847
|
const parsed = parseRegistrySpecifier2(s.name);
|
|
5558
|
-
|
|
5848
|
+
if (!parsed) return false;
|
|
5849
|
+
const effectiveName = parsed.subname ?? parsed.name;
|
|
5850
|
+
return effectiveName === shortName;
|
|
5559
5851
|
});
|
|
5560
5852
|
if (foundRegistry) {
|
|
5561
5853
|
await removeRegistry(foundRegistry.name, agents, agentConfigs);
|
|
@@ -5657,6 +5949,396 @@ function formatDownloads(count) {
|
|
|
5657
5949
|
return String(count);
|
|
5658
5950
|
}
|
|
5659
5951
|
|
|
5952
|
+
// src/commands/skill-list.ts
|
|
5953
|
+
init_api_client();
|
|
5954
|
+
init_config();
|
|
5955
|
+
function parseListSpecifier(specifier) {
|
|
5956
|
+
const match = specifier.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
|
|
5957
|
+
if (!match) return null;
|
|
5958
|
+
return {
|
|
5959
|
+
ownerType: match[1],
|
|
5960
|
+
ownerName: match[2],
|
|
5961
|
+
listName: match[3]
|
|
5962
|
+
};
|
|
5963
|
+
}
|
|
5964
|
+
function extractErrorMessage(text) {
|
|
5965
|
+
try {
|
|
5966
|
+
const json = JSON.parse(text);
|
|
5967
|
+
if (typeof json.message === "string") return json.message;
|
|
5968
|
+
if (typeof json.error === "string") return json.error;
|
|
5969
|
+
if (typeof json.error?.message === "string") return json.error.message;
|
|
5970
|
+
} catch {
|
|
5971
|
+
}
|
|
5972
|
+
return text;
|
|
5973
|
+
}
|
|
5974
|
+
async function getBaseUrl() {
|
|
5975
|
+
const config2 = await resolveConfig();
|
|
5976
|
+
return config2.registryUrl.replace(/\/api\/skills\/?$/, "");
|
|
5977
|
+
}
|
|
5978
|
+
async function getAuthHeaders() {
|
|
5979
|
+
const apiKey = await requireApiKey();
|
|
5980
|
+
return {
|
|
5981
|
+
"Content-Type": "application/json",
|
|
5982
|
+
Authorization: `Bearer ${apiKey}`
|
|
5983
|
+
};
|
|
5984
|
+
}
|
|
5985
|
+
async function getOptionalAuthHeaders() {
|
|
5986
|
+
const config2 = await resolveConfig();
|
|
5987
|
+
const apiKey = getTokenForRegistry(config2, config2.registryUrl);
|
|
5988
|
+
const headers = {
|
|
5989
|
+
"Content-Type": "application/json"
|
|
5990
|
+
};
|
|
5991
|
+
if (apiKey) {
|
|
5992
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
5993
|
+
}
|
|
5994
|
+
return headers;
|
|
5995
|
+
}
|
|
5996
|
+
async function skillListList(options) {
|
|
5997
|
+
const baseUrl = await getBaseUrl();
|
|
5998
|
+
const config2 = await resolveConfig();
|
|
5999
|
+
let path;
|
|
6000
|
+
if (options.org) {
|
|
6001
|
+
path = `/api/skill-lists/lists/@org/${options.org}`;
|
|
6002
|
+
} else {
|
|
6003
|
+
if (!config2.username) {
|
|
6004
|
+
console.error("Error: Not logged in. Run `pspm login` first.");
|
|
6005
|
+
process.exit(1);
|
|
6006
|
+
}
|
|
6007
|
+
path = `/api/skill-lists/lists/@user/${config2.username}`;
|
|
6008
|
+
}
|
|
6009
|
+
const headers = await getOptionalAuthHeaders();
|
|
6010
|
+
const response = await fetch(`${baseUrl}${path}`, { headers });
|
|
6011
|
+
if (!response.ok) {
|
|
6012
|
+
const errorText = await response.text();
|
|
6013
|
+
console.error(
|
|
6014
|
+
`Error: Failed to list skill lists (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6015
|
+
);
|
|
6016
|
+
process.exit(1);
|
|
6017
|
+
}
|
|
6018
|
+
const lists = await response.json();
|
|
6019
|
+
if (options.json) {
|
|
6020
|
+
console.log(JSON.stringify(lists, null, 2));
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
if (lists.length === 0) {
|
|
6024
|
+
console.log("No skill lists found.");
|
|
6025
|
+
return;
|
|
6026
|
+
}
|
|
6027
|
+
const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
|
|
6028
|
+
console.log(`
|
|
6029
|
+
Skill lists for ${ownerLabel}:
|
|
6030
|
+
`);
|
|
6031
|
+
console.log(
|
|
6032
|
+
`${"Name".padEnd(35)} ${"Skills".padEnd(8)} ${"Visibility".padEnd(12)} Description`
|
|
6033
|
+
);
|
|
6034
|
+
console.log("-".repeat(90));
|
|
6035
|
+
for (const list2 of lists) {
|
|
6036
|
+
const desc = list2.description ? list2.description.slice(0, 30) : "";
|
|
6037
|
+
console.log(
|
|
6038
|
+
`${list2.name.slice(0, 33).padEnd(35)} ${String(list2.itemCount).padEnd(8)} ${list2.visibility.padEnd(12)} ${desc}`
|
|
6039
|
+
);
|
|
6040
|
+
}
|
|
6041
|
+
console.log(`
|
|
6042
|
+
${lists.length} list(s)`);
|
|
6043
|
+
}
|
|
6044
|
+
async function skillListCreate(name, options) {
|
|
6045
|
+
const baseUrl = await getBaseUrl();
|
|
6046
|
+
const headers = await getAuthHeaders();
|
|
6047
|
+
const config2 = await resolveConfig();
|
|
6048
|
+
const visibility = options.visibility || "private";
|
|
6049
|
+
if (visibility !== "public" && visibility !== "private") {
|
|
6050
|
+
console.error('Error: --visibility must be "public" or "private"');
|
|
6051
|
+
process.exit(1);
|
|
6052
|
+
}
|
|
6053
|
+
let path;
|
|
6054
|
+
if (options.org) {
|
|
6055
|
+
path = `/api/skill-lists/lists/@org/${options.org}`;
|
|
6056
|
+
} else {
|
|
6057
|
+
if (!config2.username) {
|
|
6058
|
+
console.error("Error: Not logged in. Run `pspm login` first.");
|
|
6059
|
+
process.exit(1);
|
|
6060
|
+
}
|
|
6061
|
+
path = `/api/skill-lists/lists/@user/${config2.username}`;
|
|
6062
|
+
}
|
|
6063
|
+
const body = { name, visibility };
|
|
6064
|
+
if (options.description) {
|
|
6065
|
+
body.description = options.description;
|
|
6066
|
+
}
|
|
6067
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6068
|
+
method: "POST",
|
|
6069
|
+
headers,
|
|
6070
|
+
body: JSON.stringify(body)
|
|
6071
|
+
});
|
|
6072
|
+
if (!response.ok) {
|
|
6073
|
+
const errorText = await response.text();
|
|
6074
|
+
console.error(
|
|
6075
|
+
`Error: Failed to create list (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6076
|
+
);
|
|
6077
|
+
process.exit(1);
|
|
6078
|
+
}
|
|
6079
|
+
const list2 = await response.json();
|
|
6080
|
+
const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
|
|
6081
|
+
console.log(`Created list: ${ownerLabel}/${list2.name} (${list2.visibility})`);
|
|
6082
|
+
console.log(
|
|
6083
|
+
`
|
|
6084
|
+
Install command: pspm skill-list install ${ownerLabel}/${list2.name}`
|
|
6085
|
+
);
|
|
6086
|
+
}
|
|
6087
|
+
async function skillListShow(specifier, options) {
|
|
6088
|
+
const parsed = parseListSpecifier(specifier);
|
|
6089
|
+
if (!parsed) {
|
|
6090
|
+
console.error(
|
|
6091
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6092
|
+
);
|
|
6093
|
+
process.exit(1);
|
|
6094
|
+
}
|
|
6095
|
+
const config2 = await resolveConfig();
|
|
6096
|
+
configure2({
|
|
6097
|
+
registryUrl: config2.registryUrl,
|
|
6098
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
6099
|
+
});
|
|
6100
|
+
const response = await fetchSkillList(
|
|
6101
|
+
parsed.ownerType,
|
|
6102
|
+
parsed.ownerName,
|
|
6103
|
+
parsed.listName
|
|
6104
|
+
);
|
|
6105
|
+
if (response.status !== 200 || !response.data) {
|
|
6106
|
+
const errorMsg = response.status === 404 ? `List "${specifier}" not found or is private.` : response.error || "Failed to fetch list";
|
|
6107
|
+
console.error(`Error: ${errorMsg}`);
|
|
6108
|
+
process.exit(1);
|
|
6109
|
+
}
|
|
6110
|
+
const list2 = response.data;
|
|
6111
|
+
if (options.json) {
|
|
6112
|
+
console.log(JSON.stringify(list2, null, 2));
|
|
6113
|
+
return;
|
|
6114
|
+
}
|
|
6115
|
+
console.log(`
|
|
6116
|
+
${list2.name}`);
|
|
6117
|
+
if (list2.description) {
|
|
6118
|
+
console.log(` ${list2.description}`);
|
|
6119
|
+
}
|
|
6120
|
+
console.log(` Visibility: ${list2.visibility}`);
|
|
6121
|
+
console.log(` Owner: @${list2.ownerType}/${list2.ownerName}`);
|
|
6122
|
+
console.log(` Skills: ${list2.items.length}`);
|
|
6123
|
+
if (list2.items.length > 0) {
|
|
6124
|
+
console.log("");
|
|
6125
|
+
for (const item of list2.items) {
|
|
6126
|
+
const ns = item.namespace === "org" ? "org" : "user";
|
|
6127
|
+
const spec = `@${ns}/${item.ownerName}/${item.skillName}`;
|
|
6128
|
+
const ver = item.pinnedVersion ? `@${item.pinnedVersion}` : "";
|
|
6129
|
+
console.log(` - ${spec}${ver}`);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
console.log(`
|
|
6133
|
+
Install all: pspm skill-list install ${specifier}`);
|
|
6134
|
+
}
|
|
6135
|
+
async function skillListDelete(specifier) {
|
|
6136
|
+
const parsed = parseListSpecifier(specifier);
|
|
6137
|
+
if (!parsed) {
|
|
6138
|
+
console.error(
|
|
6139
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6140
|
+
);
|
|
6141
|
+
process.exit(1);
|
|
6142
|
+
}
|
|
6143
|
+
const baseUrl = await getBaseUrl();
|
|
6144
|
+
const headers = await getAuthHeaders();
|
|
6145
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
|
|
6146
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6147
|
+
method: "DELETE",
|
|
6148
|
+
headers
|
|
6149
|
+
});
|
|
6150
|
+
if (!response.ok) {
|
|
6151
|
+
const errorText = await response.text();
|
|
6152
|
+
if (response.status === 404) {
|
|
6153
|
+
console.error(`Error: List "${specifier}" not found.`);
|
|
6154
|
+
} else {
|
|
6155
|
+
console.error(
|
|
6156
|
+
`Error: Failed to delete list (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6157
|
+
);
|
|
6158
|
+
}
|
|
6159
|
+
process.exit(1);
|
|
6160
|
+
}
|
|
6161
|
+
console.log(`Deleted list: ${specifier}`);
|
|
6162
|
+
}
|
|
6163
|
+
async function skillListUpdate(specifier, options) {
|
|
6164
|
+
const parsed = parseListSpecifier(specifier);
|
|
6165
|
+
if (!parsed) {
|
|
6166
|
+
console.error(
|
|
6167
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6168
|
+
);
|
|
6169
|
+
process.exit(1);
|
|
6170
|
+
}
|
|
6171
|
+
if (!options.description && !options.visibility) {
|
|
6172
|
+
console.error(
|
|
6173
|
+
"Error: Provide at least one of --description or --visibility"
|
|
6174
|
+
);
|
|
6175
|
+
process.exit(1);
|
|
6176
|
+
}
|
|
6177
|
+
if (options.visibility && options.visibility !== "public" && options.visibility !== "private") {
|
|
6178
|
+
console.error('Error: --visibility must be "public" or "private"');
|
|
6179
|
+
process.exit(1);
|
|
6180
|
+
}
|
|
6181
|
+
const baseUrl = await getBaseUrl();
|
|
6182
|
+
const headers = await getAuthHeaders();
|
|
6183
|
+
const body = {};
|
|
6184
|
+
if (options.description !== void 0) {
|
|
6185
|
+
body.description = options.description;
|
|
6186
|
+
}
|
|
6187
|
+
if (options.visibility) {
|
|
6188
|
+
body.visibility = options.visibility;
|
|
6189
|
+
}
|
|
6190
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
|
|
6191
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6192
|
+
method: "PUT",
|
|
6193
|
+
headers,
|
|
6194
|
+
body: JSON.stringify(body)
|
|
6195
|
+
});
|
|
6196
|
+
if (!response.ok) {
|
|
6197
|
+
const errorText = await response.text();
|
|
6198
|
+
console.error(
|
|
6199
|
+
`Error: Failed to update list (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6200
|
+
);
|
|
6201
|
+
process.exit(1);
|
|
6202
|
+
}
|
|
6203
|
+
console.log(`Updated list: ${specifier}`);
|
|
6204
|
+
}
|
|
6205
|
+
async function skillListAddSkill(specifier, skillSpecifiers, options) {
|
|
6206
|
+
const parsed = parseListSpecifier(specifier);
|
|
6207
|
+
if (!parsed) {
|
|
6208
|
+
console.error(
|
|
6209
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6210
|
+
);
|
|
6211
|
+
process.exit(1);
|
|
6212
|
+
}
|
|
6213
|
+
const baseUrl = await getBaseUrl();
|
|
6214
|
+
const headers = await getAuthHeaders();
|
|
6215
|
+
const config2 = await resolveConfig();
|
|
6216
|
+
configure2({
|
|
6217
|
+
registryUrl: config2.registryUrl,
|
|
6218
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
6219
|
+
});
|
|
6220
|
+
for (const skillSpec of skillSpecifiers) {
|
|
6221
|
+
const skillId = await resolveSkillId(baseUrl, headers, skillSpec);
|
|
6222
|
+
if (!skillId) {
|
|
6223
|
+
console.error(`Error: Skill "${skillSpec}" not found.`);
|
|
6224
|
+
continue;
|
|
6225
|
+
}
|
|
6226
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items`;
|
|
6227
|
+
const addBody = { skillId };
|
|
6228
|
+
if (options.note) {
|
|
6229
|
+
addBody.note = options.note;
|
|
6230
|
+
}
|
|
6231
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6232
|
+
method: "POST",
|
|
6233
|
+
headers,
|
|
6234
|
+
body: JSON.stringify(addBody)
|
|
6235
|
+
});
|
|
6236
|
+
if (!response.ok) {
|
|
6237
|
+
const errorText = await response.text();
|
|
6238
|
+
if (response.status === 409) {
|
|
6239
|
+
console.log(`Already in list: ${skillSpec}`);
|
|
6240
|
+
} else {
|
|
6241
|
+
console.error(
|
|
6242
|
+
`Error: Failed to add "${skillSpec}" (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6243
|
+
);
|
|
6244
|
+
}
|
|
6245
|
+
continue;
|
|
6246
|
+
}
|
|
6247
|
+
console.log(`Added: ${skillSpec}`);
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
async function skillListRemoveSkill(specifier, skillSpecifier) {
|
|
6251
|
+
const parsed = parseListSpecifier(specifier);
|
|
6252
|
+
if (!parsed) {
|
|
6253
|
+
console.error(
|
|
6254
|
+
"Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
|
|
6255
|
+
);
|
|
6256
|
+
process.exit(1);
|
|
6257
|
+
}
|
|
6258
|
+
const config2 = await resolveConfig();
|
|
6259
|
+
configure2({
|
|
6260
|
+
registryUrl: config2.registryUrl,
|
|
6261
|
+
apiKey: getTokenForRegistry(config2, config2.registryUrl)
|
|
6262
|
+
});
|
|
6263
|
+
const listResponse = await fetchSkillList(
|
|
6264
|
+
parsed.ownerType,
|
|
6265
|
+
parsed.ownerName,
|
|
6266
|
+
parsed.listName
|
|
6267
|
+
);
|
|
6268
|
+
if (listResponse.status !== 200 || !listResponse.data) {
|
|
6269
|
+
console.error(`Error: List "${specifier}" not found.`);
|
|
6270
|
+
process.exit(1);
|
|
6271
|
+
}
|
|
6272
|
+
const item = listResponse.data.items.find((i) => {
|
|
6273
|
+
const ns = i.namespace === "org" ? "org" : "user";
|
|
6274
|
+
const fullSpec = `@${ns}/${i.ownerName}/${i.skillName}`;
|
|
6275
|
+
return fullSpec === skillSpecifier || i.skillName === skillSpecifier || `${i.ownerName}/${i.skillName}` === skillSpecifier;
|
|
6276
|
+
});
|
|
6277
|
+
if (!item) {
|
|
6278
|
+
console.error(
|
|
6279
|
+
`Error: Skill "${skillSpecifier}" not found in list "${specifier}".`
|
|
6280
|
+
);
|
|
6281
|
+
process.exit(1);
|
|
6282
|
+
}
|
|
6283
|
+
const baseUrl = await getBaseUrl();
|
|
6284
|
+
const headers = await getAuthHeaders();
|
|
6285
|
+
const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items/${item.id}`;
|
|
6286
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
6287
|
+
method: "DELETE",
|
|
6288
|
+
headers
|
|
6289
|
+
});
|
|
6290
|
+
if (!response.ok) {
|
|
6291
|
+
const errorText = await response.text();
|
|
6292
|
+
console.error(
|
|
6293
|
+
`Error: Failed to remove skill (${response.status}): ${extractErrorMessage(errorText)}`
|
|
6294
|
+
);
|
|
6295
|
+
process.exit(1);
|
|
6296
|
+
}
|
|
6297
|
+
console.log(`Removed: ${skillSpecifier} from ${specifier}`);
|
|
6298
|
+
}
|
|
6299
|
+
async function skillListInstall(specifier, options) {
|
|
6300
|
+
const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
6301
|
+
await install2([], {
|
|
6302
|
+
list: specifier,
|
|
6303
|
+
agent: options.agent,
|
|
6304
|
+
yes: options.yes,
|
|
6305
|
+
global: options.global,
|
|
6306
|
+
dir: options.dir
|
|
6307
|
+
});
|
|
6308
|
+
}
|
|
6309
|
+
async function resolveSkillId(baseUrl, headers, specifier) {
|
|
6310
|
+
const match = specifier.match(/^@(user|org|github)\/(.+)$/);
|
|
6311
|
+
let search2;
|
|
6312
|
+
let namespace;
|
|
6313
|
+
if (match) {
|
|
6314
|
+
namespace = match[1];
|
|
6315
|
+
const parts = match[2].split("/");
|
|
6316
|
+
search2 = parts[parts.length - 1];
|
|
6317
|
+
} else {
|
|
6318
|
+
search2 = specifier;
|
|
6319
|
+
}
|
|
6320
|
+
const params = new URLSearchParams({ search: search2, limit: "5" });
|
|
6321
|
+
if (namespace) {
|
|
6322
|
+
params.set("namespace", namespace);
|
|
6323
|
+
}
|
|
6324
|
+
const response = await fetch(`${baseUrl}/api/skills/-/explore?${params}`, {
|
|
6325
|
+
headers
|
|
6326
|
+
});
|
|
6327
|
+
if (!response.ok) return null;
|
|
6328
|
+
const data = await response.json();
|
|
6329
|
+
if (!data.skills || data.skills.length === 0) return null;
|
|
6330
|
+
if (match) {
|
|
6331
|
+
const parts = match[2].split("/");
|
|
6332
|
+
const skillName = parts[parts.length - 1];
|
|
6333
|
+
const ownerName = parts.length >= 2 ? parts[parts.length - 2] : void 0;
|
|
6334
|
+
const exact = data.skills.find(
|
|
6335
|
+
(s) => s.name === skillName && (!ownerName || s.username === ownerName) && (!namespace || s.namespace === namespace)
|
|
6336
|
+
);
|
|
6337
|
+
if (exact) return exact.id;
|
|
6338
|
+
}
|
|
6339
|
+
return data.skills[0].id;
|
|
6340
|
+
}
|
|
6341
|
+
|
|
5660
6342
|
// src/commands/unpublish.ts
|
|
5661
6343
|
init_api_client();
|
|
5662
6344
|
init_config();
|
|
@@ -6100,11 +6782,15 @@ program.command("install [specifiers...]").alias("i").description(
|
|
|
6100
6782
|
).option("--frozen-lockfile", "Fail if lockfile is missing or outdated").option("--dir <path>", "Install skills to a specific directory").option(
|
|
6101
6783
|
"--agent <agents>",
|
|
6102
6784
|
'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
|
|
6785
|
+
).option(
|
|
6786
|
+
"--list <specifier>",
|
|
6787
|
+
"Install all skills from a skill list (e.g. @user/username/list-name)"
|
|
6103
6788
|
).option("-g, --global", "Install to user home directory instead of project").option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifiers, options) => {
|
|
6104
6789
|
await install(specifiers, {
|
|
6105
6790
|
frozenLockfile: options.frozenLockfile,
|
|
6106
6791
|
dir: options.dir,
|
|
6107
6792
|
agent: options.agent,
|
|
6793
|
+
list: options.list,
|
|
6108
6794
|
yes: options.yes,
|
|
6109
6795
|
global: options.global
|
|
6110
6796
|
});
|
|
@@ -6185,6 +6871,73 @@ program.command("deprecate <specifier> [message]").description(
|
|
|
6185
6871
|
).option("--undo", "Remove deprecation status").action(async (specifier, message, options) => {
|
|
6186
6872
|
await deprecate(specifier, message, { undo: options.undo });
|
|
6187
6873
|
});
|
|
6874
|
+
var skillListCmd = program.command("skill-list").description("Manage skill lists (collections of skills)");
|
|
6875
|
+
skillListCmd.command("list").description("List your skill lists").option("--org <orgname>", "List organization skill lists").option("--json", "Output as JSON").action(async (options) => {
|
|
6876
|
+
await skillListList({ org: options.org, json: options.json });
|
|
6877
|
+
});
|
|
6878
|
+
skillListCmd.command("create <name>").description("Create a new skill list").option("-d, --description <description>", "List description").option(
|
|
6879
|
+
"--visibility <visibility>",
|
|
6880
|
+
"Visibility: private or public",
|
|
6881
|
+
"private"
|
|
6882
|
+
).option("--org <orgname>", "Create under an organization").action(async (name, options) => {
|
|
6883
|
+
await skillListCreate(name, {
|
|
6884
|
+
description: options.description,
|
|
6885
|
+
visibility: options.visibility,
|
|
6886
|
+
org: options.org
|
|
6887
|
+
});
|
|
6888
|
+
});
|
|
6889
|
+
skillListCmd.command("show <specifier>").description("Show skill list details (e.g. @user/alice/my-tools)").option("--json", "Output as JSON").action(async (specifier, options) => {
|
|
6890
|
+
await skillListShow(specifier, { json: options.json });
|
|
6891
|
+
});
|
|
6892
|
+
skillListCmd.command("delete <specifier>").description("Delete a skill list").action(async (specifier) => {
|
|
6893
|
+
await skillListDelete(specifier);
|
|
6894
|
+
});
|
|
6895
|
+
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) => {
|
|
6896
|
+
await skillListUpdate(specifier, {
|
|
6897
|
+
description: options.description,
|
|
6898
|
+
visibility: options.visibility
|
|
6899
|
+
});
|
|
6900
|
+
});
|
|
6901
|
+
skillListCmd.command("add-skill <specifier> <skills...>").description(
|
|
6902
|
+
"Add skills to a list (e.g. pspm skill-list add-skill @user/me/my-list @user/alice/tool)"
|
|
6903
|
+
).option("--note <note>", "Note for the added skill").action(async (specifier, skills, options) => {
|
|
6904
|
+
await skillListAddSkill(specifier, skills, {
|
|
6905
|
+
note: options.note
|
|
6906
|
+
});
|
|
6907
|
+
});
|
|
6908
|
+
skillListCmd.command("remove-skill <specifier> <skill>").description("Remove a skill from a list").action(async (specifier, skill) => {
|
|
6909
|
+
await skillListRemoveSkill(specifier, skill);
|
|
6910
|
+
});
|
|
6911
|
+
skillListCmd.command("install <specifier>").description(
|
|
6912
|
+
"Install all skills from a list (e.g. pspm skill-list install @user/alice/my-tools)"
|
|
6913
|
+
).option(
|
|
6914
|
+
"--agent <agents>",
|
|
6915
|
+
'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
|
|
6916
|
+
).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) => {
|
|
6917
|
+
await skillListInstall(specifier, {
|
|
6918
|
+
agent: options.agent,
|
|
6919
|
+
yes: options.yes,
|
|
6920
|
+
global: options.global,
|
|
6921
|
+
dir: options.dir
|
|
6922
|
+
});
|
|
6923
|
+
});
|
|
6924
|
+
var notebookCmd = program.command("notebook").description("Manage AnyT notebooks");
|
|
6925
|
+
notebookCmd.command("upload <file>").description("Upload a .anyt notebook").option("--org <orgname>", "Upload to an organization").option(
|
|
6926
|
+
"--visibility <visibility>",
|
|
6927
|
+
"Visibility: private, team, or public",
|
|
6928
|
+
"private"
|
|
6929
|
+
).option("--description <description>", "Notebook description").action(async (file, options) => {
|
|
6930
|
+
await notebookUpload(file, options);
|
|
6931
|
+
});
|
|
6932
|
+
notebookCmd.command("list").description("List notebooks").option("--org <orgname>", "List organization notebooks").action(async (options) => {
|
|
6933
|
+
await notebookList(options);
|
|
6934
|
+
});
|
|
6935
|
+
notebookCmd.command("download <id>").description("Download a notebook by ID").option("-o, --output <path>", "Output file path").action(async (id, options) => {
|
|
6936
|
+
await notebookDownload(id, options.output);
|
|
6937
|
+
});
|
|
6938
|
+
notebookCmd.command("delete <id>").description("Delete a notebook by ID").action(async (id) => {
|
|
6939
|
+
await notebookDelete(id);
|
|
6940
|
+
});
|
|
6188
6941
|
await program.parseAsync();
|
|
6189
6942
|
var executedCommand = program.args[0];
|
|
6190
6943
|
if (executedCommand !== "upgrade") {
|