Repository navigation
Expand file tree
/
Copy pathpackage.js
More file actions
76 lines (73 loc) · 3.17 KB
/
Copy pathpackage.js
File metadata and controls
76 lines (73 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// pypi package — fetch a single PyPI package's metadata.
//
// Hits `https://pypi.org/pypi/<pkg>/json`. Returns the most agent-useful
// projection. Download stats are intentionally separate (see `pypi downloads`).
import { cli, Strategy } from '@agentrhq/webcmd/registry';
import { EmptyResultError } from '@agentrhq/webcmd/errors';
import { PYPI_BASE, pypiFetch, requirePackageName } from './utils.js';
function pickHomepage(info) {
if (info.home_page) return String(info.home_page);
const proj = info.project_urls;
if (proj && typeof proj === 'object') {
return String(proj.Homepage || proj.homepage || proj.Documentation || proj.Source || proj['Source Code'] || '');
}
return '';
}
function pickRepository(info) {
const proj = info.project_urls;
if (proj && typeof proj === 'object') {
return String(proj.Source || proj['Source Code'] || proj.Repository || proj.repository || '');
}
return '';
}
cli({
site: 'pypi',
name: 'package',
access: 'read',
description: 'Single PyPI package metadata (latest version, license, homepage, classifiers)',
domain: 'pypi.org',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'name', positional: true, required: true, help: 'PyPI package name (e.g. "requests", "pandas")' },
],
columns: [
'name', 'latestVersion', 'summary', 'author', 'license', 'homepage', 'repository',
'requiresPython', 'keywords', 'releases', 'firstReleased', 'lastReleased', 'url',
],
func: async (args) => {
const name = requirePackageName(args.name);
const body = await pypiFetch(`${PYPI_BASE}/pypi/${encodeURIComponent(name)}/json`, `pypi package ${name}`);
const info = body?.info;
if (!info || !info.name) {
throw new EmptyResultError('pypi package', `PyPI returned no metadata for "${name}".`);
}
const releases = body?.releases ?? {};
const releaseVersions = Object.keys(releases).filter(v => Array.isArray(releases[v]) && releases[v].length > 0);
let firstReleased = '';
let lastReleased = '';
for (const version of releaseVersions) {
for (const file of releases[version]) {
const time = String(file?.upload_time ?? '').slice(0, 10);
if (!time) continue;
if (!firstReleased || time < firstReleased) firstReleased = time;
if (!lastReleased || time > lastReleased) lastReleased = time;
}
}
return [{
name: String(info.name),
latestVersion: String(info.version ?? ''),
summary: String(info.summary ?? ''),
author: String(info.author ?? info.author_email ?? ''),
license: String(info.license_expression ?? info.license ?? ''),
homepage: pickHomepage(info),
repository: pickRepository(info),
requiresPython: String(info.requires_python ?? ''),
keywords: String(info.keywords ?? ''),
releases: releaseVersions.length,
firstReleased,
lastReleased,
url: String(info.package_url ?? `${PYPI_BASE}/project/${info.name}/`),
}];
},
});