@web42/w42 0.1.12 → 0.1.13
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/register.js +122 -27
- package/dist/commands/send.js +19 -32
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,51 +1,146 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
1
|
import chalk from "chalk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import inquirer from "inquirer";
|
|
3
4
|
import ora from "ora";
|
|
4
|
-
import {
|
|
5
|
+
import { apiGet, apiPost } from "../utils/api.js";
|
|
6
|
+
import { getConfig, isAuthenticated } from "../utils/config.js";
|
|
7
|
+
function toSlugPart(name) {
|
|
8
|
+
return name
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
11
|
+
.replace(/^-|-$/g, "");
|
|
12
|
+
}
|
|
5
13
|
export const registerCommand = new Command("register")
|
|
6
14
|
.description("Register an agent with the Web42 Network")
|
|
7
|
-
.argument("<url>", "Public URL of the running agent (must serve /.well-known/agent-card.json)")
|
|
15
|
+
.argument("<url>", "Public base URL of the running agent (must serve /.well-known/agent-card.json)")
|
|
8
16
|
.option("--tags <tags>", "Comma-separated discovery tags")
|
|
9
17
|
.option("--categories <cats>", "Comma-separated categories")
|
|
10
18
|
.action(async (url, opts) => {
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
if (opts.tags)
|
|
16
|
-
body.tags = opts.tags.split(",").map((t) => t.trim());
|
|
17
|
-
if (opts.categories)
|
|
18
|
-
body.categories = opts.categories.split(",").map((c) => c.trim());
|
|
19
|
+
// ── Step 1: Fetch agent card ───────────────────────────────────────────
|
|
20
|
+
const cardUrl = `${url.replace(/\/$/, "")}/.well-known/agent-card.json`;
|
|
21
|
+
const cardSpinner = ora("Fetching agent card...").start();
|
|
22
|
+
let agentCard;
|
|
19
23
|
try {
|
|
20
|
-
const res = await fetch(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Authorization: `Bearer ${config.token}`,
|
|
24
|
-
"Content-Type": "application/json",
|
|
25
|
-
},
|
|
26
|
-
body: JSON.stringify(body),
|
|
24
|
+
const res = await fetch(cardUrl, {
|
|
25
|
+
headers: { Accept: "application/json" },
|
|
26
|
+
signal: AbortSignal.timeout(10000),
|
|
27
27
|
});
|
|
28
28
|
if (!res.ok) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
console.error(chalk.red(errBody.error ?? `HTTP ${res.status}`));
|
|
29
|
+
cardSpinner.fail("Could not fetch agent card");
|
|
30
|
+
console.error(chalk.red(` ${cardUrl} returned ${res.status} ${res.statusText}`));
|
|
32
31
|
process.exit(1);
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
agentCard = (await res.json());
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
cardSpinner.fail("Could not fetch agent card");
|
|
37
|
+
console.error(chalk.red(` ${cardUrl}: ${String(err)}`));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
cardSpinner.stop();
|
|
41
|
+
// ── Step 2: URL check (no auth) ────────────────────────────────────────
|
|
42
|
+
const checkSpinner = ora("Checking registration status...").start();
|
|
43
|
+
let alreadyRegistered = false;
|
|
44
|
+
try {
|
|
45
|
+
const check = await apiGet(`/api/agents/check?url=${encodeURIComponent(url)}`);
|
|
46
|
+
if (check.registered) {
|
|
47
|
+
alreadyRegistered = true;
|
|
48
|
+
const displaySlug = check.slug.replace("~", "/");
|
|
49
|
+
checkSpinner.warn(`Already registered as ${chalk.cyan(displaySlug)}`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
checkSpinner.stop();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
checkSpinner.warn("Could not verify URL — proceeding anyway");
|
|
57
|
+
}
|
|
58
|
+
if (alreadyRegistered) {
|
|
59
|
+
const { proceed } = await inquirer.prompt([
|
|
60
|
+
{
|
|
61
|
+
type: "confirm",
|
|
62
|
+
name: "proceed",
|
|
63
|
+
message: "Continue to update the registration?",
|
|
64
|
+
default: false,
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
if (!proceed)
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
// ── Step 3: Auth gate ──────────────────────────────────────────────────
|
|
71
|
+
if (!isAuthenticated()) {
|
|
72
|
+
console.log();
|
|
73
|
+
console.log(chalk.yellow("You must be logged in to register an agent."));
|
|
74
|
+
console.log(chalk.dim("Run: ") + chalk.cyan("w42 auth login"));
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
const cfg = getConfig();
|
|
78
|
+
const username = cfg.username;
|
|
79
|
+
// ── Step 4: Slug prompt + availability check ───────────────────────────
|
|
80
|
+
const defaultSlugPart = toSlugPart(agentCard.name ?? "my-agent") || "my-agent";
|
|
81
|
+
console.log();
|
|
82
|
+
if (agentCard.name)
|
|
83
|
+
console.log(chalk.bold(agentCard.name));
|
|
84
|
+
if (agentCard.description)
|
|
85
|
+
console.log(chalk.dim(agentCard.description));
|
|
86
|
+
console.log();
|
|
87
|
+
let slugPart;
|
|
88
|
+
while (true) {
|
|
89
|
+
const answer = await inquirer.prompt([
|
|
90
|
+
{
|
|
91
|
+
type: "input",
|
|
92
|
+
name: "slugPart",
|
|
93
|
+
message: `Agent slug ${chalk.dim(`(@${username}/`)}`,
|
|
94
|
+
default: defaultSlugPart,
|
|
95
|
+
validate: (input) => {
|
|
96
|
+
if (!input)
|
|
97
|
+
return "Slug cannot be empty";
|
|
98
|
+
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(input))
|
|
99
|
+
return "Lowercase letters, numbers, and hyphens only (no leading/trailing hyphens)";
|
|
100
|
+
return true;
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
slugPart = answer.slugPart;
|
|
105
|
+
const fullSlug = `@${username}~${slugPart}`;
|
|
106
|
+
const slugSpinner = ora("Checking slug availability...").start();
|
|
107
|
+
try {
|
|
108
|
+
const check = await apiGet(`/api/agents/check?slug=${encodeURIComponent(fullSlug)}`);
|
|
109
|
+
if (check.taken) {
|
|
110
|
+
slugSpinner.fail(`${chalk.cyan(`@${username}/${slugPart}`)} is already taken — choose another`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
slugSpinner.stop();
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
slugSpinner.warn("Could not verify slug — proceeding anyway");
|
|
117
|
+
}
|
|
118
|
+
console.log(chalk.dim(` Full slug: @${username}/${slugPart}`));
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
// ── Step 5: POST ───────────────────────────────────────────────────────
|
|
122
|
+
const registerSpinner = ora("Registering agent...").start();
|
|
123
|
+
const body = { url, slug: slugPart };
|
|
124
|
+
if (opts.tags)
|
|
125
|
+
body.tags = opts.tags.split(",").map((t) => t.trim());
|
|
126
|
+
if (opts.categories)
|
|
127
|
+
body.categories = opts.categories.split(",").map((c) => c.trim());
|
|
128
|
+
try {
|
|
129
|
+
const data = await apiPost("/api/agents", body);
|
|
35
130
|
const slug = data.agent?.slug ?? "unknown";
|
|
36
|
-
const name = data.agent?.agent_card?.name ?? slug;
|
|
131
|
+
const name = data.agent?.agent_card?.name ?? agentCard.name ?? slug;
|
|
37
132
|
const displaySlug = slug.replace("~", "/");
|
|
38
133
|
if (data.created) {
|
|
39
|
-
|
|
134
|
+
registerSpinner.succeed(`Registered "${name}" (${displaySlug})`);
|
|
40
135
|
}
|
|
41
136
|
else {
|
|
42
|
-
|
|
137
|
+
registerSpinner.succeed(`Updated "${name}" (${displaySlug})`);
|
|
43
138
|
}
|
|
44
139
|
console.log(chalk.dim(` Send: w42 send ${slug} "hello"`));
|
|
45
|
-
console.log(chalk.dim(` View: ${
|
|
140
|
+
console.log(chalk.dim(` View: ${cfg.apiUrl}/${displaySlug.replace("@", "")}`));
|
|
46
141
|
}
|
|
47
142
|
catch (err) {
|
|
48
|
-
|
|
143
|
+
registerSpinner.fail("Registration failed");
|
|
49
144
|
console.error(chalk.red(String(err)));
|
|
50
145
|
process.exit(1);
|
|
51
146
|
}
|
package/dist/commands/send.js
CHANGED
|
@@ -28,6 +28,20 @@ function printPart(part) {
|
|
|
28
28
|
process.stdout.write("\n" + JSON.stringify(part.data, null, 2) + "\n");
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
function handleTaskState(state, taskId) {
|
|
32
|
+
if (state === "input-required") {
|
|
33
|
+
process.stdout.write("\n");
|
|
34
|
+
console.log(chalk.yellow(`[task ${taskId} is awaiting input — reply with --task-id ${taskId}]`));
|
|
35
|
+
}
|
|
36
|
+
else if (state === "auth-required") {
|
|
37
|
+
console.error(chalk.red("\nAgent requires authentication."));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
else if (state === "failed" || state === "canceled" || state === "rejected") {
|
|
41
|
+
console.error(chalk.red(`\nAgent ${state}.`));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
31
45
|
function getCachedToken(slug) {
|
|
32
46
|
const raw = getConfigValue(`agentTokens.${slug}`);
|
|
33
47
|
if (!raw)
|
|
@@ -186,48 +200,21 @@ export const sendCommand = new Command("send")
|
|
|
186
200
|
for (const part of event.status.message.parts ?? [])
|
|
187
201
|
printPart(part);
|
|
188
202
|
}
|
|
189
|
-
|
|
190
|
-
if (state === "input-required") {
|
|
191
|
-
process.stdout.write("\n");
|
|
192
|
-
console.log(chalk.yellow(`[task ${event.taskId} is awaiting input — reply with --task-id ${event.taskId}]`));
|
|
193
|
-
}
|
|
194
|
-
else if (state === "auth-required") {
|
|
195
|
-
console.error(chalk.red("\nAgent requires authentication."));
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
|
-
else if (state === "failed" ||
|
|
199
|
-
state === "canceled" ||
|
|
200
|
-
state === "rejected") {
|
|
201
|
-
console.error(chalk.red(`\nAgent ${state}.`));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
203
|
+
handleTaskState(event.status?.state, event.taskId);
|
|
204
204
|
}
|
|
205
205
|
else if (event.kind === "task") {
|
|
206
206
|
// Non-streaming fallback: server returned the full task object.
|
|
207
207
|
// Print accumulated artifacts and handle terminal state.
|
|
208
|
-
const
|
|
209
|
-
for (const artifact of task.artifacts ?? []) {
|
|
208
|
+
for (const artifact of event.artifacts ?? []) {
|
|
210
209
|
process.stdout.write("\n");
|
|
211
210
|
for (const part of artifact.parts ?? [])
|
|
212
211
|
printPart(part);
|
|
213
212
|
}
|
|
214
|
-
if (
|
|
215
|
-
for (const part of
|
|
213
|
+
if (event.status?.message) {
|
|
214
|
+
for (const part of event.status.message.parts ?? [])
|
|
216
215
|
printPart(part);
|
|
217
216
|
}
|
|
218
|
-
|
|
219
|
-
if (taskState === "input-required") {
|
|
220
|
-
process.stdout.write("\n");
|
|
221
|
-
console.log(chalk.yellow(`[task ${task.id} is awaiting input — reply with --task-id ${task.id}]`));
|
|
222
|
-
}
|
|
223
|
-
else if (taskState === "auth-required") {
|
|
224
|
-
console.error(chalk.red("\nAgent requires authentication."));
|
|
225
|
-
process.exit(1);
|
|
226
|
-
}
|
|
227
|
-
else if (taskState === "failed" || taskState === "canceled" || taskState === "rejected") {
|
|
228
|
-
console.error(chalk.red(`\nAgent ${taskState}.`));
|
|
229
|
-
process.exit(1);
|
|
230
|
-
}
|
|
217
|
+
handleTaskState(event.status?.state, event.id);
|
|
231
218
|
}
|
|
232
219
|
}
|
|
233
220
|
process.stdout.write("\n");
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.13";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.1.
|
|
1
|
+
export const CLI_VERSION = "0.1.13";
|