@micar-tsp/skillhub 0.1.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/README.md +34 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +67 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +5 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +59 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/skills.d.ts +5 -0
- package/dist/commands/skills.js +127 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/user.d.ts +2 -0
- package/dist/commands/user.js +15 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/format/output.d.ts +2 -0
- package/dist/format/output.js +28 -0
- package/dist/format/output.js.map +1 -0
- package/dist/format/publish.d.ts +2 -0
- package/dist/format/publish.js +13 -0
- package/dist/format/publish.js.map +1 -0
- package/dist/format/skills.d.ts +21 -0
- package/dist/format/skills.js +114 -0
- package/dist/format/skills.js.map +1 -0
- package/dist/format/user.d.ts +4 -0
- package/dist/format/user.js +23 -0
- package/dist/format/user.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +29 -0
- package/dist/lib/api.js +108 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/archive.d.ts +8 -0
- package/dist/lib/archive.js +85 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/config.d.ts +32 -0
- package/dist/lib/config.js +78 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +4 -0
- package/dist/lib/constants.js +5 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/context.d.ts +15 -0
- package/dist/lib/context.js +15 -0
- package/dist/lib/context.js.map +1 -0
- package/dist/lib/download.d.ts +8 -0
- package/dist/lib/download.js +27 -0
- package/dist/lib/download.js.map +1 -0
- package/dist/lib/errors.d.ts +12 -0
- package/dist/lib/errors.js +12 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/human.d.ts +2 -0
- package/dist/lib/human.js +20 -0
- package/dist/lib/human.js.map +1 -0
- package/dist/lib/page.d.ts +7 -0
- package/dist/lib/page.js +15 -0
- package/dist/lib/page.js.map +1 -0
- package/dist/lib/target.d.ts +8 -0
- package/dist/lib/target.js +30 -0
- package/dist/lib/target.js.map +1 -0
- package/dist/lib/token.d.ts +1 -0
- package/dist/lib/token.js +8 -0
- package/dist/lib/token.js.map +1 -0
- package/dist/lib/types.d.ts +62 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { CONTACT_EMAIL, DEFAULT_BASE_URL, FRONTEND_URL } from './lib/constants.js';
|
|
4
|
+
import { printError } from './format/output.js';
|
|
5
|
+
import { registerConfigCommands } from './commands/config.js';
|
|
6
|
+
import { registerUserCommands } from './commands/user.js';
|
|
7
|
+
import { registerSkillCommands } from './commands/skills.js';
|
|
8
|
+
import { registerPublishCommand } from './commands/publish.js';
|
|
9
|
+
async function main() {
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('skillhub')
|
|
13
|
+
.description([
|
|
14
|
+
'Local SkillHub CLI for token-authenticated skill management.',
|
|
15
|
+
'',
|
|
16
|
+
`Default base URL: ${DEFAULT_BASE_URL}`,
|
|
17
|
+
`Frontend page: ${FRONTEND_URL}`,
|
|
18
|
+
`Contact: ${CONTACT_EMAIL}`
|
|
19
|
+
].join('\n'))
|
|
20
|
+
.version('0.1.0')
|
|
21
|
+
.option('--base-url <url>', 'Override configured/default base URL')
|
|
22
|
+
.option('--token <token>', 'Override configured token for this command')
|
|
23
|
+
.option('--json', 'Output JSON')
|
|
24
|
+
.option('--verbose', 'Show detailed error information')
|
|
25
|
+
.showHelpAfterError();
|
|
26
|
+
registerConfigCommands(program);
|
|
27
|
+
registerUserCommands(program);
|
|
28
|
+
registerSkillCommands(program);
|
|
29
|
+
registerPublishCommand(program);
|
|
30
|
+
try {
|
|
31
|
+
await program.parseAsync(process.argv);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
printError(error, Boolean(program.opts().verbose));
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
void main();
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;IAC7B,OAAO;SACJ,IAAI,CAAC,UAAU,CAAC;SAChB,WAAW,CAAC;QACX,8DAA8D;QAC9D,EAAE;QACF,qBAAqB,gBAAgB,EAAE;QACvC,kBAAkB,YAAY,EAAE;QAChC,YAAY,aAAa,EAAE;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACZ,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CAAC,kBAAkB,EAAE,sCAAsC,CAAC;SAClE,MAAM,CAAC,iBAAiB,EAAE,4CAA4C,CAAC;SACvE,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;SAC/B,MAAM,CAAC,WAAW,EAAE,iCAAiC,CAAC;SACtD,kBAAkB,EAAE,CAAA;IAEvB,sBAAsB,CAAC,OAAO,CAAC,CAAA;IAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAA;IAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAA;IAE/B,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAyB,CAAC,OAAO,CAAC,CAAC,CAAA;QACzE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC;AAED,KAAK,IAAI,EAAE,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface RequestOptions {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
token?: string;
|
|
4
|
+
verbose?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface ApiEnvelope<T> {
|
|
7
|
+
code: number;
|
|
8
|
+
message: string;
|
|
9
|
+
data: T;
|
|
10
|
+
timestamp?: string;
|
|
11
|
+
requestId?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class SkillHubApiClient {
|
|
14
|
+
private readonly options;
|
|
15
|
+
constructor(options: RequestOptions);
|
|
16
|
+
getJson<T>(path: string, params?: Record<string, string | number | string[] | undefined>): Promise<T>;
|
|
17
|
+
postJson<T>(path: string, body?: unknown): Promise<T>;
|
|
18
|
+
download(path: string, params?: Record<string, string | number | string[] | undefined>): Promise<{
|
|
19
|
+
bytes: Uint8Array;
|
|
20
|
+
fileName?: string;
|
|
21
|
+
contentType?: string;
|
|
22
|
+
}>;
|
|
23
|
+
uploadMultipart<T>(path: string, form: FormData): Promise<T>;
|
|
24
|
+
private requestJson;
|
|
25
|
+
private headers;
|
|
26
|
+
private buildUrl;
|
|
27
|
+
private throwHttpError;
|
|
28
|
+
}
|
|
29
|
+
export declare function requireToken(token: string | undefined): string;
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { API_PREFIX } from './constants.js';
|
|
2
|
+
import { CliError } from './errors.js';
|
|
3
|
+
export class SkillHubApiClient {
|
|
4
|
+
options;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async getJson(path, params) {
|
|
9
|
+
return this.requestJson('GET', path, undefined, params);
|
|
10
|
+
}
|
|
11
|
+
async postJson(path, body) {
|
|
12
|
+
return this.requestJson('POST', path, body);
|
|
13
|
+
}
|
|
14
|
+
async download(path, params) {
|
|
15
|
+
const request = this.buildUrl(path, params);
|
|
16
|
+
const response = await fetch(request, { headers: this.headers() });
|
|
17
|
+
if (!response.ok)
|
|
18
|
+
await this.throwHttpError(response, 'GET', request);
|
|
19
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
20
|
+
return {
|
|
21
|
+
bytes: new Uint8Array(arrayBuffer),
|
|
22
|
+
fileName: parseContentDispositionFileName(response.headers.get('content-disposition')),
|
|
23
|
+
contentType: response.headers.get('content-type') ?? undefined
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async uploadMultipart(path, form) {
|
|
27
|
+
return this.requestJson('POST', path, form);
|
|
28
|
+
}
|
|
29
|
+
async requestJson(method, path, body, params) {
|
|
30
|
+
const request = this.buildUrl(path, params);
|
|
31
|
+
const headers = this.headers(body instanceof FormData ? undefined : 'application/json');
|
|
32
|
+
const response = await fetch(request, {
|
|
33
|
+
method,
|
|
34
|
+
headers,
|
|
35
|
+
body: body === undefined ? undefined : body instanceof FormData ? body : JSON.stringify(body)
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok)
|
|
38
|
+
await this.throwHttpError(response, method, request);
|
|
39
|
+
const envelope = (await response.json());
|
|
40
|
+
if (envelope.code !== 0) {
|
|
41
|
+
throw new CliError(envelope.message || 'SkillHub request failed.', {
|
|
42
|
+
request: `${method} ${request}`,
|
|
43
|
+
status: response.status,
|
|
44
|
+
requestId: envelope.requestId,
|
|
45
|
+
backendMessage: envelope.message
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return envelope.data;
|
|
49
|
+
}
|
|
50
|
+
headers(contentType) {
|
|
51
|
+
const headers = { Accept: 'application/json' };
|
|
52
|
+
if (contentType)
|
|
53
|
+
headers['Content-Type'] = contentType;
|
|
54
|
+
if (this.options.token)
|
|
55
|
+
headers.Authorization = `Bearer ${this.options.token}`;
|
|
56
|
+
return headers;
|
|
57
|
+
}
|
|
58
|
+
buildUrl(path, params) {
|
|
59
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
60
|
+
const url = new URL(`${this.options.baseUrl}${API_PREFIX}${normalizedPath}`);
|
|
61
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
62
|
+
if (value === undefined)
|
|
63
|
+
continue;
|
|
64
|
+
if (Array.isArray(value)) {
|
|
65
|
+
for (const item of value)
|
|
66
|
+
url.searchParams.append(key, item);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
url.searchParams.set(key, String(value));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return url.toString();
|
|
73
|
+
}
|
|
74
|
+
async throwHttpError(response, method, request) {
|
|
75
|
+
let backendMessage;
|
|
76
|
+
let requestId;
|
|
77
|
+
try {
|
|
78
|
+
const body = (await response.json());
|
|
79
|
+
backendMessage = body.message;
|
|
80
|
+
requestId = body.requestId;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
backendMessage = response.statusText;
|
|
84
|
+
}
|
|
85
|
+
const authMessage = response.status === 401 ? 'Authentication failed. Check your token or run "skillhub login" again.' : undefined;
|
|
86
|
+
throw new CliError(authMessage ?? backendMessage ?? `SkillHub request failed with status ${response.status}.`, {
|
|
87
|
+
request: `${method} ${request}`,
|
|
88
|
+
status: response.status,
|
|
89
|
+
requestId,
|
|
90
|
+
backendMessage
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function requireToken(token) {
|
|
95
|
+
if (!token)
|
|
96
|
+
throw new CliError('No SkillHub token configured. Run "skillhub login --token <token>" first.');
|
|
97
|
+
return token;
|
|
98
|
+
}
|
|
99
|
+
function parseContentDispositionFileName(value) {
|
|
100
|
+
if (!value)
|
|
101
|
+
return undefined;
|
|
102
|
+
const utf8Match = /filename\*=UTF-8''([^;]+)/i.exec(value);
|
|
103
|
+
if (utf8Match)
|
|
104
|
+
return decodeURIComponent(utf8Match[1]);
|
|
105
|
+
const match = /filename="?([^";]+)"?/i.exec(value);
|
|
106
|
+
return match?.[1];
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function installArchive(options: {
|
|
2
|
+
archivePath: string;
|
|
3
|
+
targetDir: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
force: boolean;
|
|
6
|
+
}): Promise<string>;
|
|
7
|
+
export declare function listArchiveEntries(archivePath: string): Promise<string[]>;
|
|
8
|
+
export declare function assertSafeEntries(entries: string[]): void;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import AdmZip from 'adm-zip';
|
|
5
|
+
import tar from 'tar';
|
|
6
|
+
import { CliError } from './errors.js';
|
|
7
|
+
export async function installArchive(options) {
|
|
8
|
+
const entries = await listArchiveEntries(options.archivePath);
|
|
9
|
+
assertSafeEntries(entries);
|
|
10
|
+
const topLevel = singleTopLevel(entries);
|
|
11
|
+
const destination = path.resolve(options.targetDir, topLevel ?? options.slug);
|
|
12
|
+
if (await exists(destination)) {
|
|
13
|
+
if (!options.force)
|
|
14
|
+
throw new CliError(`Skill directory already exists: ${destination}\nUse --force to overwrite it.`);
|
|
15
|
+
await fs.rm(destination, { recursive: true, force: true });
|
|
16
|
+
}
|
|
17
|
+
await fs.mkdir(options.targetDir, { recursive: true });
|
|
18
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'skillhub-install-'));
|
|
19
|
+
try {
|
|
20
|
+
await extractArchive(options.archivePath, tempDir);
|
|
21
|
+
if (topLevel) {
|
|
22
|
+
await movePath(path.join(tempDir, topLevel), destination);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
await fs.mkdir(destination, { recursive: true });
|
|
26
|
+
const children = await fs.readdir(tempDir);
|
|
27
|
+
for (const child of children)
|
|
28
|
+
await movePath(path.join(tempDir, child), path.join(destination, child));
|
|
29
|
+
}
|
|
30
|
+
return destination;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function listArchiveEntries(archivePath) {
|
|
37
|
+
if (archivePath.endsWith('.zip')) {
|
|
38
|
+
const zip = new AdmZip(archivePath);
|
|
39
|
+
return zip.getEntries().map((entry) => entry.entryName);
|
|
40
|
+
}
|
|
41
|
+
const entries = [];
|
|
42
|
+
await tar.t({ file: archivePath, onentry: (entry) => entries.push(entry.path) });
|
|
43
|
+
return entries;
|
|
44
|
+
}
|
|
45
|
+
export function assertSafeEntries(entries) {
|
|
46
|
+
const unsafe = entries.some((entry) => {
|
|
47
|
+
const normalized = entry.replace(/\\/g, '/');
|
|
48
|
+
return normalized.startsWith('/') || normalized.includes('../') || normalized === '..' || /^[A-Za-z]:\//.test(normalized);
|
|
49
|
+
});
|
|
50
|
+
if (unsafe)
|
|
51
|
+
throw new CliError('Invalid archive. Package contains unsafe paths.');
|
|
52
|
+
}
|
|
53
|
+
function singleTopLevel(entries) {
|
|
54
|
+
const topLevels = new Set(entries.map((entry) => entry.replace(/\\/g, '/').split('/').filter(Boolean)[0]).filter(Boolean));
|
|
55
|
+
return topLevels.size === 1 ? [...topLevels][0] : undefined;
|
|
56
|
+
}
|
|
57
|
+
async function extractArchive(archivePath, destination) {
|
|
58
|
+
if (archivePath.endsWith('.zip')) {
|
|
59
|
+
const zip = new AdmZip(archivePath);
|
|
60
|
+
zip.extractAllTo(destination, true);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await tar.x({ file: archivePath, cwd: destination });
|
|
64
|
+
}
|
|
65
|
+
async function movePath(source, destination) {
|
|
66
|
+
try {
|
|
67
|
+
await fs.rename(source, destination);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error.code !== 'EXDEV')
|
|
71
|
+
throw error;
|
|
72
|
+
await fs.cp(source, destination, { recursive: true });
|
|
73
|
+
await fs.rm(source, { recursive: true, force: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function exists(filePath) {
|
|
77
|
+
try {
|
|
78
|
+
await fs.access(filePath);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/lib/archive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const configSchema: z.ZodObject<{
|
|
3
|
+
baseUrl: z.ZodOptional<z.ZodType<string, z.ZodTypeDef, string>>;
|
|
4
|
+
token: z.ZodOptional<z.ZodType<string, z.ZodTypeDef, string>>;
|
|
5
|
+
defaultDownloadDir: z.ZodOptional<z.ZodType<string, z.ZodTypeDef, string>>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
baseUrl?: string | undefined;
|
|
8
|
+
token?: string | undefined;
|
|
9
|
+
defaultDownloadDir?: string | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
baseUrl?: string | undefined;
|
|
12
|
+
token?: string | undefined;
|
|
13
|
+
defaultDownloadDir?: string | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export type SkillHubConfig = z.infer<typeof configSchema>;
|
|
16
|
+
export declare const CONFIG_KEYS: readonly ["baseUrl", "token", "defaultDownloadDir"];
|
|
17
|
+
export type ConfigKey = typeof CONFIG_KEYS[number];
|
|
18
|
+
export declare function getConfigPath(): string;
|
|
19
|
+
export declare function readConfig(): Promise<SkillHubConfig>;
|
|
20
|
+
export declare function writeConfig(config: SkillHubConfig): Promise<void>;
|
|
21
|
+
export declare function updateConfig(updates: SkillHubConfig): Promise<SkillHubConfig>;
|
|
22
|
+
export declare function validateConfigKey(key: string): ConfigKey;
|
|
23
|
+
export declare function normalizeBaseUrl(baseUrl: string): string;
|
|
24
|
+
export interface ResolvedOptions {
|
|
25
|
+
baseUrl: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
defaultDownloadDir?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function resolveOptions(overrides?: {
|
|
30
|
+
baseUrl?: string;
|
|
31
|
+
token?: string;
|
|
32
|
+
}): Promise<ResolvedOptions>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { DEFAULT_BASE_URL } from './constants.js';
|
|
6
|
+
import { CliError } from './errors.js';
|
|
7
|
+
const optionalNonEmptyText = z.custom((value) => typeof value === 'string' && value.length > 0).optional();
|
|
8
|
+
export const configSchema = z.object({
|
|
9
|
+
baseUrl: optionalNonEmptyText,
|
|
10
|
+
token: optionalNonEmptyText,
|
|
11
|
+
defaultDownloadDir: optionalNonEmptyText
|
|
12
|
+
});
|
|
13
|
+
export const CONFIG_KEYS = ['baseUrl', 'token', 'defaultDownloadDir'];
|
|
14
|
+
export function getConfigPath() {
|
|
15
|
+
if (process.platform === 'win32') {
|
|
16
|
+
const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming');
|
|
17
|
+
return path.join(appData, 'skillhub', 'config.json');
|
|
18
|
+
}
|
|
19
|
+
const configHome = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
|
|
20
|
+
return path.join(configHome, 'skillhub', 'config.json');
|
|
21
|
+
}
|
|
22
|
+
export async function readConfig() {
|
|
23
|
+
const filePath = getConfigPath();
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
return configSchema.parse(parsed);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error.code === 'ENOENT')
|
|
31
|
+
return {};
|
|
32
|
+
if (error instanceof z.ZodError)
|
|
33
|
+
throw new CliError(`Invalid config file: ${filePath}`);
|
|
34
|
+
if (error instanceof SyntaxError)
|
|
35
|
+
throw new CliError(`Invalid JSON in config file: ${filePath}`);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function writeConfig(config) {
|
|
40
|
+
const filePath = getConfigPath();
|
|
41
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
42
|
+
await fs.writeFile(filePath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
43
|
+
if (process.platform !== 'win32')
|
|
44
|
+
await fs.chmod(filePath, 0o600);
|
|
45
|
+
}
|
|
46
|
+
export async function updateConfig(updates) {
|
|
47
|
+
const current = await readConfig();
|
|
48
|
+
const next = configSchema.parse({ ...current, ...updates });
|
|
49
|
+
await writeConfig(next);
|
|
50
|
+
return next;
|
|
51
|
+
}
|
|
52
|
+
export function validateConfigKey(key) {
|
|
53
|
+
if (CONFIG_KEYS.includes(key))
|
|
54
|
+
return key;
|
|
55
|
+
throw new CliError(`Unknown config key "${key}".\nAllowed keys: ${CONFIG_KEYS.join(', ')}.`);
|
|
56
|
+
}
|
|
57
|
+
export function normalizeBaseUrl(baseUrl) {
|
|
58
|
+
let url;
|
|
59
|
+
try {
|
|
60
|
+
url = new URL(baseUrl);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new CliError('Invalid base URL. Expected an HTTP or HTTPS URL.');
|
|
64
|
+
}
|
|
65
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
66
|
+
throw new CliError('Invalid base URL. Expected an HTTP or HTTPS URL.');
|
|
67
|
+
}
|
|
68
|
+
return url.toString().replace(/\/$/, '');
|
|
69
|
+
}
|
|
70
|
+
export async function resolveOptions(overrides = {}) {
|
|
71
|
+
const config = await readConfig();
|
|
72
|
+
return {
|
|
73
|
+
baseUrl: normalizeBaseUrl(overrides.baseUrl ?? config.baseUrl ?? DEFAULT_BASE_URL),
|
|
74
|
+
token: overrides.token ?? config.token,
|
|
75
|
+
defaultDownloadDir: config.defaultDownloadDir
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_BASE_URL = "https://pre-ai-mcp.tsp.xiaomi.net/skillhub";
|
|
2
|
+
export declare const API_PREFIX = "/api/v1/token";
|
|
3
|
+
export declare const FRONTEND_URL = "https://pre-skillhub.tsp.mioffice.cn";
|
|
4
|
+
export declare const CONTACT_EMAIL = "luozhilin@xiaomi.com";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const DEFAULT_BASE_URL = 'https://pre-ai-mcp.tsp.xiaomi.net/skillhub';
|
|
2
|
+
export const API_PREFIX = '/api/v1/token';
|
|
3
|
+
export const FRONTEND_URL = 'https://pre-skillhub.tsp.mioffice.cn';
|
|
4
|
+
export const CONTACT_EMAIL = 'luozhilin@xiaomi.com';
|
|
5
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,4CAA4C,CAAA;AAC5E,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAA;AACzC,MAAM,CAAC,MAAM,YAAY,GAAG,sCAAsC,CAAA;AAClE,MAAM,CAAC,MAAM,aAAa,GAAG,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SkillHubApiClient } from './api.js';
|
|
2
|
+
export interface CommandContext {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
json: boolean;
|
|
6
|
+
verbose: boolean;
|
|
7
|
+
client: SkillHubApiClient;
|
|
8
|
+
defaultDownloadDir?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function createContext(options: {
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
token?: string;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
verbose?: boolean;
|
|
15
|
+
}, authRequired?: boolean): Promise<CommandContext>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SkillHubApiClient, requireToken } from './api.js';
|
|
2
|
+
import { resolveOptions } from './config.js';
|
|
3
|
+
export async function createContext(options, authRequired = true) {
|
|
4
|
+
const resolved = await resolveOptions(options);
|
|
5
|
+
const token = authRequired ? requireToken(resolved.token) : resolved.token;
|
|
6
|
+
return {
|
|
7
|
+
baseUrl: resolved.baseUrl,
|
|
8
|
+
token,
|
|
9
|
+
json: Boolean(options.json),
|
|
10
|
+
verbose: Boolean(options.verbose),
|
|
11
|
+
defaultDownloadDir: resolved.defaultDownloadDir,
|
|
12
|
+
client: new SkillHubApiClient({ baseUrl: resolved.baseUrl, token })
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAW5C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgF,EAAE,YAAY,GAAG,IAAI;IACvI,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAA;IAC1E,OAAO;QACL,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,KAAK;QACL,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;QAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QACjC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;QAC/C,MAAM,EAAE,IAAI,iBAAiB,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;KACpE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function writeDownload(options: {
|
|
2
|
+
bytes: Uint8Array;
|
|
3
|
+
output: string;
|
|
4
|
+
fileName: string;
|
|
5
|
+
force: boolean;
|
|
6
|
+
}): Promise<string>;
|
|
7
|
+
export declare function resolveOutputPath(output: string, fileName: string): string;
|
|
8
|
+
export declare function fallbackArchiveName(skill: string): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CliError } from './errors.js';
|
|
4
|
+
export async function writeDownload(options) {
|
|
5
|
+
const outputPath = resolveOutputPath(options.output, options.fileName);
|
|
6
|
+
try {
|
|
7
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
8
|
+
await fs.writeFile(outputPath, options.bytes, { flag: options.force ? 'w' : 'wx' });
|
|
9
|
+
return outputPath;
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code === 'EEXIST') {
|
|
13
|
+
throw new CliError(`Output file already exists: ${outputPath}\nUse --force to overwrite it.`);
|
|
14
|
+
}
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function resolveOutputPath(output, fileName) {
|
|
19
|
+
const hasExtension = path.extname(output) !== '';
|
|
20
|
+
if (hasExtension)
|
|
21
|
+
return path.resolve(output);
|
|
22
|
+
return path.resolve(output, fileName);
|
|
23
|
+
}
|
|
24
|
+
export function fallbackArchiveName(skill) {
|
|
25
|
+
return `${skill.replace(/[\\/]/g, '-')}.zip`;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/lib/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgF;IAClH,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtE,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7D,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QACnF,OAAO,UAAU,CAAA;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,MAAM,IAAI,QAAQ,CAAC,+BAA+B,UAAU,gCAAgC,CAAC,CAAA;QAC/F,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,QAAgB;IAChE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IAChD,IAAI,YAAY;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAA;AAC9C,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class CliError extends Error {
|
|
2
|
+
readonly details?: ErrorDetails | undefined;
|
|
3
|
+
constructor(message: string, details?: ErrorDetails | undefined);
|
|
4
|
+
}
|
|
5
|
+
export interface ErrorDetails {
|
|
6
|
+
request?: string;
|
|
7
|
+
status?: number;
|
|
8
|
+
requestId?: string;
|
|
9
|
+
backendMessage?: string;
|
|
10
|
+
code?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function isCliError(error: unknown): error is CliError;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class CliError extends Error {
|
|
2
|
+
details;
|
|
3
|
+
constructor(message, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.details = details;
|
|
6
|
+
this.name = 'CliError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function isCliError(error) {
|
|
10
|
+
return error instanceof CliError;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACK;IAAtC,YAAY,OAAe,EAAW,OAAsB;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAA;QADsB,YAAO,GAAP,OAAO,CAAe;QAE1D,IAAI,CAAC,IAAI,GAAG,UAAU,CAAA;IACxB,CAAC;CACF;AAUD,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,QAAQ,CAAA;AAClC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function formatBytes(bytes) {
|
|
2
|
+
if (!Number.isFinite(bytes) || bytes < 0)
|
|
3
|
+
return '0 B';
|
|
4
|
+
if (bytes < 1024)
|
|
5
|
+
return `${bytes} B`;
|
|
6
|
+
const units = ['KB', 'MB', 'GB'];
|
|
7
|
+
let value = bytes / 1024;
|
|
8
|
+
let unitIndex = 0;
|
|
9
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
10
|
+
value /= 1024;
|
|
11
|
+
unitIndex += 1;
|
|
12
|
+
}
|
|
13
|
+
return `${value.toFixed(value >= 10 ? 1 : 1)} ${units[unitIndex]}`;
|
|
14
|
+
}
|
|
15
|
+
export function valueOrDash(value) {
|
|
16
|
+
if (value === null || value === undefined || value === '')
|
|
17
|
+
return '-';
|
|
18
|
+
return String(value);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=human.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human.js","sourceRoot":"","sources":["../../src/lib/human.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IACtD,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAA;IACrC,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IAChC,IAAI,KAAK,GAAG,KAAK,GAAG,IAAI,CAAA;IACxB,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,OAAO,KAAK,IAAI,IAAI,IAAI,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,KAAK,IAAI,IAAI,CAAA;QACb,SAAS,IAAI,CAAC,CAAA;IAChB,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAA;AACpE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,GAAG,CAAA;IACrE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC"}
|
package/dist/lib/page.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function getPageItems(page) {
|
|
2
|
+
if (Array.isArray(page))
|
|
3
|
+
return page;
|
|
4
|
+
return page.items ?? page.content ?? [];
|
|
5
|
+
}
|
|
6
|
+
export function getPageMeta(page) {
|
|
7
|
+
if (Array.isArray(page))
|
|
8
|
+
return { page: 0, size: page.length, total: page.length };
|
|
9
|
+
return {
|
|
10
|
+
page: page.page ?? page.number ?? 0,
|
|
11
|
+
size: page.size ?? getPageItems(page).length,
|
|
12
|
+
total: page.total ?? page.totalElements ?? getPageItems(page).length
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/lib/page.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAI,IAA2B;IACzD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,WAAW,CAAI,IAA2B;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA;IAClF,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;QACnC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM;QAC5C,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,aAAa,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM;KACrE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SkillTarget {
|
|
2
|
+
raw: string;
|
|
3
|
+
slug: string;
|
|
4
|
+
namespace?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseSkillTarget(value: string): SkillTarget;
|
|
7
|
+
export declare function formatSkillTarget(target: SkillTarget): string;
|
|
8
|
+
export declare function encodeTargetPath(target: SkillTarget): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CliError } from './errors.js';
|
|
3
|
+
const segmentSchema = z.string().min(1).regex(/^[A-Za-z0-9._-]+$/);
|
|
4
|
+
export function parseSkillTarget(value) {
|
|
5
|
+
const parts = value.split('/');
|
|
6
|
+
if (parts.length === 1) {
|
|
7
|
+
const slug = segmentSchema.safeParse(parts[0]);
|
|
8
|
+
if (!slug.success)
|
|
9
|
+
throw new CliError(`Invalid skill target: ${value}. Expected slug or namespace/slug.`);
|
|
10
|
+
return { raw: value, slug: slug.data };
|
|
11
|
+
}
|
|
12
|
+
if (parts.length === 2) {
|
|
13
|
+
const namespace = segmentSchema.safeParse(parts[0]);
|
|
14
|
+
const slug = segmentSchema.safeParse(parts[1]);
|
|
15
|
+
if (!namespace.success || !slug.success)
|
|
16
|
+
throw new CliError(`Invalid skill target: ${value}. Expected slug or namespace/slug.`);
|
|
17
|
+
return { raw: value, namespace: namespace.data, slug: slug.data };
|
|
18
|
+
}
|
|
19
|
+
throw new CliError(`Invalid skill target: ${value}. Expected slug or namespace/slug.`);
|
|
20
|
+
}
|
|
21
|
+
export function formatSkillTarget(target) {
|
|
22
|
+
return target.namespace ? `${target.namespace}/${target.slug}` : target.slug;
|
|
23
|
+
}
|
|
24
|
+
export function encodeTargetPath(target) {
|
|
25
|
+
const slug = encodeURIComponent(target.slug);
|
|
26
|
+
if (!target.namespace)
|
|
27
|
+
return `/skills/${slug}`;
|
|
28
|
+
return `/skills/${encodeURIComponent(target.namespace)}/${slug}`;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=target.js.map
|