@web42/cli 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/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +86 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +27 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +171 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.js +198 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.js +22 -0
- package/dist/commands/pack.d.ts +2 -0
- package/dist/commands/pack.js +80 -0
- package/dist/commands/pull.d.ts +2 -0
- package/dist/commands/pull.js +63 -0
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +127 -0
- package/dist/commands/remix.d.ts +2 -0
- package/dist/commands/remix.js +49 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +58 -0
- package/dist/commands/uninstall.d.ts +3 -0
- package/dist/commands/uninstall.js +54 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +59 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +37 -0
- package/dist/platforms/base.d.ts +58 -0
- package/dist/platforms/base.js +1 -0
- package/dist/platforms/openclaw/adapter.d.ts +10 -0
- package/dist/platforms/openclaw/adapter.js +452 -0
- package/dist/platforms/openclaw/templates.d.ts +7 -0
- package/dist/platforms/openclaw/templates.js +369 -0
- package/dist/platforms/registry.d.ts +6 -0
- package/dist/platforms/registry.js +30 -0
- package/dist/utils/api.d.ts +3 -0
- package/dist/utils/api.js +35 -0
- package/dist/utils/config.d.ts +22 -0
- package/dist/utils/config.js +50 -0
- package/dist/utils/secrets.d.ts +32 -0
- package/dist/utils/secrets.js +118 -0
- package/dist/utils/skill.d.ts +4 -0
- package/dist/utils/skill.js +21 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import { apiGet } from "../utils/api.js";
|
|
7
|
+
import { requireAuth } from "../utils/config.js";
|
|
8
|
+
export const pullCommand = new Command("pull")
|
|
9
|
+
.description("Pull latest agent files from the Web42 marketplace into the current directory")
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const config = requireAuth();
|
|
12
|
+
const cwd = process.cwd();
|
|
13
|
+
const manifestPath = join(cwd, "manifest.json");
|
|
14
|
+
if (!existsSync(manifestPath)) {
|
|
15
|
+
console.log(chalk.red("No manifest.json found. Are you in an agent directory?"));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
19
|
+
if (!manifest.name) {
|
|
20
|
+
console.log(chalk.red("manifest.json is missing a name field."));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const spinner = ora(`Pulling @${config.username}/${manifest.name}...`).start();
|
|
24
|
+
try {
|
|
25
|
+
const agents = await apiGet(`/api/agents?username=${config.username}`);
|
|
26
|
+
const agent = agents.find((a) => a.slug === manifest.name);
|
|
27
|
+
if (!agent) {
|
|
28
|
+
spinner.fail(`Agent @${config.username}/${manifest.name} not found on the marketplace. Run \`web42 push\` first.`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const files = await apiGet(`/api/agents/${agent.id}/files?include_content=true`);
|
|
32
|
+
if (files.length === 0) {
|
|
33
|
+
spinner.succeed("Agent is up to date (no files on remote).");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let written = 0;
|
|
37
|
+
let skipped = 0;
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
if (file.content === null || file.content === undefined) {
|
|
40
|
+
skipped++;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Skip packaging artifacts that are regenerated on push
|
|
44
|
+
if (file.path === ".openclaw/config.json") {
|
|
45
|
+
skipped++;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const filePath = join(cwd, file.path);
|
|
49
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
50
|
+
writeFileSync(filePath, file.content, "utf-8");
|
|
51
|
+
written++;
|
|
52
|
+
}
|
|
53
|
+
if (agent.manifest) {
|
|
54
|
+
writeFileSync(manifestPath, JSON.stringify(agent.manifest, null, 2) + "\n");
|
|
55
|
+
}
|
|
56
|
+
spinner.succeed(`Pulled ${chalk.bold(`@${config.username}/${manifest.name}`)} (${written} files written${skipped > 0 ? `, ${skipped} skipped` : ""})`);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
spinner.fail("Pull failed");
|
|
60
|
+
console.error(chalk.red(error.message));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, } from "fs";
|
|
3
|
+
import { join, relative } from "path";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import { apiPost } from "../utils/api.js";
|
|
8
|
+
import { requireAuth } from "../utils/config.js";
|
|
9
|
+
import { openclawAdapter } from "../platforms/openclaw/adapter.js";
|
|
10
|
+
function hashContent(content) {
|
|
11
|
+
return createHash("sha256").update(content).digest("hex");
|
|
12
|
+
}
|
|
13
|
+
function readPackedFiles(dir) {
|
|
14
|
+
const files = [];
|
|
15
|
+
function walk(currentDir) {
|
|
16
|
+
const entries = readdirSync(currentDir, { withFileTypes: true });
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const fullPath = join(currentDir, entry.name);
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
walk(fullPath);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
if (entry.name === "manifest.json" && currentDir === dir)
|
|
24
|
+
continue;
|
|
25
|
+
const stat = statSync(fullPath);
|
|
26
|
+
if (stat.size > 1024 * 1024)
|
|
27
|
+
continue;
|
|
28
|
+
try {
|
|
29
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
30
|
+
const relPath = relative(dir, fullPath);
|
|
31
|
+
files.push({ path: relPath, content, hash: hashContent(content) });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Skip binary files
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
walk(dir);
|
|
40
|
+
return files;
|
|
41
|
+
}
|
|
42
|
+
export const pushCommand = new Command("push")
|
|
43
|
+
.description("Push your agent package to the Web42 marketplace")
|
|
44
|
+
.action(async () => {
|
|
45
|
+
const config = requireAuth();
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const manifestPath = join(cwd, "manifest.json");
|
|
48
|
+
if (!existsSync(manifestPath)) {
|
|
49
|
+
console.log(chalk.red("No manifest.json found. Run `web42 init` first."));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
let manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
53
|
+
if (!manifest.name || !manifest.version || !manifest.author) {
|
|
54
|
+
console.log(chalk.red("Invalid manifest.json. Must have name, version, and author."));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const spinner = ora("Preparing agent package...").start();
|
|
58
|
+
const web42Dir = join(cwd, ".web42");
|
|
59
|
+
let processedFiles;
|
|
60
|
+
if (existsSync(web42Dir)) {
|
|
61
|
+
spinner.text = "Reading packed artifact...";
|
|
62
|
+
processedFiles = readPackedFiles(web42Dir);
|
|
63
|
+
const packedManifestPath = join(web42Dir, "manifest.json");
|
|
64
|
+
if (existsSync(packedManifestPath)) {
|
|
65
|
+
manifest = JSON.parse(readFileSync(packedManifestPath, "utf-8"));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
spinner.text = "No .web42/ found, packing automatically...";
|
|
70
|
+
const result = await openclawAdapter.pack({ cwd, outputDir: ".web42" });
|
|
71
|
+
processedFiles = result.files;
|
|
72
|
+
const existingKeys = new Set((manifest.configVariables ?? []).map((v) => v.key));
|
|
73
|
+
for (const cv of result.configVariables) {
|
|
74
|
+
if (!existingKeys.has(cv.key)) {
|
|
75
|
+
if (!manifest.configVariables)
|
|
76
|
+
manifest.configVariables = [];
|
|
77
|
+
manifest.configVariables.push(cv);
|
|
78
|
+
existingKeys.add(cv.key);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Write the packed artifact so it can be inspected later
|
|
82
|
+
mkdirSync(web42Dir, { recursive: true });
|
|
83
|
+
for (const file of result.files) {
|
|
84
|
+
const filePath = join(web42Dir, file.path);
|
|
85
|
+
mkdirSync(join(filePath, ".."), { recursive: true });
|
|
86
|
+
writeFileSync(filePath, file.content, "utf-8");
|
|
87
|
+
}
|
|
88
|
+
writeFileSync(join(web42Dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
89
|
+
}
|
|
90
|
+
spinner.text = `Pushing ${processedFiles.length} files...`;
|
|
91
|
+
let readme = "";
|
|
92
|
+
const readmePath = join(cwd, "README.md");
|
|
93
|
+
if (existsSync(readmePath)) {
|
|
94
|
+
readme = readFileSync(readmePath, "utf-8");
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const agentResult = await apiPost("/api/agents", {
|
|
98
|
+
slug: manifest.name,
|
|
99
|
+
name: manifest.name,
|
|
100
|
+
description: manifest.description,
|
|
101
|
+
readme,
|
|
102
|
+
manifest,
|
|
103
|
+
demo_video_url: manifest.demoVideoUrl,
|
|
104
|
+
});
|
|
105
|
+
const agentId = agentResult.agent.id;
|
|
106
|
+
const fileEntries = processedFiles.map((f) => ({
|
|
107
|
+
path: f.path,
|
|
108
|
+
content: f.content,
|
|
109
|
+
content_hash: f.hash,
|
|
110
|
+
storage_url: `agent-files/${config.username}/${manifest.name}/${f.path}`,
|
|
111
|
+
}));
|
|
112
|
+
await apiPost(`/api/agents/${agentId}/files`, { files: fileEntries });
|
|
113
|
+
spinner.succeed(`Pushed ${chalk.bold(`@${config.username}/${manifest.name}`)} (${processedFiles.length} files)`);
|
|
114
|
+
if (agentResult.created) {
|
|
115
|
+
console.log(chalk.green(" New agent created!"));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(chalk.green(" Agent updated."));
|
|
119
|
+
}
|
|
120
|
+
console.log(chalk.dim(` View at: ${config.apiUrl ? config.apiUrl.replace("https://", "") : "agents.web42.ai"}/${config.username}/${manifest.name}`));
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
spinner.fail("Push failed");
|
|
124
|
+
console.error(chalk.red(error.message));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import { apiGet, apiPost } from "../utils/api.js";
|
|
6
|
+
import { requireAuth } from "../utils/config.js";
|
|
7
|
+
export const remixCommand = new Command("remix")
|
|
8
|
+
.description("Remix an agent package to your account")
|
|
9
|
+
.argument("<agent>", "Agent to remix (e.g. @user/agent-name)")
|
|
10
|
+
.action(async (agentRef) => {
|
|
11
|
+
const config = requireAuth();
|
|
12
|
+
const match = agentRef.match(/^@?([^/]+)\/(.+)$/);
|
|
13
|
+
if (!match) {
|
|
14
|
+
console.log(chalk.red("Invalid agent reference. Use @user/agent-name format."));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const [, username, agentSlug] = match;
|
|
18
|
+
const { confirm } = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
type: "confirm",
|
|
21
|
+
name: "confirm",
|
|
22
|
+
message: `Remix @${username}/${agentSlug} to @${config.username}/${agentSlug}?`,
|
|
23
|
+
default: true,
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
if (!confirm) {
|
|
27
|
+
console.log(chalk.yellow("Aborted."));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const spinner = ora("Remixing...").start();
|
|
31
|
+
try {
|
|
32
|
+
// Find the agent
|
|
33
|
+
const agents = await apiGet(`/api/agents?username=${username}`);
|
|
34
|
+
const agent = agents.find((a) => a.slug === agentSlug);
|
|
35
|
+
if (!agent) {
|
|
36
|
+
spinner.fail(`Agent @${username}/${agentSlug} not found`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
await apiPost(`/api/agents/${agent.id}/remix`, {});
|
|
40
|
+
spinner.succeed(`Remixed to ${chalk.bold(`@${config.username}/${agentSlug}`)}`);
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(chalk.dim(`Run \`web42 install @${config.username}/${agentSlug}\` to install locally.`));
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
spinner.fail("Remix failed");
|
|
46
|
+
console.error(chalk.red(error.message));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { apiGet } from "../utils/api.js";
|
|
5
|
+
function truncate(str, max) {
|
|
6
|
+
if (str.length <= max)
|
|
7
|
+
return str;
|
|
8
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
9
|
+
}
|
|
10
|
+
export const searchCommand = new Command("search")
|
|
11
|
+
.description("Search the marketplace for agents")
|
|
12
|
+
.argument("<query>", "Search query")
|
|
13
|
+
.option("-p, --platform <platform>", "Filter results by platform (e.g. openclaw)")
|
|
14
|
+
.option("-l, --limit <number>", "Max results to show", "10")
|
|
15
|
+
.action(async (query, opts) => {
|
|
16
|
+
const spinner = ora(`Searching for "${query}"...`).start();
|
|
17
|
+
try {
|
|
18
|
+
const agents = await apiGet(`/api/agents?search=${encodeURIComponent(query)}`);
|
|
19
|
+
spinner.stop();
|
|
20
|
+
if (agents.length === 0) {
|
|
21
|
+
console.log(chalk.yellow(`No agents found for "${query}".`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const limit = parseInt(opts.limit, 10) || 10;
|
|
25
|
+
const results = agents.slice(0, limit);
|
|
26
|
+
console.log(chalk.bold(`Found ${agents.length} agent(s) for "${query}":`));
|
|
27
|
+
console.log();
|
|
28
|
+
for (const agent of results) {
|
|
29
|
+
const ref = `@${agent.owner.username}/${agent.slug}`;
|
|
30
|
+
const stars = agent.stars_count > 0 ? chalk.yellow(` \u2605 ${agent.stars_count}`) : "";
|
|
31
|
+
const price = agent.price_cents > 0
|
|
32
|
+
? chalk.green(` $${(agent.price_cents / 100).toFixed(2)}`)
|
|
33
|
+
: chalk.dim(" Free");
|
|
34
|
+
console.log(` ${chalk.cyan.bold(agent.name)}${stars}${price}`);
|
|
35
|
+
console.log(` ${chalk.dim(ref)}`);
|
|
36
|
+
if (agent.description) {
|
|
37
|
+
console.log(` ${truncate(agent.description, 80)}`);
|
|
38
|
+
}
|
|
39
|
+
const platform = agent.manifest?.platform ?? "openclaw";
|
|
40
|
+
if (agent.price_cents > 0) {
|
|
41
|
+
const siteUrl = process.env.WEB42_API_URL ?? "https://marketplace.web42.ai";
|
|
42
|
+
console.log(chalk.dim(` Purchase: ${siteUrl}/${agent.owner.username}/${agent.slug}`));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
console.log(chalk.dim(` Install: web42 ${platform} install ${ref}`));
|
|
46
|
+
}
|
|
47
|
+
console.log();
|
|
48
|
+
}
|
|
49
|
+
if (agents.length > limit) {
|
|
50
|
+
console.log(chalk.dim(` ... and ${agents.length - limit} more. Use --limit to see more.`));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
spinner.fail("Search failed");
|
|
55
|
+
console.error(chalk.red(error.message));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
export function makeUninstallCommand(adapter) {
|
|
6
|
+
return new Command("uninstall")
|
|
7
|
+
.description("Uninstall an agent")
|
|
8
|
+
.argument("<agent>", "Local agent name to uninstall")
|
|
9
|
+
.option("-f, --force", "Skip confirmation prompt")
|
|
10
|
+
.action(async (agentName, opts) => {
|
|
11
|
+
const installed = await adapter.listInstalled();
|
|
12
|
+
const found = installed.find((a) => a.name === agentName);
|
|
13
|
+
if (!found) {
|
|
14
|
+
console.log(chalk.red(`Agent "${agentName}" is not installed on ${adapter.name}.`));
|
|
15
|
+
const available = installed.map((a) => a.name);
|
|
16
|
+
if (available.length > 0) {
|
|
17
|
+
console.log(chalk.dim(` Installed agents: ${available.join(", ")}`));
|
|
18
|
+
}
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
if (!opts.force) {
|
|
22
|
+
const { confirm } = await inquirer.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: "confirm",
|
|
25
|
+
name: "confirm",
|
|
26
|
+
message: `Remove agent "${agentName}" and all its workspace files?`,
|
|
27
|
+
default: false,
|
|
28
|
+
},
|
|
29
|
+
]);
|
|
30
|
+
if (!confirm) {
|
|
31
|
+
console.log(chalk.dim("Aborted."));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const spinner = ora(`Uninstalling "${agentName}"...`).start();
|
|
36
|
+
try {
|
|
37
|
+
const result = await adapter.uninstall({ agentName });
|
|
38
|
+
if (result.removed) {
|
|
39
|
+
spinner.succeed(`Uninstalled "${agentName}"`);
|
|
40
|
+
for (const p of result.paths) {
|
|
41
|
+
console.log(chalk.dim(` Removed: ${p}`));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
spinner.warn(`Nothing to remove for "${agentName}"`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
spinner.fail("Uninstall failed");
|
|
50
|
+
console.error(chalk.red(error.message));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import { apiGet, apiPost } from "../utils/api.js";
|
|
7
|
+
function resolveTemplateVars(content, platformHome, workspacePath) {
|
|
8
|
+
return content
|
|
9
|
+
.replace(/\{\{OPENCLAW_HOME\}\}/g, platformHome)
|
|
10
|
+
.replace(/\{\{WORKSPACE\}\}/g, workspacePath);
|
|
11
|
+
}
|
|
12
|
+
export function makeUpdateCommand(adapter) {
|
|
13
|
+
return new Command("update")
|
|
14
|
+
.description("Update an installed agent to the latest version")
|
|
15
|
+
.argument("<agent>", "Agent to update (e.g. @user/agent-name)")
|
|
16
|
+
.action(async (agentRef) => {
|
|
17
|
+
const match = agentRef.match(/^@?([^/]+)\/(.+)$/);
|
|
18
|
+
if (!match) {
|
|
19
|
+
console.log(chalk.red("Invalid agent reference. Use @user/agent-name format."));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const [, username, agentSlug] = match;
|
|
23
|
+
const spinner = ora(`Checking for updates to @${username}/${agentSlug}...`).start();
|
|
24
|
+
try {
|
|
25
|
+
const agents = await apiGet(`/api/agents?username=${username}`);
|
|
26
|
+
const agent = agents.find((a) => a.slug === agentSlug);
|
|
27
|
+
if (!agent) {
|
|
28
|
+
spinner.fail(`Agent @${username}/${agentSlug} not found`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const result = await apiPost(`/api/agents/${agent.id}/install`, {});
|
|
32
|
+
spinner.text = "Applying updates...";
|
|
33
|
+
const workspacePath = join(adapter.home, `workspace-${agentSlug}`);
|
|
34
|
+
mkdirSync(workspacePath, { recursive: true });
|
|
35
|
+
let updated = 0;
|
|
36
|
+
for (const file of result.files) {
|
|
37
|
+
if (file.path === ".openclaw/config.json")
|
|
38
|
+
continue;
|
|
39
|
+
const filePath = join(workspacePath, file.path);
|
|
40
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
41
|
+
if (file.content !== null && file.content !== undefined) {
|
|
42
|
+
const resolved = resolveTemplateVars(file.content, adapter.home, workspacePath);
|
|
43
|
+
writeFileSync(filePath, resolved, "utf-8");
|
|
44
|
+
updated++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (result.agent.manifest) {
|
|
48
|
+
const manifestPath = join(workspacePath, "manifest.json");
|
|
49
|
+
writeFileSync(manifestPath, JSON.stringify(result.agent.manifest, null, 2) + "\n");
|
|
50
|
+
}
|
|
51
|
+
spinner.succeed(`Updated ${chalk.bold(`@${username}/${agentSlug}`)} (${updated} files)`);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
spinner.fail("Update failed");
|
|
55
|
+
console.error(chalk.red(error.message));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { authCommand } from "./commands/auth.js";
|
|
4
|
+
import { configCommand } from "./commands/config.js";
|
|
5
|
+
import { initCommand } from "./commands/init.js";
|
|
6
|
+
import { packCommand } from "./commands/pack.js";
|
|
7
|
+
import { pushCommand } from "./commands/push.js";
|
|
8
|
+
import { pullCommand } from "./commands/pull.js";
|
|
9
|
+
import { remixCommand } from "./commands/remix.js";
|
|
10
|
+
import { searchCommand } from "./commands/search.js";
|
|
11
|
+
import { getAllPlatformCommands } from "./platforms/registry.js";
|
|
12
|
+
import { setApiUrl } from "./utils/config.js";
|
|
13
|
+
import { CLI_VERSION } from "./version.js";
|
|
14
|
+
const program = new Command();
|
|
15
|
+
program
|
|
16
|
+
.name("web42")
|
|
17
|
+
.description("Web42 Agent Marketplace CLI — manage, install, and publish agent packages")
|
|
18
|
+
.version(CLI_VERSION)
|
|
19
|
+
.option("--api-url <url>", "Override the API URL for this invocation")
|
|
20
|
+
.hook("preAction", (thisCommand) => {
|
|
21
|
+
const opts = thisCommand.opts();
|
|
22
|
+
if (opts.apiUrl) {
|
|
23
|
+
setApiUrl(opts.apiUrl);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
program.addCommand(authCommand);
|
|
27
|
+
program.addCommand(configCommand);
|
|
28
|
+
program.addCommand(initCommand);
|
|
29
|
+
program.addCommand(packCommand);
|
|
30
|
+
program.addCommand(pushCommand);
|
|
31
|
+
program.addCommand(pullCommand);
|
|
32
|
+
program.addCommand(remixCommand);
|
|
33
|
+
program.addCommand(searchCommand);
|
|
34
|
+
for (const platformCmd of getAllPlatformCommands()) {
|
|
35
|
+
program.addCommand(platformCmd);
|
|
36
|
+
}
|
|
37
|
+
program.parse();
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface ConfigVariable {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
required: boolean;
|
|
6
|
+
default?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PackOptions {
|
|
9
|
+
cwd: string;
|
|
10
|
+
outputDir: string;
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface PackedFile {
|
|
14
|
+
path: string;
|
|
15
|
+
content: string;
|
|
16
|
+
hash: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PackResult {
|
|
19
|
+
files: PackedFile[];
|
|
20
|
+
configTemplate: Record<string, unknown> | null;
|
|
21
|
+
configVariables: ConfigVariable[];
|
|
22
|
+
}
|
|
23
|
+
export interface InstallOptions {
|
|
24
|
+
agentSlug: string;
|
|
25
|
+
username: string;
|
|
26
|
+
workspacePath: string;
|
|
27
|
+
files: Array<{
|
|
28
|
+
path: string;
|
|
29
|
+
content: string | null;
|
|
30
|
+
content_hash: string;
|
|
31
|
+
}>;
|
|
32
|
+
configTemplate: Record<string, unknown> | null;
|
|
33
|
+
configAnswers: Record<string, string>;
|
|
34
|
+
}
|
|
35
|
+
export interface InstallResult {
|
|
36
|
+
filesWritten: number;
|
|
37
|
+
agentDir: string;
|
|
38
|
+
}
|
|
39
|
+
export interface UninstallOptions {
|
|
40
|
+
agentName: string;
|
|
41
|
+
}
|
|
42
|
+
export interface UninstallResult {
|
|
43
|
+
removed: boolean;
|
|
44
|
+
paths: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface InstalledAgent {
|
|
47
|
+
name: string;
|
|
48
|
+
source?: string;
|
|
49
|
+
workspace: string;
|
|
50
|
+
}
|
|
51
|
+
export interface PlatformAdapter {
|
|
52
|
+
name: string;
|
|
53
|
+
home: string;
|
|
54
|
+
pack(options: PackOptions): Promise<PackResult>;
|
|
55
|
+
install(options: InstallOptions): Promise<InstallResult>;
|
|
56
|
+
uninstall(options: UninstallOptions): Promise<UninstallResult>;
|
|
57
|
+
listInstalled(): Promise<InstalledAgent[]>;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { InstalledAgent, InstallOptions, InstallResult, PackOptions, PackResult, PlatformAdapter, UninstallOptions, UninstallResult } from "../base.js";
|
|
2
|
+
export declare class OpenClawAdapter implements PlatformAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
home: string;
|
|
5
|
+
listInstalled(): Promise<InstalledAgent[]>;
|
|
6
|
+
uninstall(options: UninstallOptions): Promise<UninstallResult>;
|
|
7
|
+
pack(options: PackOptions): Promise<PackResult>;
|
|
8
|
+
install(options: InstallOptions): Promise<InstallResult>;
|
|
9
|
+
}
|
|
10
|
+
export declare const openclawAdapter: OpenClawAdapter;
|