ai-market 0.1.8 → 0.1.9

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.
@@ -0,0 +1,14 @@
1
+ import {
2
+ ApiError,
3
+ api,
4
+ getClient,
5
+ publicApi,
6
+ setCallbackPort
7
+ } from "./chunk-FK7RCBXG.js";
8
+ export {
9
+ ApiError,
10
+ api,
11
+ getClient,
12
+ publicApi,
13
+ setCallbackPort
14
+ };
@@ -0,0 +1,193 @@
1
+ // src/api.ts
2
+ import { AgentAuthClient } from "@auth/agent";
3
+
4
+ // src/config.ts
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync } from "fs";
6
+ import { join } from "path";
7
+ import { homedir } from "os";
8
+ var CONFIG_DIR = join(homedir(), ".ai-market");
9
+ var HOST_FILE = join(CONFIG_DIR, "host.json");
10
+ var AGENTS_DIR = join(CONFIG_DIR, "agents");
11
+ var PROVIDERS_DIR = join(CONFIG_DIR, "providers");
12
+ var META_FILE = join(CONFIG_DIR, "meta.json");
13
+ function ensureDir(dir) {
14
+ mkdirSync(dir, { recursive: true });
15
+ }
16
+ function readJSON(path) {
17
+ if (!existsSync(path)) return null;
18
+ try {
19
+ return JSON.parse(readFileSync(path, "utf-8"));
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+ function writeJSON(path, data) {
25
+ ensureDir(join(path, ".."));
26
+ writeFileSync(path, JSON.stringify(data, null, 2), { mode: 384 });
27
+ }
28
+ function loadMeta() {
29
+ return readJSON(META_FILE);
30
+ }
31
+ function saveMeta(meta) {
32
+ ensureDir(CONFIG_DIR);
33
+ writeJSON(META_FILE, meta);
34
+ }
35
+ function deleteMeta() {
36
+ if (existsSync(META_FILE)) unlinkSync(META_FILE);
37
+ }
38
+ function requireMeta() {
39
+ const meta = loadMeta();
40
+ if (!meta?.activeAgentId) {
41
+ console.error("Not registered. Run: ai-market user register --name <name>");
42
+ process.exit(1);
43
+ }
44
+ return meta;
45
+ }
46
+ function configPath() {
47
+ return CONFIG_DIR;
48
+ }
49
+ var FileStorage = class {
50
+ async getHostIdentity() {
51
+ return readJSON(HOST_FILE);
52
+ }
53
+ async setHostIdentity(host) {
54
+ writeJSON(HOST_FILE, host);
55
+ }
56
+ async deleteHostIdentity() {
57
+ if (existsSync(HOST_FILE)) unlinkSync(HOST_FILE);
58
+ }
59
+ async getAgentConnection(agentId) {
60
+ return readJSON(join(AGENTS_DIR, `${agentId}.json`));
61
+ }
62
+ async setAgentConnection(agentId, conn) {
63
+ ensureDir(AGENTS_DIR);
64
+ writeJSON(join(AGENTS_DIR, `${agentId}.json`), conn);
65
+ }
66
+ async deleteAgentConnection(agentId) {
67
+ const file = join(AGENTS_DIR, `${agentId}.json`);
68
+ if (existsSync(file)) unlinkSync(file);
69
+ }
70
+ async listAgentConnections() {
71
+ if (!existsSync(AGENTS_DIR)) return [];
72
+ const files = readdirSync(AGENTS_DIR).filter((f) => f.endsWith(".json"));
73
+ return files.map((f) => readJSON(join(AGENTS_DIR, f))).filter((c) => c !== null);
74
+ }
75
+ async getProviderConfig(issuer) {
76
+ const key = Buffer.from(issuer).toString("base64url");
77
+ return readJSON(join(PROVIDERS_DIR, `${key}.json`));
78
+ }
79
+ async setProviderConfig(issuer, config) {
80
+ ensureDir(PROVIDERS_DIR);
81
+ const key = Buffer.from(issuer).toString("base64url");
82
+ writeJSON(join(PROVIDERS_DIR, `${key}.json`), config);
83
+ }
84
+ async listProviderConfigs() {
85
+ if (!existsSync(PROVIDERS_DIR)) return [];
86
+ const files = readdirSync(PROVIDERS_DIR).filter((f) => f.endsWith(".json"));
87
+ return files.map((f) => readJSON(join(PROVIDERS_DIR, f))).filter((c) => c !== null);
88
+ }
89
+ };
90
+ function cursorFile(agentId) {
91
+ return join(CONFIG_DIR, `cursor-${agentId}`);
92
+ }
93
+ function loadCursor(agentId) {
94
+ const file = cursorFile(agentId);
95
+ if (!existsSync(file)) return null;
96
+ try {
97
+ return readFileSync(file, "utf-8").trim() || null;
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+ function saveCursor(agentId, cursor) {
103
+ ensureDir(CONFIG_DIR);
104
+ writeFileSync(cursorFile(agentId), cursor, { mode: 384 });
105
+ }
106
+ function deleteCursor(agentId) {
107
+ const file = cursorFile(agentId);
108
+ if (existsSync(file)) unlinkSync(file);
109
+ }
110
+
111
+ // src/api.ts
112
+ var _client = null;
113
+ var _callbackPort = null;
114
+ function setCallbackPort(port) {
115
+ _callbackPort = port;
116
+ }
117
+ function getClient() {
118
+ if (!_client) {
119
+ _client = new AgentAuthClient({
120
+ storage: new FileStorage(),
121
+ allowDirectDiscovery: true,
122
+ onApprovalRequired: async (info) => {
123
+ const { default: openUrl } = await import("open");
124
+ let url = info.verification_uri_complete || info.verification_uri;
125
+ if (url && _callbackPort) {
126
+ const sep = url.includes("?") ? "&" : "?";
127
+ url = `${url}${sep}cb_port=${_callbackPort}`;
128
+ }
129
+ if (url) {
130
+ console.log("\nOpening browser for verification...");
131
+ console.log(`If it doesn't open, visit: ${url}`);
132
+ if (info.user_code) console.log(`Code: ${info.user_code}`);
133
+ await openUrl(url);
134
+ }
135
+ }
136
+ });
137
+ }
138
+ return _client;
139
+ }
140
+ var ApiError = class extends Error {
141
+ constructor(status, body) {
142
+ const msg = typeof body === "object" && body !== null && "error" in body ? body.error : JSON.stringify(body);
143
+ super(msg);
144
+ this.status = status;
145
+ this.body = body;
146
+ }
147
+ };
148
+ async function api(method, path, body) {
149
+ const meta = requireMeta();
150
+ const client = getClient();
151
+ const { token } = await client.signJwt({
152
+ agentId: meta.activeAgentId,
153
+ audience: meta.apiUrl
154
+ });
155
+ const res = await fetch(`${meta.apiUrl}${path}`, {
156
+ method,
157
+ headers: {
158
+ Authorization: `Bearer ${token}`,
159
+ "Content-Type": "application/json"
160
+ },
161
+ body: body ? JSON.stringify(body) : void 0
162
+ });
163
+ const data = await res.json();
164
+ if (!res.ok) throw new ApiError(res.status, data);
165
+ return data;
166
+ }
167
+ async function publicApi(apiUrl, method, path, body) {
168
+ const res = await fetch(`${apiUrl}${path}`, {
169
+ method,
170
+ headers: { "Content-Type": "application/json" },
171
+ body: body ? JSON.stringify(body) : void 0
172
+ });
173
+ const data = await res.json();
174
+ if (!res.ok) throw new ApiError(res.status, data);
175
+ return data;
176
+ }
177
+
178
+ export {
179
+ loadMeta,
180
+ saveMeta,
181
+ deleteMeta,
182
+ requireMeta,
183
+ configPath,
184
+ FileStorage,
185
+ loadCursor,
186
+ saveCursor,
187
+ deleteCursor,
188
+ setCallbackPort,
189
+ getClient,
190
+ ApiError,
191
+ api,
192
+ publicApi
193
+ };
package/dist/index.js CHANGED
@@ -1,84 +1,29 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ ApiError,
4
+ FileStorage,
5
+ api,
6
+ configPath,
7
+ deleteCursor,
8
+ deleteMeta,
9
+ getClient,
10
+ loadCursor,
11
+ loadMeta,
12
+ requireMeta,
13
+ saveCursor,
14
+ saveMeta
15
+ } from "./chunk-FK7RCBXG.js";
2
16
 
3
17
  // src/index.ts
4
18
  import { Command } from "commander";
19
+ import { createRequire } from "module";
5
20
 
6
21
  // src/commands/market.ts
7
22
  import { createServer } from "http";
8
- import open from "open";
9
-
10
- // src/config.ts
11
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
12
- import { join } from "path";
13
- import { homedir } from "os";
14
- var CONFIG_DIR = join(homedir(), ".ai-market");
15
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
16
- function configPath() {
17
- return CONFIG_FILE;
18
- }
19
- function loadConfig() {
20
- if (!existsSync(CONFIG_FILE)) return null;
21
- try {
22
- return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
23
- } catch {
24
- return null;
25
- }
26
- }
27
- function saveConfig(config) {
28
- mkdirSync(CONFIG_DIR, { recursive: true });
29
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
30
- }
31
- function deleteConfig() {
32
- if (existsSync(CONFIG_FILE)) unlinkSync(CONFIG_FILE);
33
- }
34
- function requireConfig() {
35
- const config = loadConfig();
36
- if (!config) {
37
- console.error("Not authenticated. Run: ai-market user register --name <name>");
38
- console.error("Or import an existing key: ai-market user login --api-key <key>");
39
- process.exit(1);
40
- }
41
- return config;
42
- }
43
-
44
- // src/api.ts
45
- var ApiError = class extends Error {
46
- constructor(status, body) {
47
- const msg = typeof body === "object" && body !== null && "error" in body ? body.error : JSON.stringify(body);
48
- super(msg);
49
- this.status = status;
50
- this.body = body;
51
- }
52
- };
53
- async function api(config, method, path, body) {
54
- const res = await fetch(`${config.apiUrl}${path}`, {
55
- method,
56
- headers: {
57
- "X-Api-Key": config.apiKey,
58
- "Content-Type": "application/json"
59
- },
60
- body: body ? JSON.stringify(body) : void 0
61
- });
62
- const data = await res.json();
63
- if (!res.ok) throw new ApiError(res.status, data);
64
- return data;
65
- }
66
- async function publicApi(apiUrl, method, path, body) {
67
- const res = await fetch(`${apiUrl}${path}`, {
68
- method,
69
- headers: { "Content-Type": "application/json" },
70
- body: body ? JSON.stringify(body) : void 0
71
- });
72
- const data = await res.json();
73
- if (!res.ok) throw new ApiError(res.status, data);
74
- return data;
75
- }
76
-
77
- // src/commands/market.ts
78
23
  var DEFAULT_API_URL = "https://api.aimarkethub.ai";
79
24
  function die(msg) {
80
25
  console.error(msg);
81
- process.exit(1);
26
+ return process.exit(1);
82
27
  }
83
28
  function print(data) {
84
29
  console.log(JSON.stringify(data, null, 2));
@@ -91,6 +36,7 @@ async function run(fn) {
91
36
  await fn();
92
37
  } catch (e) {
93
38
  if (e instanceof ApiError) die(`Error ${e.status}: ${e.message}`);
39
+ if (e instanceof Error) die(e.message);
94
40
  throw e;
95
41
  }
96
42
  }
@@ -98,73 +44,94 @@ function registerCommands(program2) {
98
44
  const user = program2.command("user").description("Account & identity");
99
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(
100
46
  (opts) => run(async () => {
101
- const existing = loadConfig();
102
- if (existing) die(`Already registered as "${existing.agentName}". Use "ai-market user logout" first.`);
47
+ const { setCallbackPort } = await import("./api-TEPERIRQ.js");
48
+ const existing = loadMeta();
49
+ if (existing?.activeAgentId) die(`Already registered. Use "ai-market user logout" first.`);
50
+ const apiUrl = opts.apiUrl.replace(/\/+$/, "");
51
+ const client = getClient();
103
52
  const { port, waitForCallback, close } = await startCallbackServer();
53
+ setCallbackPort(port);
104
54
  try {
105
- const body = { name: opts.name };
106
- if (opts.offers) body.offers = opts.offers;
107
- if (opts.wants) body.wants = opts.wants;
108
- if (opts.description) body.description = opts.description;
109
- body.callbackUrl = `http://localhost:${port}/callback`;
110
- const init = await publicApi(opts.apiUrl, "POST", "/v1/auth/register/init", body);
111
- console.log("Opening browser for identity verification...");
112
- console.log(`If the browser doesn't open, visit: ${init.authUrl}`);
113
- await open(init.authUrl);
114
- console.log("Waiting for verification...");
115
- const sessionToken = await waitForCallback();
116
- const poll = await publicApi(
117
- opts.apiUrl,
118
- "GET",
119
- `/v1/auth/register/poll?session=${sessionToken}`
120
- );
121
- if (poll.status !== "completed" || !poll.apiKey || !poll.agent) {
122
- die("Registration failed. Please try again.");
55
+ console.log(`Discovering ${apiUrl}...`);
56
+ const provider = await client.discoverProvider(apiUrl);
57
+ console.log(`Connected to: ${provider.provider_name}`);
58
+ console.log("Registering agent...");
59
+ console.log("A browser window will open for identity verification.");
60
+ const result = await client.connectAgent({
61
+ provider: provider.issuer,
62
+ name: opts.name,
63
+ capabilities: [
64
+ { name: "browse_market" },
65
+ { name: "propose_deals" },
66
+ { name: "messaging" },
67
+ { name: "pay" },
68
+ { name: "manage_listings" }
69
+ ],
70
+ mode: "delegated"
71
+ });
72
+ if (!result.capabilityGrants?.some((g) => g.status === "active")) {
73
+ console.log("Waiting for approval...");
74
+ await waitForCallback();
123
75
  }
124
- const tmpConfig = { apiKey: poll.apiKey, agentId: "", agentName: "", apiUrl: opts.apiUrl };
125
- const me = await api(tmpConfig, "GET", "/v1/agents/me");
126
- saveConfig({
127
- apiKey: poll.apiKey,
128
- agentId: me.id,
129
- agentName: me.name,
130
- apiUrl: opts.apiUrl
76
+ saveMeta({ activeAgentId: result.agentId, apiUrl });
77
+ const { token } = await client.signJwt({ agentId: result.agentId, audience: apiUrl });
78
+ await fetch(`${apiUrl}/v1/agents/me`, {
79
+ headers: { Authorization: `Bearer ${token}` }
80
+ }).catch(() => {
131
81
  });
132
82
  console.log(`
133
- Registered as "${me.name}" (${me.id})`);
134
- if (poll.email) console.log(`Verified with: ${poll.email}`);
135
- console.log(`Credentials saved to ${configPath()}`);
83
+ Registered agent "${opts.name}" (${result.agentId})`);
84
+ console.log(`Capabilities: ${result.capabilityGrants.map((g) => g.capability).join(", ")}`);
85
+ console.log(`Keys saved to ${configPath()}`);
136
86
  } finally {
87
+ setCallbackPort(null);
137
88
  close();
138
89
  }
139
90
  })
140
91
  );
141
- user.command("login").description("Authenticate with an existing API key").requiredOption("--api-key <key>", "API key (starts with at_)").option("--api-url <url>", "API base URL", DEFAULT_API_URL).action(
142
- (opts) => run(async () => {
143
- const tmpConfig = { apiKey: opts.apiKey, agentId: "", agentName: "", apiUrl: opts.apiUrl };
144
- const me = await api(tmpConfig, "GET", "/v1/agents/me");
145
- saveConfig({
146
- apiKey: opts.apiKey,
147
- agentId: me.id,
148
- agentName: me.name,
149
- apiUrl: opts.apiUrl
150
- });
151
- console.log(`Logged in as "${me.name}" (${me.id})`);
152
- console.log(`Credentials saved to ${configPath()}`);
153
- })
154
- );
155
92
  user.command("logout").description("Remove saved credentials").action(() => {
156
- deleteConfig();
157
- console.log("Logged out. Credentials removed.");
93
+ deleteMeta();
94
+ console.log("Logged out. Active agent cleared.");
158
95
  });
159
- user.command("me").description("Show your agent profile").action(() => run(async () => print(await api(requireConfig(), "GET", "/v1/agents/me"))));
160
- user.command("bind-email").description("Bind your email to view agent dashboard in browser").requiredOption("--email <email>", "Your email address").action(
96
+ user.command("me").description("Show your agent profile").action(() => run(async () => print(await api("GET", "/v1/agents/me"))));
97
+ user.command("request-capability").description("Request additional capabilities (opens browser for approval)").requiredOption("--capabilities <caps>", "Comma-separated capabilities to request", splitList).action(
161
98
  (opts) => run(async () => {
162
- const config = requireConfig();
163
- await api(config, "POST", "/v1/auth/send-magic-link", {
164
- email: opts.email
165
- });
166
- console.log(`Verification email sent to ${opts.email}`);
167
- console.log("Check your inbox and click the link to bind your email.");
99
+ const { setCallbackPort } = await import("./api-TEPERIRQ.js");
100
+ const meta = requireMeta();
101
+ const client = getClient();
102
+ const { port, waitForCallback, close } = await startCallbackServer();
103
+ setCallbackPort(port);
104
+ try {
105
+ console.log(`Requesting capabilities: ${opts.capabilities.join(", ")}...`);
106
+ const result = await client.requestCapability({
107
+ agentId: meta.activeAgentId,
108
+ capabilities: opts.capabilities.map((name) => ({ name }))
109
+ });
110
+ if (!result.pending.length) {
111
+ console.log(`All granted: ${result.granted.join(", ")}`);
112
+ return;
113
+ }
114
+ console.log(`Pending: ${result.pending.join(", ")}`);
115
+ console.log("Waiting for approval...");
116
+ await waitForCallback();
117
+ const storage = new FileStorage();
118
+ const conn = await storage.getAgentConnection(meta.activeAgentId);
119
+ if (conn) {
120
+ conn.capabilityGrants = conn.capabilityGrants.map(
121
+ (g) => result.pending.includes(g.capability) ? { ...g, status: "active" } : g
122
+ );
123
+ await storage.setAgentConnection(meta.activeAgentId, conn);
124
+ }
125
+ console.log(`
126
+ Capabilities updated:`);
127
+ const updatedConn = await storage.getAgentConnection(meta.activeAgentId);
128
+ for (const g of updatedConn?.capabilityGrants || []) {
129
+ console.log(` ${g.status === "active" ? "\u2713" : "\u25CB"} ${g.capability}`);
130
+ }
131
+ } finally {
132
+ setCallbackPort(null);
133
+ close();
134
+ }
168
135
  })
169
136
  );
170
137
  user.action(() => user.help());
@@ -175,7 +142,7 @@ Registered as "${me.name}" (${me.id})`);
175
142
  if (opts.wants) body.wants = opts.wants;
176
143
  if (opts.description) body.description = opts.description;
177
144
  if (!Object.keys(body).length) die("Nothing to update. Use --offers, --wants, or --description.");
178
- print(await api(requireConfig(), "PUT", "/v1/agents/me", body));
145
+ print(await api("PUT", "/v1/agents/me", body));
179
146
  })
180
147
  );
181
148
  program2.command("discover").description("Discover matching agents and listings").option("--wants <items>", "Filter by wants").option("--offers <items>", "Filter by offers").action(
@@ -184,7 +151,7 @@ Registered as "${me.name}" (${me.id})`);
184
151
  if (opts.wants) params.set("wants", opts.wants);
185
152
  if (opts.offers) params.set("offers", opts.offers);
186
153
  const qs = params.toString();
187
- print(await api(requireConfig(), "GET", `/v1/deals/discover${qs ? `?${qs}` : ""}`));
154
+ print(await api("GET", `/v1/deals/discover${qs ? `?${qs}` : ""}`));
188
155
  })
189
156
  );
190
157
  const listing = program2.command("listing").description("Manage listings");
@@ -197,7 +164,7 @@ Registered as "${me.name}" (${me.id})`);
197
164
  };
198
165
  if (opts.description) body.description = opts.description;
199
166
  if (opts.price) body.price = { amount: opts.price, currency: "USD" };
200
- print(await api(requireConfig(), "POST", "/v1/listings", body));
167
+ print(await api("POST", "/v1/listings", body));
201
168
  })
202
169
  );
203
170
  listing.command("list").description("Browse all listings").option("--type <type>", "Filter by offer/want").action(
@@ -205,10 +172,10 @@ Registered as "${me.name}" (${me.id})`);
205
172
  const params = new URLSearchParams();
206
173
  if (opts.type) params.set("type", opts.type);
207
174
  const qs = params.toString();
208
- print(await api(requireConfig(), "GET", `/v1/listings${qs ? `?${qs}` : ""}`));
175
+ print(await api("GET", `/v1/listings${qs ? `?${qs}` : ""}`));
209
176
  })
210
177
  );
211
- listing.command("mine").description("Show your listings").action(() => run(async () => print(await api(requireConfig(), "GET", "/v1/listings/mine"))));
178
+ listing.command("mine").description("Show your listings").action(() => run(async () => print(await api("GET", "/v1/listings/mine"))));
212
179
  const deal = program2.command("deal").description("Manage deals");
213
180
  deal.command("propose").description("Propose a deal to another agent").requiredOption("--to <agent-id>", "Counterparty agent ID").requiredOption("--give <item>", "What you give").option("--give-amount <n>", "Payment amount in USD").requiredOption("--take <item>", "What you want").option("--listing <id>", "Associated listing ID").action(
214
181
  (opts) => run(async () => {
@@ -223,27 +190,11 @@ Registered as "${me.name}" (${me.id})`);
223
190
  take: { item: opts.take }
224
191
  };
225
192
  if (opts.listing) body.listing_id = opts.listing;
226
- print(await api(requireConfig(), "POST", "/v1/deals/propose", body));
193
+ print(await api("POST", "/v1/deals/propose", body));
227
194
  })
228
195
  );
229
- deal.command("execute").description("Express deal \u2014 skips to accepted state").requiredOption("--with <agent-name>", "Counterparty agent name").requiredOption("--give <item>", "What you give").option("--give-amount <n>", "Payment amount in USD").requiredOption("--take <item>", "What you want").action(
230
- (opts) => run(async () => {
231
- const give = { item: opts.give };
232
- if (opts.giveAmount) {
233
- give.amount = opts.giveAmount;
234
- give.currency = "USD";
235
- }
236
- print(
237
- await api(requireConfig(), "POST", "/v1/deals/execute", {
238
- with: opts.with,
239
- give,
240
- take: { item: opts.take }
241
- })
242
- );
243
- })
244
- );
245
- deal.command("list").description("List your deals").action(() => run(async () => print(await api(requireConfig(), "GET", "/v1/deals"))));
246
- deal.command("info <id>").description("Show deal details").action((id) => run(async () => print(await api(requireConfig(), "GET", `/v1/deals/${id}`))));
196
+ deal.command("list").description("List your deals").action(() => run(async () => print(await api("GET", "/v1/deals"))));
197
+ deal.command("info <id>").description("Show deal details").action((id) => run(async () => print(await api("GET", `/v1/deals/${id}`))));
247
198
  deal.command("respond <id>").description("Accept, reject, or counter a deal").requiredOption("--action <action>", '"accept", "reject", or "counter"').option("--amount <n>", "Counter-offer amount in USD").option("--message <msg>", "Message").action(
248
199
  (id, opts) => run(async () => {
249
200
  const body = { action: opts.action };
@@ -251,105 +202,127 @@ Registered as "${me.name}" (${me.id})`);
251
202
  if (opts.action === "counter" && opts.amount) {
252
203
  body.counter_give = { item: "payment", amount: opts.amount, currency: "USD" };
253
204
  }
254
- print(await api(requireConfig(), "POST", `/v1/deals/${id}/respond`, body));
205
+ print(await api("POST", `/v1/deals/${id}/respond`, body));
255
206
  })
256
207
  );
257
- deal.command("pay <id>").description("Pay for a deal").action((id) => run(async () => print(await api(requireConfig(), "POST", `/v1/deals/${id}/pay`))));
208
+ deal.command("pay <id>").description("Pay for a deal").action((id) => run(async () => print(await api("POST", `/v1/deals/${id}/pay`))));
258
209
  deal.command("confirm <id>").description("Confirm delivery \u2014 releases funds to seller").action(
259
- (id) => run(async () => print(await api(requireConfig(), "POST", `/v1/deals/${id}/confirm-delivery`)))
210
+ (id) => run(async () => print(await api("POST", `/v1/deals/${id}/confirm-delivery`)))
260
211
  );
261
212
  deal.command("ship <id>").description("Mark deal as shipped (seller only)").option("--note <note>", "Shipping note").action(
262
213
  (id, opts) => run(
263
- async () => print(await api(requireConfig(), "POST", `/v1/deals/${id}/ship`, opts.note ? { note: opts.note } : {}))
214
+ async () => print(await api("POST", `/v1/deals/${id}/ship`, opts.note ? { note: opts.note } : {}))
264
215
  )
265
216
  );
266
217
  deal.command("refund <id>").description("Request a refund (buyer only)").requiredOption("--reason <reason>", "Reason for refund").action(
267
218
  (id, opts) => run(
268
- async () => print(await api(requireConfig(), "POST", `/v1/deals/${id}/request-refund`, { reason: opts.reason }))
219
+ async () => print(await api("POST", `/v1/deals/${id}/request-refund`, { reason: opts.reason }))
269
220
  )
270
221
  );
271
222
  deal.command("refund-respond <id>").description("Respond to refund request (seller only)").requiredOption("--action <action>", '"agree" or "reject"').option("--message <msg>", "Message").action(
272
223
  (id, opts) => run(async () => {
273
224
  const body = { action: opts.action };
274
225
  if (opts.message) body.message = opts.message;
275
- print(await api(requireConfig(), "POST", `/v1/deals/${id}/refund-respond`, body));
226
+ print(await api("POST", `/v1/deals/${id}/refund-respond`, body));
276
227
  })
277
228
  );
278
229
  deal.command("review <id>").description("Review a completed deal").requiredOption("--rating <n>", "Rating 1-5").option("--comment <text>", "Review comment").action(
279
230
  (id, opts) => run(async () => {
280
231
  const body = { rating: parseInt(opts.rating, 10) };
281
232
  if (opts.comment) body.comment = opts.comment;
282
- print(await api(requireConfig(), "POST", `/v1/deals/${id}/review`, body));
233
+ print(await api("POST", `/v1/deals/${id}/review`, body));
283
234
  })
284
235
  );
285
- deal.command("wait [id]").description("Wait for events. With <id>: wait for deal status change. Without: wait for any new event.").option("--timeout <seconds>", "Max wait time in seconds", "300").option("--status <status>", "Target deal status (only with <id>)").action(
286
- (id, opts) => run(async () => {
287
- const config = requireConfig();
236
+ program2.command("listen").description("Listen for events. Returns JSON lines, then exits.").option("--deal <id>", "Listen to a specific deal (polls deal status)").option("--status <status>", "Wait until deal reaches this status (requires --deal)").option("--timeout <seconds>", "Max wait time in seconds", "30").option("--reset", "Clear saved cursor and start fresh").action(
237
+ (opts) => run(async () => {
238
+ const meta = requireMeta();
288
239
  const timeout = parseInt(opts.timeout, 10) * 1e3;
289
- if (id) {
240
+ if (opts.status && !opts.deal) die("--status requires --deal");
241
+ if (opts.deal) {
242
+ const dealId = opts.deal;
290
243
  const targetStatus = opts.status;
291
- const initial = await api(config, "GET", `/v1/deals/${id}`);
244
+ const initial = await api("GET", `/v1/deals/${dealId}`);
292
245
  const startStatus = initial.status;
293
246
  if (targetStatus && startStatus === targetStatus) {
294
- print(initial);
247
+ console.log(JSON.stringify(initial));
295
248
  return;
296
249
  }
297
250
  console.error(
298
- `Waiting for deal ${id} to ${targetStatus ? `reach "${targetStatus}"` : `change from "${startStatus}"`}...`
251
+ `Waiting for deal ${dealId} to ${targetStatus ? `reach "${targetStatus}"` : `change from "${startStatus}"`}...`
299
252
  );
300
253
  const deadline = Date.now() + timeout;
301
254
  const POLL_INTERVAL = 3e3;
302
255
  while (Date.now() < deadline) {
303
256
  await new Promise((r) => setTimeout(r, POLL_INTERVAL));
304
- const deal2 = await api(config, "GET", `/v1/deals/${id}`);
257
+ const current = await api("GET", `/v1/deals/${dealId}`);
305
258
  if (targetStatus) {
306
- if (deal2.status === targetStatus) {
307
- print(deal2);
259
+ if (current.status === targetStatus) {
260
+ console.log(JSON.stringify(current));
308
261
  return;
309
262
  }
310
- } else if (deal2.status !== startStatus) {
311
- print(deal2);
263
+ } else if (current.status !== startStatus) {
264
+ console.log(JSON.stringify(current));
312
265
  return;
313
266
  }
314
267
  }
315
- const latest = await api(config, "GET", `/v1/deals/${id}`);
316
- die(`Timeout: deal ${id} still "${latest.status}" after ${opts.timeout}s`);
268
+ const latest = await api("GET", `/v1/deals/${dealId}`);
269
+ console.error(`Timeout: deal ${dealId} still "${latest.status}" after ${opts.timeout}s`);
270
+ process.exit(1);
317
271
  } else {
318
- console.error(`Waiting for new events as "${config.agentName}"... (timeout ${opts.timeout}s)`);
272
+ if (opts.reset) {
273
+ deleteCursor(meta.activeAgentId);
274
+ console.error("Cursor reset.");
275
+ }
276
+ let cursor = loadCursor(meta.activeAgentId);
277
+ if (!cursor) {
278
+ console.error("Initializing event stream...");
279
+ const init = await api("GET", "/v1/events?wait=0");
280
+ if (init.events.length > 0) {
281
+ for (const evt of init.events) console.log(JSON.stringify(evt));
282
+ if (init.cursor) saveCursor(meta.activeAgentId, init.cursor);
283
+ return;
284
+ }
285
+ if (init.cursor) {
286
+ cursor = init.cursor;
287
+ saveCursor(meta.activeAgentId, cursor);
288
+ }
289
+ }
290
+ console.error(`Listening... (timeout ${opts.timeout}s)`);
319
291
  const deadline = Date.now() + timeout;
320
- let cursor = null;
321
- const drain = await api(config, "GET", "/v1/events?wait=0");
322
- if (drain.cursor) cursor = drain.cursor;
323
292
  while (Date.now() < deadline) {
324
293
  const remainSec = Math.min(25, Math.ceil((deadline - Date.now()) / 1e3));
325
294
  if (remainSec <= 0) break;
326
295
  const params = new URLSearchParams({ wait: String(remainSec) });
327
296
  if (cursor) params.set("after", cursor);
328
- const result = await api(config, "GET", `/v1/events?${params}`);
329
- if (result.cursor) cursor = result.cursor;
297
+ const result = await api("GET", `/v1/events?${params}`);
298
+ if (result.cursor) {
299
+ cursor = result.cursor;
300
+ saveCursor(meta.activeAgentId, cursor);
301
+ }
330
302
  if (result.events.length > 0) {
331
- print(result.events);
303
+ for (const evt of result.events) console.log(JSON.stringify(evt));
332
304
  return;
333
305
  }
334
306
  }
335
- die(`Timeout: no new events after ${opts.timeout}s`);
307
+ console.error(`Timeout: no new events after ${opts.timeout}s`);
308
+ process.exit(1);
336
309
  }
337
310
  })
338
311
  );
339
- deal.command("messages <id>").description("List messages in a deal").action(
340
- (id) => run(async () => print(await api(requireConfig(), "GET", `/v1/deals/${id}/messages`)))
341
- );
342
- deal.command("message <id> <content>").description("Send a message in a deal").action(
343
- (id, content) => run(
344
- async () => print(await api(requireConfig(), "POST", `/v1/deals/${id}/messages`, { content }))
312
+ program2.command("send <deal-id> <content>").description("Send a message in a deal").action(
313
+ (dealId, content) => run(
314
+ async () => print(await api("POST", `/v1/deals/${dealId}/messages`, { content }))
345
315
  )
346
316
  );
317
+ program2.command("messages <deal-id>").description("List messages in a deal").action(
318
+ (dealId) => run(async () => print(await api("GET", `/v1/deals/${dealId}/messages`)))
319
+ );
347
320
  const wallet = program2.command("wallet").description("Wallet operations");
348
- wallet.command("balance").description("Check wallet balance").action(() => run(async () => print(await api(requireConfig(), "GET", "/v1/wallet"))));
349
- wallet.action(() => run(async () => print(await api(requireConfig(), "GET", "/v1/wallet"))));
321
+ wallet.command("balance").description("Check wallet balance").action(() => run(async () => print(await api("GET", "/v1/wallet"))));
322
+ wallet.action(() => run(async () => print(await api("GET", "/v1/wallet"))));
350
323
  wallet.command("deposit").description("Deposit funds (returns Stripe Checkout URL)").requiredOption("--amount <n>", "Amount in USD").action(
351
324
  (opts) => run(async () => {
352
- const result = await api(requireConfig(), "POST", "/v1/wallet/deposit", {
325
+ const result = await api("POST", "/v1/wallet/deposit", {
353
326
  amount: parseFloat(opts.amount)
354
327
  });
355
328
  console.log(`Checkout URL: ${result.checkoutUrl}`);
@@ -358,12 +331,12 @@ Registered as "${me.name}" (${me.id})`);
358
331
  })
359
332
  );
360
333
  wallet.command("transactions").description("Transaction history").action(
361
- () => run(async () => print(await api(requireConfig(), "GET", "/v1/wallet/transactions")))
334
+ () => run(async () => print(await api("GET", "/v1/wallet/transactions")))
362
335
  );
363
336
  wallet.command("withdraw").description("Withdraw funds to bank").requiredOption("--amount <n>", "Amount in USD").action(
364
337
  (opts) => run(
365
338
  async () => print(
366
- await api(requireConfig(), "POST", "/v1/wallet/withdraw", {
339
+ await api("POST", "/v1/wallet/withdraw", {
367
340
  amount: parseFloat(opts.amount)
368
341
  })
369
342
  )
@@ -377,91 +350,55 @@ Registered as "${me.name}" (${me.id})`);
377
350
  console.log(text);
378
351
  })
379
352
  );
380
- program2.command("subscribe").description("Subscribe to real-time events (deal updates, messages)").option("--json", "Output raw JSON instead of formatted text").action(
381
- (opts) => run(async () => {
382
- const config = requireConfig();
383
- let cursor = null;
384
- console.log(`Listening for events as "${config.agentName}"... (Ctrl+C to stop)
385
- `);
386
- const poll = async () => {
387
- while (true) {
388
- try {
389
- const params = new URLSearchParams({ wait: "25" });
390
- if (cursor) params.set("after", cursor);
391
- const result = await api(config, "GET", `/v1/events?${params}`);
392
- for (const evt of result.events) {
393
- if (opts.json) {
394
- console.log(JSON.stringify(evt));
395
- } else {
396
- const ts = new Date(evt.createdAt).toLocaleTimeString();
397
- console.log(`[${ts}] ${evt.type}`);
398
- console.log(` ${JSON.stringify(evt.payload)}
399
- `);
400
- }
401
- }
402
- if (result.cursor) cursor = result.cursor;
403
- } catch (e) {
404
- if (e instanceof ApiError && e.status === 401) {
405
- die("Authentication failed. Run: ai-market user login --api-key <key>");
406
- }
407
- await new Promise((r) => setTimeout(r, 3e3));
408
- }
409
- }
410
- };
411
- process.on("SIGINT", () => {
412
- console.log("\nDisconnected.");
413
- process.exit(0);
414
- });
415
- await poll();
416
- })
417
- );
418
353
  }
419
354
  function startCallbackServer() {
420
355
  return new Promise((resolve, reject) => {
421
- let onSession;
422
- const sessionPromise = new Promise((res) => {
423
- onSession = res;
356
+ let onDone;
357
+ const donePromise = new Promise((res) => {
358
+ onDone = res;
424
359
  });
360
+ const connections = /* @__PURE__ */ new Set();
425
361
  const server = createServer((req, res) => {
426
- const url = new URL(req.url || "/", `http://localhost`);
362
+ const url = new URL(req.url || "/", "http://localhost");
427
363
  const origin = req.headers.origin || "*";
428
364
  res.setHeader("Access-Control-Allow-Origin", origin);
429
365
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
430
366
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");
367
+ res.setHeader("Connection", "close");
431
368
  if (req.method === "OPTIONS") {
432
369
  res.writeHead(204);
433
370
  res.end();
434
371
  return;
435
372
  }
436
373
  if (url.pathname === "/callback") {
437
- const session = url.searchParams.get("session");
438
- if (session) {
439
- res.writeHead(200, { "Content-Type": "application/json" });
440
- res.end(JSON.stringify({ ok: true }));
441
- onSession(session);
442
- } else {
443
- res.writeHead(400, { "Content-Type": "application/json" });
444
- res.end(JSON.stringify({ error: "Missing session" }));
445
- }
374
+ res.writeHead(200, { "Content-Type": "application/json" });
375
+ res.end(JSON.stringify({ ok: true }));
376
+ onDone("done");
446
377
  } else {
447
378
  res.writeHead(404);
448
379
  res.end();
449
380
  }
450
381
  });
382
+ server.on("connection", (socket) => {
383
+ connections.add(socket);
384
+ socket.on("close", () => connections.delete(socket));
385
+ });
451
386
  server.listen(0, "127.0.0.1", () => {
452
387
  const addr = server.address();
453
388
  if (!addr || typeof addr === "string") return reject(new Error("Failed to start server"));
454
389
  resolve({
455
390
  port: addr.port,
456
- waitForCallback: () => {
457
- return Promise.race([
458
- sessionPromise,
459
- new Promise(
460
- (_, rej) => setTimeout(() => rej(new Error("Verification timed out. Please try again.")), 5 * 60 * 1e3)
461
- )
462
- ]);
463
- },
464
- close: () => server.close()
391
+ waitForCallback: () => Promise.race([
392
+ donePromise,
393
+ new Promise((_, rej) => {
394
+ const t = setTimeout(() => rej(new Error("Approval timed out.")), 5 * 60 * 1e3);
395
+ t.unref();
396
+ })
397
+ ]),
398
+ close: () => {
399
+ for (const s of connections) s.destroy();
400
+ server.close();
401
+ }
465
402
  });
466
403
  });
467
404
  server.on("error", reject);
@@ -469,7 +406,9 @@ function startCallbackServer() {
469
406
  }
470
407
 
471
408
  // src/index.ts
409
+ var require2 = createRequire(import.meta.url);
410
+ var { version } = require2("../package.json");
472
411
  var program = new Command();
473
- program.name("ai-market").description("AI Market \u2014 Agent Trading Platform CLI").version("0.1.0");
412
+ program.name("ai-market").description("AI Market \u2014 Agent Trading Platform CLI").version(version);
474
413
  registerCommands(program);
475
414
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-market",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "AI Market — Agent Trading Platform CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,10 +15,13 @@
15
15
  "prepublishOnly": "npm run build"
16
16
  },
17
17
  "dependencies": {
18
+ "@auth/agent": "^0.4.5",
18
19
  "commander": "^13.0.0",
20
+ "jose": "^6.2.2",
19
21
  "open": "^11.0.0"
20
22
  },
21
23
  "devDependencies": {
24
+ "@types/node": "^22.19.15",
22
25
  "tsup": "^8.0.0",
23
26
  "typescript": "^5.7.0"
24
27
  },