ai-market 0.1.13 → 0.1.15

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.
@@ -5,7 +5,7 @@ import {
5
5
  publicApi,
6
6
  setApprovalContext,
7
7
  setCallbackPort
8
- } from "./chunk-2POOXGCS.js";
8
+ } from "./chunk-G27TUULG.js";
9
9
  export {
10
10
  ApiError,
11
11
  api,
@@ -118,7 +118,8 @@ var CAPABILITY_LABELS = {
118
118
  messaging: "Send and receive messages",
119
119
  pay: "Make payments and manage wallet",
120
120
  manage_listings: "Create and manage listings",
121
- withdraw: "Withdraw funds to bank account"
121
+ withdraw: "Withdraw funds to bank account",
122
+ serve_listings: "Run an API worker that handles incoming calls"
122
123
  };
123
124
  function setCallbackPort(port) {
124
125
  _callbackPort = port;
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  requireMeta,
13
13
  saveCursor,
14
14
  saveMeta
15
- } from "./chunk-2POOXGCS.js";
15
+ } from "./chunk-G27TUULG.js";
16
16
 
17
17
  // src/index.ts
18
18
  import { Command } from "commander";
@@ -44,12 +44,12 @@ function registerCommands(program2) {
44
44
  const user = program2.command("user").description("Account & identity");
45
45
  user.command("register").description("Register a new agent (opens browser for verification)").requiredOption("--name <name>", "Agent name (lowercase, 2-50 chars)").option("--offers <items>", "Comma-separated list of offerings", splitList).option("--wants <items>", "Comma-separated list of wants", splitList).option("--description <desc>", "Agent description").option("--api-url <url>", "API base URL", DEFAULT_API_URL).action(
46
46
  (opts) => run(async () => {
47
- const { setCallbackPort, setApprovalContext } = await import("./api-DQ7GOMXB.js");
47
+ const { setCallbackPort, setApprovalContext } = await import("./api-NODAAOEI.js");
48
48
  const existing = loadMeta();
49
49
  if (existing?.activeAgentId) die(`Already registered. Use "ai-market user logout" first.`);
50
50
  const apiUrl = opts.apiUrl.replace(/\/+$/, "");
51
51
  const client = getClient();
52
- const capabilities = ["browse_market", "propose_deals", "messaging", "pay", "manage_listings"];
52
+ const capabilities = ["browse_market", "propose_deals", "messaging", "pay", "manage_listings", "serve_listings"];
53
53
  const { port, waitForCallback, close } = await startCallbackServer();
54
54
  setCallbackPort(port);
55
55
  setApprovalContext({ agentName: opts.name, capabilities });
@@ -67,7 +67,8 @@ Registering agent "${opts.name}"...`);
67
67
  { name: "propose_deals" },
68
68
  { name: "messaging" },
69
69
  { name: "pay" },
70
- { name: "manage_listings" }
70
+ { name: "manage_listings" },
71
+ { name: "serve_listings" }
71
72
  ],
72
73
  mode: "delegated"
73
74
  });
@@ -111,7 +112,7 @@ Registered agent "${opts.name}" (${result.agentId})`);
111
112
  user.command("me").description("Show your agent profile").action(() => run(async () => print(await api("GET", "/v1/agents/me"))));
112
113
  user.command("request-capability").description("Request additional capabilities (opens browser for approval)").requiredOption("--capabilities <caps>", "Comma-separated capabilities to request", splitList).action(
113
114
  (opts) => run(async () => {
114
- const { setCallbackPort, setApprovalContext } = await import("./api-DQ7GOMXB.js");
115
+ const { setCallbackPort, setApprovalContext } = await import("./api-NODAAOEI.js");
115
116
  const meta = requireMeta();
116
117
  const client = getClient();
117
118
  const { port, waitForCallback, close } = await startCallbackServer();
@@ -173,7 +174,7 @@ Capabilities updated:`);
173
174
  })
174
175
  );
175
176
  const listing = program2.command("listing").description("Manage listings");
176
- listing.command("create").description("Create a new listing").requiredOption("--type <type>", '"offer" or "want"').requiredOption("--category <cat>", "Category (e.g. compute, design)").requiredOption("--title <title>", "Listing title").option("--description <desc>", "Description").option("--price <amount>", "Price in USD").option("--tags <tags>", "Comma-separated tags for search discovery", splitList).action(
177
+ listing.command("create").description("Create a new listing").requiredOption("--type <type>", '"offer" or "want"').requiredOption("--category <cat>", "Category (e.g. compute, design)").requiredOption("--title <title>", "Listing title").option("--description <desc>", "Description").option("--price <amount>", "Price in USD").option("--tags <tags>", "Comma-separated tags for search discovery", splitList).option("--delivery-kind <kind>", '"manual" (default) or "api"').option("--pricing-mode <mode>", '"fixed" (default) or "per_call"').action(
177
178
  (opts) => run(async () => {
178
179
  const body = {
179
180
  type: opts.type,
@@ -183,9 +184,17 @@ Capabilities updated:`);
183
184
  if (opts.description) body.description = opts.description;
184
185
  if (opts.price) body.price = { amount: opts.price, currency: "USD" };
185
186
  if (opts.tags) body.tags = opts.tags;
187
+ if (opts.deliveryKind) body.delivery_kind = opts.deliveryKind;
188
+ if (opts.pricingMode) body.pricing_mode = opts.pricingMode;
186
189
  print(await api("POST", "/v1/listings", body));
187
190
  })
188
191
  );
192
+ listing.command("serve <listing-id>").description("Run as an API worker for an api-delivery listing (long-running)").requiredOption("--exec <cmd>", "Shell command to invoke for each call. Receives the call payload as JSON on stdin and must print the result as JSON on stdout.").action(
193
+ (listingId, opts) => run(async () => {
194
+ const { runServe } = await import("./serve-T3IQQWPL.js");
195
+ await runServe({ listingId, exec: opts.exec });
196
+ })
197
+ );
189
198
  listing.command("list").description("Browse all listings").option("--type <type>", "Filter by offer/want").action(
190
199
  (opts) => run(async () => {
191
200
  const params = new URLSearchParams();
@@ -0,0 +1,171 @@
1
+ import {
2
+ getClient,
3
+ requireMeta
4
+ } from "./chunk-G27TUULG.js";
5
+
6
+ // src/commands/serve.ts
7
+ import WebSocket from "ws";
8
+ import { spawn } from "child_process";
9
+ var CALL_EXEC_TIMEOUT_MS = 28e3;
10
+ var RECONNECT_INITIAL_MS = 1e3;
11
+ var RECONNECT_MAX_MS = 3e4;
12
+ async function runServe(opts) {
13
+ const meta = requireMeta();
14
+ const client = getClient();
15
+ const apiUrl = new URL(meta.apiUrl);
16
+ const wsUrl = new URL(meta.apiUrl);
17
+ wsUrl.protocol = apiUrl.protocol === "https:" ? "wss:" : "ws:";
18
+ wsUrl.pathname = "/v1/agent-link";
19
+ let backoff = RECONNECT_INITIAL_MS;
20
+ let stopped = false;
21
+ let currentSocket = null;
22
+ process.on("SIGINT", () => {
23
+ stopped = true;
24
+ console.log("\nDisconnecting...");
25
+ currentSocket?.close(1e3, "shutdown");
26
+ setTimeout(() => process.exit(0), 200);
27
+ });
28
+ console.log(`Worker for listing ${opts.listingId}`);
29
+ console.log(`Handler: ${opts.exec}`);
30
+ console.log(`Connecting to ${wsUrl}...
31
+ `);
32
+ while (!stopped) {
33
+ try {
34
+ await connectAndServe({
35
+ wsUrl: wsUrl.toString(),
36
+ agentId: meta.activeAgentId,
37
+ listingId: opts.listingId,
38
+ exec: opts.exec,
39
+ signJwt: () => client.signJwt({ agentId: meta.activeAgentId, audience: meta.apiUrl }),
40
+ setCurrentSocket: (s) => {
41
+ currentSocket = s;
42
+ },
43
+ onConnected: () => {
44
+ backoff = RECONNECT_INITIAL_MS;
45
+ }
46
+ // reset on successful hello
47
+ });
48
+ } catch (err) {
49
+ console.error(`[serve] error: ${err?.message || err}`);
50
+ }
51
+ if (stopped) break;
52
+ console.error(`[serve] reconnecting in ${backoff}ms...`);
53
+ await sleep(backoff);
54
+ backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
55
+ }
56
+ }
57
+ function connectAndServe(args) {
58
+ return new Promise(async (resolve, reject) => {
59
+ let token;
60
+ try {
61
+ ({ token } = await args.signJwt());
62
+ } catch (e) {
63
+ reject(new Error(`Failed to sign JWT: ${e?.message || e}`));
64
+ return;
65
+ }
66
+ const ws = new WebSocket(args.wsUrl);
67
+ args.setCurrentSocket(ws);
68
+ let helloAcked = false;
69
+ ws.on("open", () => {
70
+ ws.send(JSON.stringify({ type: "hello", agentId: args.agentId, jwt: token }));
71
+ ws.send(JSON.stringify({ type: "serve", listingIds: [args.listingId] }));
72
+ });
73
+ ws.on("message", async (data) => {
74
+ let frame;
75
+ try {
76
+ frame = JSON.parse(data.toString());
77
+ } catch {
78
+ return;
79
+ }
80
+ if (frame.type === "welcome") {
81
+ if (!helloAcked) {
82
+ helloAcked = true;
83
+ args.onConnected();
84
+ console.log(`[serve] connected.`);
85
+ }
86
+ return;
87
+ }
88
+ if (frame.type === "serving") {
89
+ const ids = frame.listingIds || [];
90
+ console.log(`[serve] serving ${ids.length} listing(s).`);
91
+ if (!ids.includes(args.listingId)) {
92
+ console.error(`[serve] WARNING: server did not accept listing ${args.listingId}. Check that you own it and it is type=offer/delivery_kind=api.`);
93
+ }
94
+ return;
95
+ }
96
+ if (frame.type === "rejected") {
97
+ console.error(`[serve] server rejected connection: ${frame.reason}`);
98
+ ws.close();
99
+ return;
100
+ }
101
+ if (frame.type === "call") {
102
+ const { reqId, listingId, input, callerId } = frame;
103
+ console.log(`[call ${reqId.slice(0, 8)}] \u2190 ${callerId}`);
104
+ try {
105
+ const output = await runHandler(args.exec, { reqId, listingId, input, callerId });
106
+ ws.send(JSON.stringify({ type: "result", reqId, output }));
107
+ console.log(`[call ${reqId.slice(0, 8)}] \u2192 ok`);
108
+ } catch (e) {
109
+ const code = e?.code === "timeout" ? "timeout" : "handler_error";
110
+ ws.send(JSON.stringify({ type: "error", reqId, code, message: e?.message || "Handler failed" }));
111
+ console.error(`[call ${reqId.slice(0, 8)}] \u2192 error: ${e?.message}`);
112
+ }
113
+ return;
114
+ }
115
+ });
116
+ ws.on("close", (code, reason) => {
117
+ args.setCurrentSocket(null);
118
+ if (helloAcked) {
119
+ console.error(`[serve] disconnected (${code} ${reason || ""})`);
120
+ } else {
121
+ console.error(`[serve] connection closed before hello (${code} ${reason || ""})`);
122
+ }
123
+ resolve();
124
+ });
125
+ ws.on("error", (err) => {
126
+ console.error(`[serve] socket error: ${err.message}`);
127
+ });
128
+ });
129
+ }
130
+ function runHandler(exec, payload) {
131
+ return new Promise((resolve, reject) => {
132
+ const child = spawn("/bin/sh", ["-c", exec], { stdio: ["pipe", "pipe", "inherit"] });
133
+ let stdout = "";
134
+ const timer = setTimeout(() => {
135
+ child.kill("SIGKILL");
136
+ reject(Object.assign(new Error("Handler timed out"), { code: "timeout" }));
137
+ }, CALL_EXEC_TIMEOUT_MS);
138
+ child.stdout.on("data", (chunk) => {
139
+ stdout += chunk.toString();
140
+ });
141
+ child.on("error", (err) => {
142
+ clearTimeout(timer);
143
+ reject(err);
144
+ });
145
+ child.on("close", (code) => {
146
+ clearTimeout(timer);
147
+ if (code !== 0) {
148
+ reject(new Error(`Handler exited with code ${code}`));
149
+ return;
150
+ }
151
+ const trimmed = stdout.trim();
152
+ if (!trimmed) {
153
+ resolve({});
154
+ return;
155
+ }
156
+ try {
157
+ resolve(JSON.parse(trimmed));
158
+ } catch {
159
+ resolve({ text: trimmed });
160
+ }
161
+ });
162
+ child.stdin.write(JSON.stringify(payload));
163
+ child.stdin.end();
164
+ });
165
+ }
166
+ function sleep(ms) {
167
+ return new Promise((r) => setTimeout(r, ms));
168
+ }
169
+ export {
170
+ runServe
171
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-market",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "AI Market — Agent Trading Platform CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,10 +18,12 @@
18
18
  "@auth/agent": "^0.4.5",
19
19
  "commander": "^13.0.0",
20
20
  "jose": "^6.2.2",
21
- "open": "^11.0.0"
21
+ "open": "^11.0.0",
22
+ "ws": "^8.20.0"
22
23
  },
23
24
  "devDependencies": {
24
25
  "@types/node": "^22.19.15",
26
+ "@types/ws": "^8.18.1",
25
27
  "tsup": "^8.0.0",
26
28
  "typescript": "^5.7.0"
27
29
  },