@agentpowers/cli 0.1.7 → 0.2.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 +17 -0
- package/dist/api-client.d.ts +2 -19
- package/dist/api-client.js +2 -137
- package/dist/api-client.js.map +1 -1
- package/dist/auth.d.ts +2 -15
- package/dist/auth.js +2 -80
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/install.js +1 -1
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/setup.d.ts +11 -0
- package/dist/commands/setup.js +161 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +97 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +2 -10
- package/dist/config.js +2 -14
- package/dist/config.js.map +1 -1
- package/dist/installer.d.ts +3 -15
- package/dist/installer.js +2 -194
- package/dist/installer.js.map +1 -1
- package/dist/types.d.ts +2 -61
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -20,6 +20,14 @@ agentpowers search "code review"
|
|
|
20
20
|
|
|
21
21
|
## Commands
|
|
22
22
|
|
|
23
|
+
### `setup`
|
|
24
|
+
|
|
25
|
+
Auto-configure AgentPowers MCP for all detected AI tools (Claude Code, Claude Desktop, Codex, Gemini CLI, Kiro, etc.).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @agentpowers/cli setup
|
|
29
|
+
```
|
|
30
|
+
|
|
23
31
|
### `search <query>`
|
|
24
32
|
|
|
25
33
|
Search the AgentPowers marketplace for skills.
|
|
@@ -48,6 +56,15 @@ npx @agentpowers/cli login
|
|
|
48
56
|
|
|
49
57
|
Credentials are stored at `~/.agentpowers/auth.json`.
|
|
50
58
|
|
|
59
|
+
### `update [slug]`
|
|
60
|
+
|
|
61
|
+
Update installed skills to their latest versions. Omit the slug to check all installed skills.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx @agentpowers/cli update
|
|
65
|
+
npx @agentpowers/cli update my-skill
|
|
66
|
+
```
|
|
67
|
+
|
|
51
68
|
## Configuration
|
|
52
69
|
|
|
53
70
|
| Environment Variable | Description | Default |
|
package/dist/api-client.d.ts
CHANGED
|
@@ -1,19 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export
|
|
3
|
-
readonly statusCode: number;
|
|
4
|
-
readonly code?: string | undefined;
|
|
5
|
-
constructor(message: string, statusCode: number, code?: string | undefined);
|
|
6
|
-
}
|
|
7
|
-
export declare class NetworkError extends Error {
|
|
8
|
-
constructor(message: string);
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Map HTTP status codes to user-friendly messages.
|
|
12
|
-
*/
|
|
13
|
-
export declare function formatAPIError(error: APIError): string;
|
|
14
|
-
export declare function apiGet<T = unknown>(path: string, params?: Record<string, string | number | undefined>, auth?: string | null): Promise<T>;
|
|
15
|
-
export declare function apiPost<T = unknown>(path: string, body?: unknown, auth?: string | null): Promise<T>;
|
|
16
|
-
/**
|
|
17
|
-
* Fire-and-forget installation tracking. Never rejects.
|
|
18
|
-
*/
|
|
19
|
-
export declare function recordInstallation(sourceSlug: string, platform: string, source: string, hostname: string, auth?: string | null): Promise<void>;
|
|
1
|
+
/** API client — re-exported from @agentpowers/core. */
|
|
2
|
+
export { APIError, NetworkError, formatAPIError, apiGet, apiPost, recordInstallation, } from "@agentpowers/core";
|
package/dist/api-client.js
CHANGED
|
@@ -1,138 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
export class APIError extends Error {
|
|
4
|
-
statusCode;
|
|
5
|
-
code;
|
|
6
|
-
constructor(message, statusCode, code) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.statusCode = statusCode;
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.name = "APIError";
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export class NetworkError extends Error {
|
|
14
|
-
constructor(message) {
|
|
15
|
-
super(message);
|
|
16
|
-
this.name = "NetworkError";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Extract a human-readable message from the API response body.
|
|
21
|
-
*/
|
|
22
|
-
function parseErrorBody(body) {
|
|
23
|
-
try {
|
|
24
|
-
const json = JSON.parse(body);
|
|
25
|
-
if (typeof json.detail === "string") {
|
|
26
|
-
return { message: json.detail };
|
|
27
|
-
}
|
|
28
|
-
if (json.detail && typeof json.detail === "object") {
|
|
29
|
-
const detail = json.detail;
|
|
30
|
-
return {
|
|
31
|
-
message: typeof detail.detail === "string" ? detail.detail : body,
|
|
32
|
-
code: typeof detail.code === "string" ? detail.code : undefined,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
// Not JSON
|
|
38
|
-
}
|
|
39
|
-
return { message: body };
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Map HTTP status codes to user-friendly messages.
|
|
43
|
-
*/
|
|
44
|
-
export function formatAPIError(error) {
|
|
45
|
-
switch (error.statusCode) {
|
|
46
|
-
case 401:
|
|
47
|
-
return "Not logged in. Run `npx @agentpowers/cli login` first.";
|
|
48
|
-
case 403:
|
|
49
|
-
return "Access denied. You may need to purchase this skill first.";
|
|
50
|
-
case 404:
|
|
51
|
-
return "Not found. Check the slug and try again.";
|
|
52
|
-
case 409:
|
|
53
|
-
return error.message || "Conflict — this action has already been taken.";
|
|
54
|
-
case 422:
|
|
55
|
-
return `Invalid request: ${error.message}`;
|
|
56
|
-
case 429:
|
|
57
|
-
return "Too many requests. Please wait a moment and try again.";
|
|
58
|
-
default:
|
|
59
|
-
return error.message || `API error (${error.statusCode})`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
async function safeFetch(url, init) {
|
|
63
|
-
try {
|
|
64
|
-
return await fetch(url, {
|
|
65
|
-
...init,
|
|
66
|
-
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
71
|
-
throw new NetworkError("Request timed out. The API may be temporarily unavailable.");
|
|
72
|
-
}
|
|
73
|
-
if (err instanceof TypeError) {
|
|
74
|
-
throw new NetworkError("Could not connect to AgentPowers API. Check your network connection.");
|
|
75
|
-
}
|
|
76
|
-
throw err;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
export async function apiGet(path, params, auth) {
|
|
80
|
-
if (!path.startsWith("/")) {
|
|
81
|
-
throw new Error(`Invalid API path: "${path}" — must start with "/".`);
|
|
82
|
-
}
|
|
83
|
-
const url = new URL(path, API_BASE_URL);
|
|
84
|
-
if (params) {
|
|
85
|
-
for (const [key, value] of Object.entries(params)) {
|
|
86
|
-
if (value !== undefined) {
|
|
87
|
-
url.searchParams.set(key, String(value));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const headers = { Accept: "application/json" };
|
|
92
|
-
if (auth) {
|
|
93
|
-
headers["Authorization"] = `Bearer ${auth}`;
|
|
94
|
-
}
|
|
95
|
-
const response = await safeFetch(url.toString(), { headers });
|
|
96
|
-
if (!response.ok) {
|
|
97
|
-
const body = await response.text();
|
|
98
|
-
const { message, code } = parseErrorBody(body);
|
|
99
|
-
throw new APIError(message, response.status, code);
|
|
100
|
-
}
|
|
101
|
-
return (await response.json());
|
|
102
|
-
}
|
|
103
|
-
export async function apiPost(path, body, auth) {
|
|
104
|
-
if (!path.startsWith("/")) {
|
|
105
|
-
throw new Error(`Invalid API path: "${path}" — must start with "/".`);
|
|
106
|
-
}
|
|
107
|
-
const url = new URL(path, API_BASE_URL);
|
|
108
|
-
const headers = {
|
|
109
|
-
Accept: "application/json",
|
|
110
|
-
"Content-Type": "application/json",
|
|
111
|
-
};
|
|
112
|
-
if (auth) {
|
|
113
|
-
headers["Authorization"] = `Bearer ${auth}`;
|
|
114
|
-
}
|
|
115
|
-
const response = await safeFetch(url.toString(), {
|
|
116
|
-
method: "POST",
|
|
117
|
-
headers,
|
|
118
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
119
|
-
});
|
|
120
|
-
if (!response.ok) {
|
|
121
|
-
const text = await response.text();
|
|
122
|
-
const { message, code } = parseErrorBody(text);
|
|
123
|
-
throw new APIError(message, response.status, code);
|
|
124
|
-
}
|
|
125
|
-
return (await response.json());
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Fire-and-forget installation tracking. Never rejects.
|
|
129
|
-
*/
|
|
130
|
-
export async function recordInstallation(sourceSlug, platform, source, hostname, auth) {
|
|
131
|
-
try {
|
|
132
|
-
await apiPost("/v1/installations", { source_slug: sourceSlug, platform, source, hostname }, auth);
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
// Swallow all errors — tracking must not break install flow
|
|
136
|
-
}
|
|
137
|
-
}
|
|
1
|
+
/** API client — re-exported from @agentpowers/core. */
|
|
2
|
+
export { APIError, NetworkError, formatAPIError, apiGet, apiPost, recordInstallation, } from "@agentpowers/core";
|
|
138
3
|
//# sourceMappingURL=api-client.js.map
|
package/dist/api-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,MAAM,EACN,OAAO,EACP,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,15 +1,2 @@
|
|
|
1
|
-
/** Auth
|
|
2
|
-
|
|
3
|
-
* Load the auth token from disk. Returns null if missing or expired.
|
|
4
|
-
*/
|
|
5
|
-
export declare function loadAuthToken(): string | null;
|
|
6
|
-
/**
|
|
7
|
-
* Check if the user is currently authenticated.
|
|
8
|
-
*/
|
|
9
|
-
export declare function isAuthenticated(): boolean;
|
|
10
|
-
/**
|
|
11
|
-
* Wait for the auth token file to appear or update.
|
|
12
|
-
* Polls the file every 2 seconds for up to 2 minutes.
|
|
13
|
-
* Returns the token on success, null on timeout.
|
|
14
|
-
*/
|
|
15
|
-
export declare function waitForAuthToken(pollIntervalMs?: number, timeoutMs?: number): Promise<string | null>;
|
|
1
|
+
/** Auth — re-exported from @agentpowers/core. */
|
|
2
|
+
export { loadAuthToken, isAuthenticated, waitForAuthToken, } from "@agentpowers/core";
|
package/dist/auth.js
CHANGED
|
@@ -1,81 +1,3 @@
|
|
|
1
|
-
/** Auth
|
|
2
|
-
|
|
3
|
-
import { AUTH_FILE, LOGIN_POLL_INTERVAL_MS, LOGIN_TIMEOUT_MS } from "./config.js";
|
|
4
|
-
/**
|
|
5
|
-
* Load the auth token from disk. Returns null if missing or expired.
|
|
6
|
-
*/
|
|
7
|
-
export function loadAuthToken() {
|
|
8
|
-
if (!existsSync(AUTH_FILE))
|
|
9
|
-
return null;
|
|
10
|
-
try {
|
|
11
|
-
const data = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
12
|
-
if (typeof data.token !== "string")
|
|
13
|
-
return null;
|
|
14
|
-
// Check expiration
|
|
15
|
-
if (data.expires_at) {
|
|
16
|
-
const expires = new Date(data.expires_at);
|
|
17
|
-
if (expires.getTime() < Date.now())
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
return data.token;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Check if the user is currently authenticated.
|
|
28
|
-
*/
|
|
29
|
-
export function isAuthenticated() {
|
|
30
|
-
return loadAuthToken() !== null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Wait for the auth token file to appear or update.
|
|
34
|
-
* Polls the file every 2 seconds for up to 2 minutes.
|
|
35
|
-
* Returns the token on success, null on timeout.
|
|
36
|
-
*/
|
|
37
|
-
export function waitForAuthToken(pollIntervalMs = LOGIN_POLL_INTERVAL_MS, timeoutMs = LOGIN_TIMEOUT_MS) {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
const startTime = Date.now();
|
|
40
|
-
const initialToken = loadAuthToken();
|
|
41
|
-
const checkToken = () => {
|
|
42
|
-
const token = loadAuthToken();
|
|
43
|
-
// Succeed if we have a token that differs from what we started with
|
|
44
|
-
// (or if we had no token before and now we do)
|
|
45
|
-
if (token && token !== initialToken) {
|
|
46
|
-
cleanup();
|
|
47
|
-
resolve(token);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (Date.now() - startTime >= timeoutMs) {
|
|
51
|
-
cleanup();
|
|
52
|
-
resolve(null);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
const interval = setInterval(checkToken, pollIntervalMs);
|
|
57
|
-
// Also watch the file for changes (faster detection)
|
|
58
|
-
let fileWatcherActive = false;
|
|
59
|
-
try {
|
|
60
|
-
watchFile(AUTH_FILE, { interval: 500 }, () => {
|
|
61
|
-
checkToken();
|
|
62
|
-
});
|
|
63
|
-
fileWatcherActive = true;
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
// watchFile may fail if directory doesn't exist — polling is fine
|
|
67
|
-
}
|
|
68
|
-
const cleanup = () => {
|
|
69
|
-
clearInterval(interval);
|
|
70
|
-
if (fileWatcherActive) {
|
|
71
|
-
try {
|
|
72
|
-
unwatchFile(AUTH_FILE);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
// ignore cleanup errors
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
});
|
|
80
|
-
}
|
|
1
|
+
/** Auth — re-exported from @agentpowers/core. */
|
|
2
|
+
export { loadAuthToken, isAuthenticated, waitForAuthToken, } from "@agentpowers/core";
|
|
81
3
|
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,OAAO,EACL,aAAa,EACb,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,8 @@ import { Command } from "commander";
|
|
|
7
7
|
import { searchCommand } from "./commands/search.js";
|
|
8
8
|
import { installCommand } from "./commands/install.js";
|
|
9
9
|
import { loginCommand } from "./commands/login.js";
|
|
10
|
+
import { setupCommand } from "./commands/setup.js";
|
|
11
|
+
import { updateCommand } from "./commands/update.js";
|
|
10
12
|
function getVersion() {
|
|
11
13
|
try {
|
|
12
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -40,5 +42,17 @@ program
|
|
|
40
42
|
.action(async () => {
|
|
41
43
|
await loginCommand();
|
|
42
44
|
});
|
|
45
|
+
program
|
|
46
|
+
.command("setup")
|
|
47
|
+
.description("Auto-configure AgentPowers MCP server for your AI tools")
|
|
48
|
+
.action(async () => {
|
|
49
|
+
await setupCommand();
|
|
50
|
+
});
|
|
51
|
+
program
|
|
52
|
+
.command("update")
|
|
53
|
+
.description("Check for and install updates to installed skills")
|
|
54
|
+
.action(async () => {
|
|
55
|
+
await updateCommand();
|
|
56
|
+
});
|
|
43
57
|
program.parse();
|
|
44
58
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,wEAAwE;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,wEAAwE;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACrF,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,kFAAkF,CAAC;KAC/F,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,EAAE;IAC9B,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,6BAA6B,CAAC;KAC1C,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;IAC7B,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,aAAa,EAAE,CAAC;AACxB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/dist/commands/install.js
CHANGED
|
@@ -16,7 +16,7 @@ export async function installCommand(slug) {
|
|
|
16
16
|
// Get download URL
|
|
17
17
|
const downloadData = await apiGet(`/v1/skills/${slug}/download`, undefined, token);
|
|
18
18
|
// Download and extract
|
|
19
|
-
const result = await downloadAndExtract(downloadData.url, slug, "agentpowers");
|
|
19
|
+
const result = await downloadAndExtract(downloadData.url, slug, "skill", "agentpowers");
|
|
20
20
|
console.log(`Installed ${slug} to ${result.installDir}`);
|
|
21
21
|
console.log(`Content hash: ${result.contentHash}`);
|
|
22
22
|
// Record installation (fire-and-forget)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EACL,MAAM,EACN,kBAAkB,EAClB,QAAQ,EACR,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGnE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,uBAAuB;IACvB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CACX,wBAAwB,IAAI,4DAA4D,CACzF,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAE9B,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC;QAEtC,mBAAmB;QACnB,MAAM,YAAY,GAAG,MAAM,MAAM,CAC/B,cAAc,IAAI,WAAW,EAC7B,SAAS,EACT,KAAK,CACN,CAAC;QAEF,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,YAAY,CAAC,GAAG,EAChB,IAAI,EACJ,aAAa,CACd,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAEnD,wCAAwC;QACxC,KAAK,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;QAEvE,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,UAAU,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACvC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACvC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EACL,MAAM,EACN,kBAAkB,EAClB,QAAQ,EACR,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGnE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,uBAAuB;IACvB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CACX,wBAAwB,IAAI,4DAA4D,CACzF,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAE9B,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC;QAEtC,mBAAmB;QACnB,MAAM,YAAY,GAAG,MAAM,MAAM,CAC/B,cAAc,IAAI,WAAW,EAC7B,SAAS,EACT,KAAK,CACN,CAAC;QAEF,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,YAAY,CAAC,GAAG,EAChB,IAAI,EACJ,OAAO,EACP,aAAa,CACd,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAEnD,wCAAwC;QACxC,KAAK,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;QAEvE,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,UAAU,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACvC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACvC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Setup command — auto-configure AgentPowers MCP server for detected AI tools. */
|
|
2
|
+
/** Platform detection entry: directory to check and MCP config file path. */
|
|
3
|
+
interface PlatformInfo {
|
|
4
|
+
label: string;
|
|
5
|
+
configDir: string;
|
|
6
|
+
mcpConfigFile: string;
|
|
7
|
+
}
|
|
8
|
+
/** Build the list of platforms to probe. */
|
|
9
|
+
export declare function getPlatforms(): PlatformInfo[];
|
|
10
|
+
export declare function setupCommand(): Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/** Setup command — auto-configure AgentPowers MCP server for detected AI tools. */
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { platform } from "node:os";
|
|
6
|
+
import { AUTH_FILE } from "../config.js";
|
|
7
|
+
/** Build the list of platforms to probe. */
|
|
8
|
+
export function getPlatforms() {
|
|
9
|
+
const home = homedir();
|
|
10
|
+
const os = platform();
|
|
11
|
+
const platforms = [
|
|
12
|
+
{
|
|
13
|
+
label: "Claude Code",
|
|
14
|
+
configDir: join(home, ".claude"),
|
|
15
|
+
mcpConfigFile: join(home, ".claude", ".mcp.json"),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: "Codex",
|
|
19
|
+
configDir: join(home, ".codex"),
|
|
20
|
+
mcpConfigFile: join(home, ".codex", ".mcp.json"),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: "Gemini",
|
|
24
|
+
configDir: join(home, ".gemini"),
|
|
25
|
+
mcpConfigFile: join(home, ".gemini", ".mcp.json"),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
label: "Kiro",
|
|
29
|
+
configDir: join(home, ".kiro"),
|
|
30
|
+
mcpConfigFile: join(home, ".kiro", ".mcp.json"),
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
// Claude Desktop has OS-specific paths
|
|
34
|
+
if (os === "darwin") {
|
|
35
|
+
platforms.push({
|
|
36
|
+
label: "Claude Desktop",
|
|
37
|
+
configDir: join(home, "Library", "Application Support", "Claude"),
|
|
38
|
+
mcpConfigFile: join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else if (os === "linux") {
|
|
42
|
+
platforms.push({
|
|
43
|
+
label: "Claude Desktop",
|
|
44
|
+
configDir: join(home, ".config", "Claude"),
|
|
45
|
+
mcpConfigFile: join(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else if (os === "win32") {
|
|
49
|
+
const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
|
|
50
|
+
platforms.push({
|
|
51
|
+
label: "Claude Desktop",
|
|
52
|
+
configDir: join(appData, "Claude"),
|
|
53
|
+
mcpConfigFile: join(appData, "Claude", "claude_desktop_config.json"),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return platforms;
|
|
57
|
+
}
|
|
58
|
+
/** The MCP server entry we inject. */
|
|
59
|
+
const MCP_ENTRY = {
|
|
60
|
+
command: "npx",
|
|
61
|
+
args: ["@agentpowers/mcp-server"],
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Read an existing MCP config file, returning parsed JSON.
|
|
65
|
+
* Returns null if the file does not exist.
|
|
66
|
+
* Throws on JSON parse errors so the caller can report them.
|
|
67
|
+
*/
|
|
68
|
+
function readMcpConfig(filePath) {
|
|
69
|
+
if (!existsSync(filePath))
|
|
70
|
+
return null;
|
|
71
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
72
|
+
return JSON.parse(raw);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Write the MCP config back, creating parent directories if needed.
|
|
76
|
+
*/
|
|
77
|
+
function writeMcpConfig(filePath, data) {
|
|
78
|
+
const dir = dirname(filePath);
|
|
79
|
+
if (dir)
|
|
80
|
+
mkdirSync(dir, { recursive: true });
|
|
81
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
82
|
+
}
|
|
83
|
+
export async function setupCommand() {
|
|
84
|
+
const platforms = getPlatforms();
|
|
85
|
+
const configured = [];
|
|
86
|
+
const skipped = [];
|
|
87
|
+
const notFound = [];
|
|
88
|
+
const errors = [];
|
|
89
|
+
for (const p of platforms) {
|
|
90
|
+
// Check if the platform's config directory exists
|
|
91
|
+
if (!existsSync(p.configDir)) {
|
|
92
|
+
notFound.push(p.label);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
let config = readMcpConfig(p.mcpConfigFile);
|
|
97
|
+
if (config === null) {
|
|
98
|
+
// Create new config file
|
|
99
|
+
config = { mcpServers: { agentpowers: MCP_ENTRY } };
|
|
100
|
+
writeMcpConfig(p.mcpConfigFile, config);
|
|
101
|
+
configured.push(p.label);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
// Ensure mcpServers key exists
|
|
105
|
+
const servers = (config.mcpServers ?? {});
|
|
106
|
+
if (servers.agentpowers) {
|
|
107
|
+
skipped.push(p.label);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Merge: add our entry, preserve existing servers
|
|
111
|
+
servers.agentpowers = MCP_ENTRY;
|
|
112
|
+
config.mcpServers = servers;
|
|
113
|
+
writeMcpConfig(p.mcpConfigFile, config);
|
|
114
|
+
configured.push(p.label);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
118
|
+
errors.push(`${p.label}: ${msg}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Print results
|
|
122
|
+
console.log("");
|
|
123
|
+
console.log("AgentPowers MCP Server Setup");
|
|
124
|
+
console.log("============================");
|
|
125
|
+
console.log("");
|
|
126
|
+
if (configured.length > 0) {
|
|
127
|
+
console.log("Configured:");
|
|
128
|
+
for (const name of configured) {
|
|
129
|
+
console.log(` + ${name}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (skipped.length > 0) {
|
|
133
|
+
console.log("Already configured (skipped):");
|
|
134
|
+
for (const name of skipped) {
|
|
135
|
+
console.log(` - ${name}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (notFound.length > 0) {
|
|
139
|
+
console.log("Not detected:");
|
|
140
|
+
for (const name of notFound) {
|
|
141
|
+
console.log(` . ${name}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (errors.length > 0) {
|
|
145
|
+
console.log("Errors:");
|
|
146
|
+
for (const msg of errors) {
|
|
147
|
+
console.log(` ! ${msg}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (configured.length === 0 && skipped.length === 0) {
|
|
151
|
+
console.log("No supported AI platforms were detected on this system.");
|
|
152
|
+
console.log("Supported: Claude Code, Claude Desktop, Codex, Gemini, Kiro");
|
|
153
|
+
}
|
|
154
|
+
// Suggest login if not authenticated
|
|
155
|
+
if (!existsSync(AUTH_FILE)) {
|
|
156
|
+
console.log("");
|
|
157
|
+
console.log("Tip: Run `npx @agentpowers/cli login` to unlock premium marketplace features.");
|
|
158
|
+
}
|
|
159
|
+
console.log("");
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Update command — check for and install updates to all installed skills. */
|
|
2
|
+
import { hostname } from "node:os";
|
|
3
|
+
import { loadAuthToken } from "../auth.js";
|
|
4
|
+
import { apiGet, recordInstallation, APIError, NetworkError, } from "../api-client.js";
|
|
5
|
+
import { downloadAndExtract, getInstallDir } from "../installer.js";
|
|
6
|
+
import { loadPins, hashDirectory, isDirectory } from "@agentpowers/core";
|
|
7
|
+
export async function updateCommand() {
|
|
8
|
+
const pins = loadPins();
|
|
9
|
+
const slugs = Object.keys(pins);
|
|
10
|
+
if (slugs.length === 0) {
|
|
11
|
+
console.log("No skills installed yet.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const token = loadAuthToken();
|
|
15
|
+
let updated = 0;
|
|
16
|
+
let current = 0;
|
|
17
|
+
let skipped = 0;
|
|
18
|
+
let errored = 0;
|
|
19
|
+
for (const slug of slugs) {
|
|
20
|
+
const pin = pins[slug];
|
|
21
|
+
const type = pin.type ?? "skill";
|
|
22
|
+
const installDir = getInstallDir(slug, type);
|
|
23
|
+
// Check if locally edited
|
|
24
|
+
let isEdited = false;
|
|
25
|
+
if (isDirectory(installDir)) {
|
|
26
|
+
try {
|
|
27
|
+
const currentHash = hashDirectory(installDir);
|
|
28
|
+
isEdited = currentHash !== pin.content_hash;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// If we can't hash, treat as not edited
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Fetch latest version from API
|
|
35
|
+
let detail;
|
|
36
|
+
try {
|
|
37
|
+
const params = {};
|
|
38
|
+
if (pin.source !== "agentpowers")
|
|
39
|
+
params.source = pin.source;
|
|
40
|
+
detail = await apiGet(`/v1/detail/${encodeURIComponent(slug)}`, params);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
errored++;
|
|
44
|
+
if (err instanceof APIError && err.statusCode === 404) {
|
|
45
|
+
console.error(` [!] ${slug}: Not found on server (may have been unpublished).`);
|
|
46
|
+
}
|
|
47
|
+
else if (err instanceof NetworkError) {
|
|
48
|
+
console.error(` [!] ${slug}: ${err.message}`);
|
|
49
|
+
}
|
|
50
|
+
else if (err instanceof Error) {
|
|
51
|
+
console.error(` [!] ${slug}: ${err.message}`);
|
|
52
|
+
}
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const latestVersion = detail.version;
|
|
56
|
+
// Compare versions
|
|
57
|
+
if (!latestVersion || latestVersion === pin.version) {
|
|
58
|
+
current++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// Update available
|
|
62
|
+
if (isEdited) {
|
|
63
|
+
skipped++;
|
|
64
|
+
console.log(` [skip] ${slug}: locally edited — ${pin.version ?? "unknown"} -> ${latestVersion} available but skipped.`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Download and install
|
|
68
|
+
try {
|
|
69
|
+
console.log(` Updating ${slug}: ${pin.version ?? "unknown"} -> ${latestVersion}...`);
|
|
70
|
+
const downloadData = await apiGet(`/v1/skills/${encodeURIComponent(slug)}/download`, undefined, token);
|
|
71
|
+
await downloadAndExtract(downloadData.url, slug, type, pin.source, latestVersion, detail.security_status ?? "pass");
|
|
72
|
+
updated++;
|
|
73
|
+
console.log(` [ok] ${slug} updated to ${latestVersion}.`);
|
|
74
|
+
// Record installation (fire-and-forget)
|
|
75
|
+
void recordInstallation(slug, "cli", pin.source, hostname(), token);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
errored++;
|
|
79
|
+
if (err instanceof Error) {
|
|
80
|
+
console.error(` [!] ${slug}: Update failed — ${err.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Summary
|
|
85
|
+
console.log("");
|
|
86
|
+
const parts = [];
|
|
87
|
+
if (updated > 0)
|
|
88
|
+
parts.push(`${updated} updated`);
|
|
89
|
+
if (current > 0)
|
|
90
|
+
parts.push(`${current} already current`);
|
|
91
|
+
if (skipped > 0)
|
|
92
|
+
parts.push(`${skipped} skipped (locally edited)`);
|
|
93
|
+
if (errored > 0)
|
|
94
|
+
parts.push(`${errored} failed`);
|
|
95
|
+
console.log(parts.join(", ") + ".");
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
/** Configuration
|
|
2
|
-
export
|
|
3
|
-
export declare const AUTH_DIR: string;
|
|
4
|
-
export declare const AUTH_FILE: string;
|
|
5
|
-
export declare const PINS_FILE: string;
|
|
6
|
-
export declare const SKILLS_DIR: string;
|
|
7
|
-
export declare const LOGIN_URL = "https://agentpowers.ai/cli-auth";
|
|
8
|
-
export declare const TIMEOUT_MS = 30000;
|
|
9
|
-
export declare const LOGIN_POLL_INTERVAL_MS = 2000;
|
|
10
|
-
export declare const LOGIN_TIMEOUT_MS = 120000;
|
|
1
|
+
/** Configuration — re-exported from @agentpowers/core. */
|
|
2
|
+
export { API_BASE_URL, AUTH_DIR, AUTH_FILE, PINS_FILE, SKILLS_DIR, LOGIN_URL, TIMEOUT_MS, LOGIN_POLL_INTERVAL_MS, LOGIN_TIMEOUT_MS, TOOL_CONFIG_DIRS, } from "@agentpowers/core";
|
package/dist/config.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/** Configuration
|
|
2
|
-
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
export const API_BASE_URL = process.env.AGENTPOWERS_API_URL ??
|
|
5
|
-
process.env.AP_API_BASE ??
|
|
6
|
-
"https://api.agentpowers.ai";
|
|
7
|
-
export const AUTH_DIR = join(homedir(), ".agentpowers");
|
|
8
|
-
export const AUTH_FILE = join(AUTH_DIR, "auth.json");
|
|
9
|
-
export const PINS_FILE = join(AUTH_DIR, "pins.json");
|
|
10
|
-
export const SKILLS_DIR = join(homedir(), ".claude", "skills");
|
|
11
|
-
export const LOGIN_URL = "https://agentpowers.ai/cli-auth";
|
|
12
|
-
export const TIMEOUT_MS = 30_000;
|
|
13
|
-
export const LOGIN_POLL_INTERVAL_MS = 2_000;
|
|
14
|
-
export const LOGIN_TIMEOUT_MS = 120_000;
|
|
1
|
+
/** Configuration — re-exported from @agentpowers/core. */
|
|
2
|
+
export { API_BASE_URL, AUTH_DIR, AUTH_FILE, PINS_FILE, SKILLS_DIR, LOGIN_URL, TIMEOUT_MS, LOGIN_POLL_INTERVAL_MS, LOGIN_TIMEOUT_MS, TOOL_CONFIG_DIRS, } from "@agentpowers/core";
|
|
15
3
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAE1D,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,UAAU,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
package/dist/installer.d.ts
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export declare function validateArchiveMembers(members: string[], installDir: string): void;
|
|
5
|
-
export declare function getInstallDir(slug: string): string;
|
|
6
|
-
export interface InstallResult {
|
|
7
|
-
installDir: string;
|
|
8
|
-
contentHash: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Download a skill package, extract it, compute content hash, and save pin.
|
|
12
|
-
*
|
|
13
|
-
* Supports both HTTP URLs and base64 data URIs (local dev fallback).
|
|
14
|
-
*/
|
|
15
|
-
export declare function downloadAndExtract(url: string, slug: string, source?: string, version?: string | null, securityStatus?: string): Promise<InstallResult>;
|
|
1
|
+
/** Installer — re-exported from @agentpowers/core. */
|
|
2
|
+
export { validateSlug, getInstallDir, validateArchiveMembers, downloadAndExtract, } from "@agentpowers/core";
|
|
3
|
+
export type { InstallResult } from "@agentpowers/core";
|
package/dist/installer.js
CHANGED
|
@@ -1,195 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
import { join, relative, normalize, resolve, isAbsolute, dirname, sep } from "node:path";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { execFileSync } from "node:child_process";
|
|
6
|
-
import { randomUUID, createHash } from "node:crypto";
|
|
7
|
-
import { SKILLS_DIR, PINS_FILE } from "./config.js";
|
|
8
|
-
/** Strict slug pattern: lowercase alphanumeric with hyphens. */
|
|
9
|
-
const VALID_SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
10
|
-
export function validateSlug(slug) {
|
|
11
|
-
return VALID_SLUG_RE.test(slug);
|
|
12
|
-
}
|
|
13
|
-
// --- Content hashing (matches CLI's content_hasher.py) ---
|
|
14
|
-
const EXCLUDED_NAMES = new Set([".DS_Store", "Thumbs.db", "__pycache__"]);
|
|
15
|
-
const EXCLUDED_SUFFIXES = new Set([".pyc", ".pyo"]);
|
|
16
|
-
function shouldSkip(relativePath) {
|
|
17
|
-
const parts = relativePath.split("/");
|
|
18
|
-
for (const part of parts) {
|
|
19
|
-
if (EXCLUDED_NAMES.has(part))
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
const lastPart = parts[parts.length - 1];
|
|
23
|
-
for (const suffix of EXCLUDED_SUFFIXES) {
|
|
24
|
-
if (lastPart.endsWith(suffix))
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
function collectFiles(dirPath, basePath) {
|
|
30
|
-
const results = [];
|
|
31
|
-
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
32
|
-
for (const entry of entries) {
|
|
33
|
-
const fullPath = join(dirPath, entry.name);
|
|
34
|
-
const relPath = relative(basePath, fullPath);
|
|
35
|
-
if (entry.isDirectory()) {
|
|
36
|
-
if (!EXCLUDED_NAMES.has(entry.name)) {
|
|
37
|
-
results.push(...collectFiles(fullPath, basePath));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (entry.isFile() && !shouldSkip(relPath)) {
|
|
41
|
-
results.push(relPath);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return results;
|
|
45
|
-
}
|
|
46
|
-
export function hashDirectory(dirPath) {
|
|
47
|
-
const files = collectFiles(dirPath, dirPath).sort();
|
|
48
|
-
const hasher = createHash("sha256");
|
|
49
|
-
for (const relPath of files) {
|
|
50
|
-
hasher.update(relPath, "utf-8");
|
|
51
|
-
hasher.update(readFileSync(join(dirPath, relPath)));
|
|
52
|
-
}
|
|
53
|
-
return `sha256:${hasher.digest("hex")}`;
|
|
54
|
-
}
|
|
55
|
-
// --- Pin management ---
|
|
56
|
-
function loadPins() {
|
|
57
|
-
try {
|
|
58
|
-
return JSON.parse(readFileSync(PINS_FILE, "utf-8"));
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
function savePin(slug, source, version, contentHash, securityStatus) {
|
|
65
|
-
mkdirSync(dirname(PINS_FILE), { recursive: true });
|
|
66
|
-
const pins = loadPins();
|
|
67
|
-
const now = new Date().toISOString();
|
|
68
|
-
pins[slug] = {
|
|
69
|
-
source,
|
|
70
|
-
version,
|
|
71
|
-
content_hash: contentHash,
|
|
72
|
-
installed_at: now,
|
|
73
|
-
scanned_at: now,
|
|
74
|
-
security_status: securityStatus,
|
|
75
|
-
type: "skill",
|
|
76
|
-
};
|
|
77
|
-
writeFileSync(PINS_FILE, JSON.stringify(pins, null, 2));
|
|
78
|
-
}
|
|
79
|
-
// --- Archive validation ---
|
|
80
|
-
export function validateArchiveMembers(members, installDir) {
|
|
81
|
-
const resolvedInstallDir = resolve(installDir);
|
|
82
|
-
for (const member of members) {
|
|
83
|
-
const normalized = member.replace(/\\/g, "/");
|
|
84
|
-
if (isAbsolute(normalized) || isAbsolute(member)) {
|
|
85
|
-
throw new Error(`Blocked path traversal: absolute path in archive member '${member}'`);
|
|
86
|
-
}
|
|
87
|
-
const parts = normalized.split("/");
|
|
88
|
-
if (parts.includes("..")) {
|
|
89
|
-
throw new Error(`Blocked path traversal: '..' in archive member '${member}'`);
|
|
90
|
-
}
|
|
91
|
-
// Use relative() instead of startsWith() to prevent prefix attacks
|
|
92
|
-
const target = resolve(installDir, normalize(normalized));
|
|
93
|
-
const rel = relative(resolvedInstallDir, target);
|
|
94
|
-
if (rel.startsWith("..") || rel.startsWith(sep)) {
|
|
95
|
-
throw new Error(`Blocked path traversal: '${member}' resolves outside destination directory`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function safeExtract(tempFile, installDir, isTarball) {
|
|
100
|
-
let memberList;
|
|
101
|
-
if (isTarball) {
|
|
102
|
-
memberList = execFileSync("tar", ["-tzf", tempFile], { encoding: "utf-8" });
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
memberList = execFileSync("zipinfo", ["-1", tempFile], { encoding: "utf-8" });
|
|
106
|
-
}
|
|
107
|
-
const members = memberList
|
|
108
|
-
.split("\n")
|
|
109
|
-
.map((m) => m.trim())
|
|
110
|
-
.filter((m) => m.length > 0);
|
|
111
|
-
validateArchiveMembers(members, installDir);
|
|
112
|
-
if (isTarball) {
|
|
113
|
-
execFileSync("tar", ["-xzf", tempFile, "-C", installDir], { stdio: "pipe" });
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
execFileSync("unzip", ["-o", tempFile, "-d", installDir], { stdio: "pipe" });
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// --- Data URI handling ---
|
|
120
|
-
function isDataUri(url) {
|
|
121
|
-
return url.startsWith("data:");
|
|
122
|
-
}
|
|
123
|
-
function decodeDataUri(dataUri) {
|
|
124
|
-
const match = dataUri.match(/^data:[^;]*;base64,(.+)$/);
|
|
125
|
-
if (!match) {
|
|
126
|
-
throw new Error("Invalid data URI format — expected base64 encoding.");
|
|
127
|
-
}
|
|
128
|
-
return Buffer.from(match[1], "base64");
|
|
129
|
-
}
|
|
130
|
-
// --- Main install function ---
|
|
131
|
-
export function getInstallDir(slug) {
|
|
132
|
-
return join(SKILLS_DIR, slug);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Download a skill package, extract it, compute content hash, and save pin.
|
|
136
|
-
*
|
|
137
|
-
* Supports both HTTP URLs and base64 data URIs (local dev fallback).
|
|
138
|
-
*/
|
|
139
|
-
export async function downloadAndExtract(url, slug, source = "agentpowers", version = null, securityStatus = "pass") {
|
|
140
|
-
if (!validateSlug(slug)) {
|
|
141
|
-
throw new Error(`Invalid slug: "${slug}" — slugs must be lowercase alphanumeric with hyphens only.`);
|
|
142
|
-
}
|
|
143
|
-
const installDir = getInstallDir(slug);
|
|
144
|
-
// Detect archive format. Data URIs check the MIME type; URLs check the extension.
|
|
145
|
-
const isTarball = isDataUri(url)
|
|
146
|
-
? url.startsWith("data:application/gzip") || url.startsWith("data:application/x-tar")
|
|
147
|
-
: url.includes(".tar.gz") || url.includes(".tgz");
|
|
148
|
-
const ext = isTarball ? ".tar.gz" : ".zip";
|
|
149
|
-
const tempFile = join(tmpdir(), `ap-${randomUUID()}${ext}`);
|
|
150
|
-
try {
|
|
151
|
-
let buffer;
|
|
152
|
-
if (isDataUri(url)) {
|
|
153
|
-
buffer = decodeDataUri(url);
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
// Enforce HTTPS for download URLs to prevent MITM attacks
|
|
157
|
-
try {
|
|
158
|
-
const parsed = new URL(url);
|
|
159
|
-
if (parsed.protocol !== "https:") {
|
|
160
|
-
throw new Error(`Download URL must use HTTPS, got "${parsed.protocol}"`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
catch (e) {
|
|
164
|
-
if (e instanceof TypeError) {
|
|
165
|
-
throw new Error(`Invalid download URL: "${url}"`);
|
|
166
|
-
}
|
|
167
|
-
throw e;
|
|
168
|
-
}
|
|
169
|
-
const response = await fetch(url);
|
|
170
|
-
if (!response.ok) {
|
|
171
|
-
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
172
|
-
}
|
|
173
|
-
buffer = Buffer.from(await response.arrayBuffer());
|
|
174
|
-
}
|
|
175
|
-
writeFileSync(tempFile, buffer);
|
|
176
|
-
// Clean existing directory for fresh install
|
|
177
|
-
rmSync(installDir, { recursive: true, force: true });
|
|
178
|
-
mkdirSync(installDir, { recursive: true });
|
|
179
|
-
// Validate and extract
|
|
180
|
-
safeExtract(tempFile, installDir, isTarball);
|
|
181
|
-
// Compute content hash and save pin
|
|
182
|
-
const contentHash = hashDirectory(installDir);
|
|
183
|
-
savePin(slug, source, version, contentHash, securityStatus);
|
|
184
|
-
return { installDir, contentHash };
|
|
185
|
-
}
|
|
186
|
-
finally {
|
|
187
|
-
try {
|
|
188
|
-
unlinkSync(tempFile);
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
// temp file cleanup is best-effort
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
1
|
+
/** Installer — re-exported from @agentpowers/core. */
|
|
2
|
+
export { validateSlug, getInstallDir, validateArchiveMembers, downloadAndExtract, } from "@agentpowers/core";
|
|
195
3
|
//# sourceMappingURL=installer.js.map
|
package/dist/installer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,61 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export
|
|
3
|
-
slug: string;
|
|
4
|
-
title: string;
|
|
5
|
-
description: string;
|
|
6
|
-
category: string;
|
|
7
|
-
type: string;
|
|
8
|
-
price_cents: number;
|
|
9
|
-
currency: string;
|
|
10
|
-
version: string;
|
|
11
|
-
security_status: string;
|
|
12
|
-
download_count: number;
|
|
13
|
-
author: {
|
|
14
|
-
display_name: string | null;
|
|
15
|
-
github_username: string | null;
|
|
16
|
-
} | null;
|
|
17
|
-
}
|
|
18
|
-
export interface ExternalSearchItem {
|
|
19
|
-
slug: string;
|
|
20
|
-
title: string;
|
|
21
|
-
description: string;
|
|
22
|
-
author: string;
|
|
23
|
-
source: string;
|
|
24
|
-
source_url: string;
|
|
25
|
-
source_installs: number | null;
|
|
26
|
-
source_rating: number | null;
|
|
27
|
-
price_cents: number;
|
|
28
|
-
version: string | null;
|
|
29
|
-
ap_security_status: string | null;
|
|
30
|
-
ap_security_score: number | null;
|
|
31
|
-
ap_scanned_at: string | null;
|
|
32
|
-
}
|
|
33
|
-
export interface SearchSection<T> {
|
|
34
|
-
items: T[];
|
|
35
|
-
total: number;
|
|
36
|
-
}
|
|
37
|
-
export interface SectionedSearchResponse {
|
|
38
|
-
agentpowers: SearchSection<NativeSearchItem>;
|
|
39
|
-
[source: string]: SearchSection<NativeSearchItem | ExternalSearchItem>;
|
|
40
|
-
}
|
|
41
|
-
export interface DownloadResponse {
|
|
42
|
-
url: string;
|
|
43
|
-
slug: string;
|
|
44
|
-
}
|
|
45
|
-
export interface AuthData {
|
|
46
|
-
token: string;
|
|
47
|
-
expires_at: string;
|
|
48
|
-
}
|
|
49
|
-
/** Pin entry stored in ~/.agentpowers/pins.json */
|
|
50
|
-
export interface PinEntry {
|
|
51
|
-
source: string;
|
|
52
|
-
version: string | null;
|
|
53
|
-
content_hash: string;
|
|
54
|
-
installed_at: string;
|
|
55
|
-
scanned_at: string;
|
|
56
|
-
security_status: string;
|
|
57
|
-
type?: "skill" | "agent";
|
|
58
|
-
}
|
|
59
|
-
export interface PinsFile {
|
|
60
|
-
[slug: string]: PinEntry;
|
|
61
|
-
}
|
|
1
|
+
/** Types — re-exported from @agentpowers/core. */
|
|
2
|
+
export type { NativeSearchItem, ExternalSearchItem, SearchSection, SectionedSearchResponse, UnifiedDetail, DownloadResponse, AuthData, PurchaseStatus, PinEntry, PinsFile, InstalledSkillInfo, PackageType, } from "@agentpowers/core";
|
package/dist/types.js
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,kDAAkD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentpowers/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI for the AgentPowers marketplace — search, install, and manage Claude Code skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,10 +44,12 @@
|
|
|
44
44
|
"node": ">=18.0.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@agentpowers/core": "file:../agentpowers-core",
|
|
47
48
|
"commander": "^13.0.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^22.0.0",
|
|
52
|
+
"memfs": "^4.57.1",
|
|
51
53
|
"typescript": "^5.7.0",
|
|
52
54
|
"vitest": "^3.0.0"
|
|
53
55
|
}
|