ai-market 0.1.8 → 0.1.10
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/api-TEPERIRQ.js +14 -0
- package/dist/chunk-FK7RCBXG.js +193 -0
- package/dist/index.js +194 -251
- package/package.json +4 -1
|
@@ -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
|
|
102
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
134
|
-
|
|
135
|
-
console.log(`
|
|
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
|
-
|
|
157
|
-
console.log("Logged out.
|
|
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(
|
|
160
|
-
user.command("
|
|
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
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
|
|
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,16 +142,17 @@ 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(
|
|
145
|
+
print(await api("PUT", "/v1/agents/me", body));
|
|
179
146
|
})
|
|
180
147
|
);
|
|
181
|
-
program2.command("discover").description("Discover
|
|
148
|
+
program2.command("discover").description("Discover listings (default) or agents").option("--wants <items>", "Filter by wants").option("--offers <items>", "Filter by offers").option("--agents", "Also include matching agents in results").action(
|
|
182
149
|
(opts) => run(async () => {
|
|
183
150
|
const params = new URLSearchParams();
|
|
184
151
|
if (opts.wants) params.set("wants", opts.wants);
|
|
185
152
|
if (opts.offers) params.set("offers", opts.offers);
|
|
153
|
+
if (opts.agents) params.set("agents", "true");
|
|
186
154
|
const qs = params.toString();
|
|
187
|
-
print(await api(
|
|
155
|
+
print(await api("GET", `/v1/deals/discover${qs ? `?${qs}` : ""}`));
|
|
188
156
|
})
|
|
189
157
|
);
|
|
190
158
|
const listing = program2.command("listing").description("Manage listings");
|
|
@@ -197,7 +165,7 @@ Registered as "${me.name}" (${me.id})`);
|
|
|
197
165
|
};
|
|
198
166
|
if (opts.description) body.description = opts.description;
|
|
199
167
|
if (opts.price) body.price = { amount: opts.price, currency: "USD" };
|
|
200
|
-
print(await api(
|
|
168
|
+
print(await api("POST", "/v1/listings", body));
|
|
201
169
|
})
|
|
202
170
|
);
|
|
203
171
|
listing.command("list").description("Browse all listings").option("--type <type>", "Filter by offer/want").action(
|
|
@@ -205,10 +173,13 @@ Registered as "${me.name}" (${me.id})`);
|
|
|
205
173
|
const params = new URLSearchParams();
|
|
206
174
|
if (opts.type) params.set("type", opts.type);
|
|
207
175
|
const qs = params.toString();
|
|
208
|
-
print(await api(
|
|
176
|
+
print(await api("GET", `/v1/listings${qs ? `?${qs}` : ""}`));
|
|
209
177
|
})
|
|
210
178
|
);
|
|
211
|
-
listing.command("mine").description("Show your listings").action(() => run(async () => print(await api(
|
|
179
|
+
listing.command("mine").description("Show your listings").action(() => run(async () => print(await api("GET", "/v1/listings/mine"))));
|
|
180
|
+
listing.command("buy <id>").description("Buy a listing instantly (fixed-price, auto-pay)").action(
|
|
181
|
+
(id) => run(async () => print(await api("POST", `/v1/listings/${id}/buy`)))
|
|
182
|
+
);
|
|
212
183
|
const deal = program2.command("deal").description("Manage deals");
|
|
213
184
|
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
185
|
(opts) => run(async () => {
|
|
@@ -223,27 +194,11 @@ Registered as "${me.name}" (${me.id})`);
|
|
|
223
194
|
take: { item: opts.take }
|
|
224
195
|
};
|
|
225
196
|
if (opts.listing) body.listing_id = opts.listing;
|
|
226
|
-
print(await api(
|
|
197
|
+
print(await api("POST", "/v1/deals/propose", body));
|
|
227
198
|
})
|
|
228
199
|
);
|
|
229
|
-
deal.command("
|
|
230
|
-
|
|
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}`))));
|
|
200
|
+
deal.command("list").description("List your deals").action(() => run(async () => print(await api("GET", "/v1/deals"))));
|
|
201
|
+
deal.command("info <id>").description("Show deal details").action((id) => run(async () => print(await api("GET", `/v1/deals/${id}`))));
|
|
247
202
|
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
203
|
(id, opts) => run(async () => {
|
|
249
204
|
const body = { action: opts.action };
|
|
@@ -251,105 +206,127 @@ Registered as "${me.name}" (${me.id})`);
|
|
|
251
206
|
if (opts.action === "counter" && opts.amount) {
|
|
252
207
|
body.counter_give = { item: "payment", amount: opts.amount, currency: "USD" };
|
|
253
208
|
}
|
|
254
|
-
print(await api(
|
|
209
|
+
print(await api("POST", `/v1/deals/${id}/respond`, body));
|
|
255
210
|
})
|
|
256
211
|
);
|
|
257
|
-
deal.command("pay <id>").description("Pay for a deal").action((id) => run(async () => print(await api(
|
|
212
|
+
deal.command("pay <id>").description("Pay for a deal").action((id) => run(async () => print(await api("POST", `/v1/deals/${id}/pay`))));
|
|
258
213
|
deal.command("confirm <id>").description("Confirm delivery \u2014 releases funds to seller").action(
|
|
259
|
-
(id) => run(async () => print(await api(
|
|
214
|
+
(id) => run(async () => print(await api("POST", `/v1/deals/${id}/confirm-delivery`)))
|
|
260
215
|
);
|
|
261
216
|
deal.command("ship <id>").description("Mark deal as shipped (seller only)").option("--note <note>", "Shipping note").action(
|
|
262
217
|
(id, opts) => run(
|
|
263
|
-
async () => print(await api(
|
|
218
|
+
async () => print(await api("POST", `/v1/deals/${id}/ship`, opts.note ? { note: opts.note } : {}))
|
|
264
219
|
)
|
|
265
220
|
);
|
|
266
221
|
deal.command("refund <id>").description("Request a refund (buyer only)").requiredOption("--reason <reason>", "Reason for refund").action(
|
|
267
222
|
(id, opts) => run(
|
|
268
|
-
async () => print(await api(
|
|
223
|
+
async () => print(await api("POST", `/v1/deals/${id}/request-refund`, { reason: opts.reason }))
|
|
269
224
|
)
|
|
270
225
|
);
|
|
271
226
|
deal.command("refund-respond <id>").description("Respond to refund request (seller only)").requiredOption("--action <action>", '"agree" or "reject"').option("--message <msg>", "Message").action(
|
|
272
227
|
(id, opts) => run(async () => {
|
|
273
228
|
const body = { action: opts.action };
|
|
274
229
|
if (opts.message) body.message = opts.message;
|
|
275
|
-
print(await api(
|
|
230
|
+
print(await api("POST", `/v1/deals/${id}/refund-respond`, body));
|
|
276
231
|
})
|
|
277
232
|
);
|
|
278
233
|
deal.command("review <id>").description("Review a completed deal").requiredOption("--rating <n>", "Rating 1-5").option("--comment <text>", "Review comment").action(
|
|
279
234
|
(id, opts) => run(async () => {
|
|
280
235
|
const body = { rating: parseInt(opts.rating, 10) };
|
|
281
236
|
if (opts.comment) body.comment = opts.comment;
|
|
282
|
-
print(await api(
|
|
237
|
+
print(await api("POST", `/v1/deals/${id}/review`, body));
|
|
283
238
|
})
|
|
284
239
|
);
|
|
285
|
-
|
|
286
|
-
(
|
|
287
|
-
const
|
|
240
|
+
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(
|
|
241
|
+
(opts) => run(async () => {
|
|
242
|
+
const meta = requireMeta();
|
|
288
243
|
const timeout = parseInt(opts.timeout, 10) * 1e3;
|
|
289
|
-
if (
|
|
244
|
+
if (opts.status && !opts.deal) die("--status requires --deal");
|
|
245
|
+
if (opts.deal) {
|
|
246
|
+
const dealId = opts.deal;
|
|
290
247
|
const targetStatus = opts.status;
|
|
291
|
-
const initial = await api(
|
|
248
|
+
const initial = await api("GET", `/v1/deals/${dealId}`);
|
|
292
249
|
const startStatus = initial.status;
|
|
293
250
|
if (targetStatus && startStatus === targetStatus) {
|
|
294
|
-
|
|
251
|
+
console.log(JSON.stringify(initial));
|
|
295
252
|
return;
|
|
296
253
|
}
|
|
297
254
|
console.error(
|
|
298
|
-
`Waiting for deal ${
|
|
255
|
+
`Waiting for deal ${dealId} to ${targetStatus ? `reach "${targetStatus}"` : `change from "${startStatus}"`}...`
|
|
299
256
|
);
|
|
300
257
|
const deadline = Date.now() + timeout;
|
|
301
258
|
const POLL_INTERVAL = 3e3;
|
|
302
259
|
while (Date.now() < deadline) {
|
|
303
260
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
|
|
304
|
-
const
|
|
261
|
+
const current = await api("GET", `/v1/deals/${dealId}`);
|
|
305
262
|
if (targetStatus) {
|
|
306
|
-
if (
|
|
307
|
-
|
|
263
|
+
if (current.status === targetStatus) {
|
|
264
|
+
console.log(JSON.stringify(current));
|
|
308
265
|
return;
|
|
309
266
|
}
|
|
310
|
-
} else if (
|
|
311
|
-
|
|
267
|
+
} else if (current.status !== startStatus) {
|
|
268
|
+
console.log(JSON.stringify(current));
|
|
312
269
|
return;
|
|
313
270
|
}
|
|
314
271
|
}
|
|
315
|
-
const latest = await api(
|
|
316
|
-
|
|
272
|
+
const latest = await api("GET", `/v1/deals/${dealId}`);
|
|
273
|
+
console.error(`Timeout: deal ${dealId} still "${latest.status}" after ${opts.timeout}s`);
|
|
274
|
+
process.exit(1);
|
|
317
275
|
} else {
|
|
318
|
-
|
|
276
|
+
if (opts.reset) {
|
|
277
|
+
deleteCursor(meta.activeAgentId);
|
|
278
|
+
console.error("Cursor reset.");
|
|
279
|
+
}
|
|
280
|
+
let cursor = loadCursor(meta.activeAgentId);
|
|
281
|
+
if (!cursor) {
|
|
282
|
+
console.error("Initializing event stream...");
|
|
283
|
+
const init = await api("GET", "/v1/events?wait=0");
|
|
284
|
+
if (init.events.length > 0) {
|
|
285
|
+
for (const evt of init.events) console.log(JSON.stringify(evt));
|
|
286
|
+
if (init.cursor) saveCursor(meta.activeAgentId, init.cursor);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (init.cursor) {
|
|
290
|
+
cursor = init.cursor;
|
|
291
|
+
saveCursor(meta.activeAgentId, cursor);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
console.error(`Listening... (timeout ${opts.timeout}s)`);
|
|
319
295
|
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
296
|
while (Date.now() < deadline) {
|
|
324
297
|
const remainSec = Math.min(25, Math.ceil((deadline - Date.now()) / 1e3));
|
|
325
298
|
if (remainSec <= 0) break;
|
|
326
299
|
const params = new URLSearchParams({ wait: String(remainSec) });
|
|
327
300
|
if (cursor) params.set("after", cursor);
|
|
328
|
-
const result = await api(
|
|
329
|
-
if (result.cursor)
|
|
301
|
+
const result = await api("GET", `/v1/events?${params}`);
|
|
302
|
+
if (result.cursor) {
|
|
303
|
+
cursor = result.cursor;
|
|
304
|
+
saveCursor(meta.activeAgentId, cursor);
|
|
305
|
+
}
|
|
330
306
|
if (result.events.length > 0) {
|
|
331
|
-
|
|
307
|
+
for (const evt of result.events) console.log(JSON.stringify(evt));
|
|
332
308
|
return;
|
|
333
309
|
}
|
|
334
310
|
}
|
|
335
|
-
|
|
311
|
+
console.error(`Timeout: no new events after ${opts.timeout}s`);
|
|
312
|
+
process.exit(1);
|
|
336
313
|
}
|
|
337
314
|
})
|
|
338
315
|
);
|
|
339
|
-
|
|
340
|
-
(
|
|
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 }))
|
|
316
|
+
program2.command("send <deal-id> <content>").description("Send a message in a deal").action(
|
|
317
|
+
(dealId, content) => run(
|
|
318
|
+
async () => print(await api("POST", `/v1/deals/${dealId}/messages`, { content }))
|
|
345
319
|
)
|
|
346
320
|
);
|
|
321
|
+
program2.command("messages <deal-id>").description("List messages in a deal").action(
|
|
322
|
+
(dealId) => run(async () => print(await api("GET", `/v1/deals/${dealId}/messages`)))
|
|
323
|
+
);
|
|
347
324
|
const wallet = program2.command("wallet").description("Wallet operations");
|
|
348
|
-
wallet.command("balance").description("Check wallet balance").action(() => run(async () => print(await api(
|
|
349
|
-
wallet.action(() => run(async () => print(await api(
|
|
325
|
+
wallet.command("balance").description("Check wallet balance").action(() => run(async () => print(await api("GET", "/v1/wallet"))));
|
|
326
|
+
wallet.action(() => run(async () => print(await api("GET", "/v1/wallet"))));
|
|
350
327
|
wallet.command("deposit").description("Deposit funds (returns Stripe Checkout URL)").requiredOption("--amount <n>", "Amount in USD").action(
|
|
351
328
|
(opts) => run(async () => {
|
|
352
|
-
const result = await api(
|
|
329
|
+
const result = await api("POST", "/v1/wallet/deposit", {
|
|
353
330
|
amount: parseFloat(opts.amount)
|
|
354
331
|
});
|
|
355
332
|
console.log(`Checkout URL: ${result.checkoutUrl}`);
|
|
@@ -358,12 +335,12 @@ Registered as "${me.name}" (${me.id})`);
|
|
|
358
335
|
})
|
|
359
336
|
);
|
|
360
337
|
wallet.command("transactions").description("Transaction history").action(
|
|
361
|
-
() => run(async () => print(await api(
|
|
338
|
+
() => run(async () => print(await api("GET", "/v1/wallet/transactions")))
|
|
362
339
|
);
|
|
363
340
|
wallet.command("withdraw").description("Withdraw funds to bank").requiredOption("--amount <n>", "Amount in USD").action(
|
|
364
341
|
(opts) => run(
|
|
365
342
|
async () => print(
|
|
366
|
-
await api(
|
|
343
|
+
await api("POST", "/v1/wallet/withdraw", {
|
|
367
344
|
amount: parseFloat(opts.amount)
|
|
368
345
|
})
|
|
369
346
|
)
|
|
@@ -377,91 +354,55 @@ Registered as "${me.name}" (${me.id})`);
|
|
|
377
354
|
console.log(text);
|
|
378
355
|
})
|
|
379
356
|
);
|
|
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
357
|
}
|
|
419
358
|
function startCallbackServer() {
|
|
420
359
|
return new Promise((resolve, reject) => {
|
|
421
|
-
let
|
|
422
|
-
const
|
|
423
|
-
|
|
360
|
+
let onDone;
|
|
361
|
+
const donePromise = new Promise((res) => {
|
|
362
|
+
onDone = res;
|
|
424
363
|
});
|
|
364
|
+
const connections = /* @__PURE__ */ new Set();
|
|
425
365
|
const server = createServer((req, res) => {
|
|
426
|
-
const url = new URL(req.url || "/",
|
|
366
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
427
367
|
const origin = req.headers.origin || "*";
|
|
428
368
|
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
429
369
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
430
370
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
371
|
+
res.setHeader("Connection", "close");
|
|
431
372
|
if (req.method === "OPTIONS") {
|
|
432
373
|
res.writeHead(204);
|
|
433
374
|
res.end();
|
|
434
375
|
return;
|
|
435
376
|
}
|
|
436
377
|
if (url.pathname === "/callback") {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
}
|
|
378
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
379
|
+
res.end(JSON.stringify({ ok: true }));
|
|
380
|
+
onDone("done");
|
|
446
381
|
} else {
|
|
447
382
|
res.writeHead(404);
|
|
448
383
|
res.end();
|
|
449
384
|
}
|
|
450
385
|
});
|
|
386
|
+
server.on("connection", (socket) => {
|
|
387
|
+
connections.add(socket);
|
|
388
|
+
socket.on("close", () => connections.delete(socket));
|
|
389
|
+
});
|
|
451
390
|
server.listen(0, "127.0.0.1", () => {
|
|
452
391
|
const addr = server.address();
|
|
453
392
|
if (!addr || typeof addr === "string") return reject(new Error("Failed to start server"));
|
|
454
393
|
resolve({
|
|
455
394
|
port: addr.port,
|
|
456
|
-
waitForCallback: () =>
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
new
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
395
|
+
waitForCallback: () => Promise.race([
|
|
396
|
+
donePromise,
|
|
397
|
+
new Promise((_, rej) => {
|
|
398
|
+
const t = setTimeout(() => rej(new Error("Approval timed out.")), 5 * 60 * 1e3);
|
|
399
|
+
t.unref();
|
|
400
|
+
})
|
|
401
|
+
]),
|
|
402
|
+
close: () => {
|
|
403
|
+
for (const s of connections) s.destroy();
|
|
404
|
+
server.close();
|
|
405
|
+
}
|
|
465
406
|
});
|
|
466
407
|
});
|
|
467
408
|
server.on("error", reject);
|
|
@@ -469,7 +410,9 @@ function startCallbackServer() {
|
|
|
469
410
|
}
|
|
470
411
|
|
|
471
412
|
// src/index.ts
|
|
413
|
+
var require2 = createRequire(import.meta.url);
|
|
414
|
+
var { version } = require2("../package.json");
|
|
472
415
|
var program = new Command();
|
|
473
|
-
program.name("ai-market").description("AI Market \u2014 Agent Trading Platform CLI").version(
|
|
416
|
+
program.name("ai-market").description("AI Market \u2014 Agent Trading Platform CLI").version(version);
|
|
474
417
|
registerCommands(program);
|
|
475
418
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-market",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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
|
},
|