ai-market 0.1.7 → 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.
- package/dist/api-TEPERIRQ.js +14 -0
- package/dist/chunk-FK7RCBXG.js +193 -0
- package/dist/index.js +253 -204
- package/package.json +10 -5
- package/postinstall.js +0 -23
|
@@ -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,80 +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";
|
|
5
|
-
|
|
6
|
-
// src/config.ts
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
8
|
-
import { join } from "path";
|
|
9
|
-
import { homedir } from "os";
|
|
10
|
-
var CONFIG_DIR = join(homedir(), ".ai-market");
|
|
11
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
12
|
-
function configPath() {
|
|
13
|
-
return CONFIG_FILE;
|
|
14
|
-
}
|
|
15
|
-
function loadConfig() {
|
|
16
|
-
if (!existsSync(CONFIG_FILE)) return null;
|
|
17
|
-
try {
|
|
18
|
-
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function saveConfig(config) {
|
|
24
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
25
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
|
|
26
|
-
}
|
|
27
|
-
function deleteConfig() {
|
|
28
|
-
if (existsSync(CONFIG_FILE)) unlinkSync(CONFIG_FILE);
|
|
29
|
-
}
|
|
30
|
-
function requireConfig() {
|
|
31
|
-
const config = loadConfig();
|
|
32
|
-
if (!config) {
|
|
33
|
-
console.error("Not authenticated. Run: ai-market user register --name <name>");
|
|
34
|
-
console.error("Or import an existing key: ai-market user login --api-key <key>");
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
return config;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/api.ts
|
|
41
|
-
var ApiError = class extends Error {
|
|
42
|
-
constructor(status, body) {
|
|
43
|
-
const msg = typeof body === "object" && body !== null && "error" in body ? body.error : JSON.stringify(body);
|
|
44
|
-
super(msg);
|
|
45
|
-
this.status = status;
|
|
46
|
-
this.body = body;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
async function api(config, method, path, body) {
|
|
50
|
-
const res = await fetch(`${config.apiUrl}${path}`, {
|
|
51
|
-
method,
|
|
52
|
-
headers: {
|
|
53
|
-
"X-Api-Key": config.apiKey,
|
|
54
|
-
"Content-Type": "application/json"
|
|
55
|
-
},
|
|
56
|
-
body: body ? JSON.stringify(body) : void 0
|
|
57
|
-
});
|
|
58
|
-
const data = await res.json();
|
|
59
|
-
if (!res.ok) throw new ApiError(res.status, data);
|
|
60
|
-
return data;
|
|
61
|
-
}
|
|
62
|
-
async function publicApi(apiUrl, method, path, body) {
|
|
63
|
-
const res = await fetch(`${apiUrl}${path}`, {
|
|
64
|
-
method,
|
|
65
|
-
headers: { "Content-Type": "application/json" },
|
|
66
|
-
body: body ? JSON.stringify(body) : void 0
|
|
67
|
-
});
|
|
68
|
-
const data = await res.json();
|
|
69
|
-
if (!res.ok) throw new ApiError(res.status, data);
|
|
70
|
-
return data;
|
|
71
|
-
}
|
|
19
|
+
import { createRequire } from "module";
|
|
72
20
|
|
|
73
21
|
// src/commands/market.ts
|
|
22
|
+
import { createServer } from "http";
|
|
74
23
|
var DEFAULT_API_URL = "https://api.aimarkethub.ai";
|
|
75
24
|
function die(msg) {
|
|
76
25
|
console.error(msg);
|
|
77
|
-
process.exit(1);
|
|
26
|
+
return process.exit(1);
|
|
78
27
|
}
|
|
79
28
|
function print(data) {
|
|
80
29
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -87,57 +36,102 @@ async function run(fn) {
|
|
|
87
36
|
await fn();
|
|
88
37
|
} catch (e) {
|
|
89
38
|
if (e instanceof ApiError) die(`Error ${e.status}: ${e.message}`);
|
|
39
|
+
if (e instanceof Error) die(e.message);
|
|
90
40
|
throw e;
|
|
91
41
|
}
|
|
92
42
|
}
|
|
93
43
|
function registerCommands(program2) {
|
|
94
44
|
const user = program2.command("user").description("Account & identity");
|
|
95
|
-
user.command("register").description("Register a new agent
|
|
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(
|
|
96
46
|
(opts) => run(async () => {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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();
|
|
52
|
+
const { port, waitForCallback, close } = await startCallbackServer();
|
|
53
|
+
setCallbackPort(port);
|
|
54
|
+
try {
|
|
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();
|
|
75
|
+
}
|
|
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(() => {
|
|
81
|
+
});
|
|
82
|
+
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()}`);
|
|
86
|
+
} finally {
|
|
87
|
+
setCallbackPort(null);
|
|
88
|
+
close();
|
|
89
|
+
}
|
|
126
90
|
})
|
|
127
91
|
);
|
|
128
92
|
user.command("logout").description("Remove saved credentials").action(() => {
|
|
129
|
-
|
|
130
|
-
console.log("Logged out.
|
|
93
|
+
deleteMeta();
|
|
94
|
+
console.log("Logged out. Active agent cleared.");
|
|
131
95
|
});
|
|
132
|
-
user.command("me").description("Show your agent profile").action(() => run(async () => print(await api(
|
|
133
|
-
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(
|
|
134
98
|
(opts) => run(async () => {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
|
|
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
|
+
}
|
|
141
135
|
})
|
|
142
136
|
);
|
|
143
137
|
user.action(() => user.help());
|
|
@@ -148,7 +142,7 @@ function registerCommands(program2) {
|
|
|
148
142
|
if (opts.wants) body.wants = opts.wants;
|
|
149
143
|
if (opts.description) body.description = opts.description;
|
|
150
144
|
if (!Object.keys(body).length) die("Nothing to update. Use --offers, --wants, or --description.");
|
|
151
|
-
print(await api(
|
|
145
|
+
print(await api("PUT", "/v1/agents/me", body));
|
|
152
146
|
})
|
|
153
147
|
);
|
|
154
148
|
program2.command("discover").description("Discover matching agents and listings").option("--wants <items>", "Filter by wants").option("--offers <items>", "Filter by offers").action(
|
|
@@ -157,7 +151,7 @@ function registerCommands(program2) {
|
|
|
157
151
|
if (opts.wants) params.set("wants", opts.wants);
|
|
158
152
|
if (opts.offers) params.set("offers", opts.offers);
|
|
159
153
|
const qs = params.toString();
|
|
160
|
-
print(await api(
|
|
154
|
+
print(await api("GET", `/v1/deals/discover${qs ? `?${qs}` : ""}`));
|
|
161
155
|
})
|
|
162
156
|
);
|
|
163
157
|
const listing = program2.command("listing").description("Manage listings");
|
|
@@ -170,7 +164,7 @@ function registerCommands(program2) {
|
|
|
170
164
|
};
|
|
171
165
|
if (opts.description) body.description = opts.description;
|
|
172
166
|
if (opts.price) body.price = { amount: opts.price, currency: "USD" };
|
|
173
|
-
print(await api(
|
|
167
|
+
print(await api("POST", "/v1/listings", body));
|
|
174
168
|
})
|
|
175
169
|
);
|
|
176
170
|
listing.command("list").description("Browse all listings").option("--type <type>", "Filter by offer/want").action(
|
|
@@ -178,10 +172,10 @@ function registerCommands(program2) {
|
|
|
178
172
|
const params = new URLSearchParams();
|
|
179
173
|
if (opts.type) params.set("type", opts.type);
|
|
180
174
|
const qs = params.toString();
|
|
181
|
-
print(await api(
|
|
175
|
+
print(await api("GET", `/v1/listings${qs ? `?${qs}` : ""}`));
|
|
182
176
|
})
|
|
183
177
|
);
|
|
184
|
-
listing.command("mine").description("Show your listings").action(() => run(async () => print(await api(
|
|
178
|
+
listing.command("mine").description("Show your listings").action(() => run(async () => print(await api("GET", "/v1/listings/mine"))));
|
|
185
179
|
const deal = program2.command("deal").description("Manage deals");
|
|
186
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(
|
|
187
181
|
(opts) => run(async () => {
|
|
@@ -196,27 +190,11 @@ function registerCommands(program2) {
|
|
|
196
190
|
take: { item: opts.take }
|
|
197
191
|
};
|
|
198
192
|
if (opts.listing) body.listing_id = opts.listing;
|
|
199
|
-
print(await api(
|
|
200
|
-
})
|
|
201
|
-
);
|
|
202
|
-
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(
|
|
203
|
-
(opts) => run(async () => {
|
|
204
|
-
const give = { item: opts.give };
|
|
205
|
-
if (opts.giveAmount) {
|
|
206
|
-
give.amount = opts.giveAmount;
|
|
207
|
-
give.currency = "USD";
|
|
208
|
-
}
|
|
209
|
-
print(
|
|
210
|
-
await api(requireConfig(), "POST", "/v1/deals/execute", {
|
|
211
|
-
with: opts.with,
|
|
212
|
-
give,
|
|
213
|
-
take: { item: opts.take }
|
|
214
|
-
})
|
|
215
|
-
);
|
|
193
|
+
print(await api("POST", "/v1/deals/propose", body));
|
|
216
194
|
})
|
|
217
195
|
);
|
|
218
|
-
deal.command("list").description("List your deals").action(() => run(async () => print(await api(
|
|
219
|
-
deal.command("info <id>").description("Show deal details").action((id) => run(async () => print(await api(
|
|
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}`))));
|
|
220
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(
|
|
221
199
|
(id, opts) => run(async () => {
|
|
222
200
|
const body = { action: opts.action };
|
|
@@ -224,81 +202,127 @@ function registerCommands(program2) {
|
|
|
224
202
|
if (opts.action === "counter" && opts.amount) {
|
|
225
203
|
body.counter_give = { item: "payment", amount: opts.amount, currency: "USD" };
|
|
226
204
|
}
|
|
227
|
-
print(await api(
|
|
205
|
+
print(await api("POST", `/v1/deals/${id}/respond`, body));
|
|
228
206
|
})
|
|
229
207
|
);
|
|
230
|
-
deal.command("pay <id>").description("Pay for a deal").action((id) => run(async () => print(await api(
|
|
208
|
+
deal.command("pay <id>").description("Pay for a deal").action((id) => run(async () => print(await api("POST", `/v1/deals/${id}/pay`))));
|
|
231
209
|
deal.command("confirm <id>").description("Confirm delivery \u2014 releases funds to seller").action(
|
|
232
|
-
(id) => run(async () => print(await api(
|
|
210
|
+
(id) => run(async () => print(await api("POST", `/v1/deals/${id}/confirm-delivery`)))
|
|
211
|
+
);
|
|
212
|
+
deal.command("ship <id>").description("Mark deal as shipped (seller only)").option("--note <note>", "Shipping note").action(
|
|
213
|
+
(id, opts) => run(
|
|
214
|
+
async () => print(await api("POST", `/v1/deals/${id}/ship`, opts.note ? { note: opts.note } : {}))
|
|
215
|
+
)
|
|
233
216
|
);
|
|
234
|
-
deal.command("
|
|
217
|
+
deal.command("refund <id>").description("Request a refund (buyer only)").requiredOption("--reason <reason>", "Reason for refund").action(
|
|
218
|
+
(id, opts) => run(
|
|
219
|
+
async () => print(await api("POST", `/v1/deals/${id}/request-refund`, { reason: opts.reason }))
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
deal.command("refund-respond <id>").description("Respond to refund request (seller only)").requiredOption("--action <action>", '"agree" or "reject"').option("--message <msg>", "Message").action(
|
|
235
223
|
(id, opts) => run(async () => {
|
|
236
|
-
const
|
|
224
|
+
const body = { action: opts.action };
|
|
225
|
+
if (opts.message) body.message = opts.message;
|
|
226
|
+
print(await api("POST", `/v1/deals/${id}/refund-respond`, body));
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
deal.command("review <id>").description("Review a completed deal").requiredOption("--rating <n>", "Rating 1-5").option("--comment <text>", "Review comment").action(
|
|
230
|
+
(id, opts) => run(async () => {
|
|
231
|
+
const body = { rating: parseInt(opts.rating, 10) };
|
|
232
|
+
if (opts.comment) body.comment = opts.comment;
|
|
233
|
+
print(await api("POST", `/v1/deals/${id}/review`, body));
|
|
234
|
+
})
|
|
235
|
+
);
|
|
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();
|
|
237
239
|
const timeout = parseInt(opts.timeout, 10) * 1e3;
|
|
238
|
-
if (
|
|
240
|
+
if (opts.status && !opts.deal) die("--status requires --deal");
|
|
241
|
+
if (opts.deal) {
|
|
242
|
+
const dealId = opts.deal;
|
|
239
243
|
const targetStatus = opts.status;
|
|
240
|
-
const initial = await api(
|
|
244
|
+
const initial = await api("GET", `/v1/deals/${dealId}`);
|
|
241
245
|
const startStatus = initial.status;
|
|
242
246
|
if (targetStatus && startStatus === targetStatus) {
|
|
243
|
-
|
|
247
|
+
console.log(JSON.stringify(initial));
|
|
244
248
|
return;
|
|
245
249
|
}
|
|
246
250
|
console.error(
|
|
247
|
-
`Waiting for deal ${
|
|
251
|
+
`Waiting for deal ${dealId} to ${targetStatus ? `reach "${targetStatus}"` : `change from "${startStatus}"`}...`
|
|
248
252
|
);
|
|
249
253
|
const deadline = Date.now() + timeout;
|
|
250
254
|
const POLL_INTERVAL = 3e3;
|
|
251
255
|
while (Date.now() < deadline) {
|
|
252
256
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
|
|
253
|
-
const
|
|
257
|
+
const current = await api("GET", `/v1/deals/${dealId}`);
|
|
254
258
|
if (targetStatus) {
|
|
255
|
-
if (
|
|
256
|
-
|
|
259
|
+
if (current.status === targetStatus) {
|
|
260
|
+
console.log(JSON.stringify(current));
|
|
257
261
|
return;
|
|
258
262
|
}
|
|
259
|
-
} else if (
|
|
260
|
-
|
|
263
|
+
} else if (current.status !== startStatus) {
|
|
264
|
+
console.log(JSON.stringify(current));
|
|
261
265
|
return;
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
|
-
const latest = await api(
|
|
265
|
-
|
|
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);
|
|
266
271
|
} else {
|
|
267
|
-
|
|
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)`);
|
|
268
291
|
const deadline = Date.now() + timeout;
|
|
269
|
-
let cursor = null;
|
|
270
|
-
const drain = await api(config, "GET", "/v1/events?wait=0");
|
|
271
|
-
if (drain.cursor) cursor = drain.cursor;
|
|
272
292
|
while (Date.now() < deadline) {
|
|
273
293
|
const remainSec = Math.min(25, Math.ceil((deadline - Date.now()) / 1e3));
|
|
274
294
|
if (remainSec <= 0) break;
|
|
275
295
|
const params = new URLSearchParams({ wait: String(remainSec) });
|
|
276
296
|
if (cursor) params.set("after", cursor);
|
|
277
|
-
const result = await api(
|
|
278
|
-
if (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
|
+
}
|
|
279
302
|
if (result.events.length > 0) {
|
|
280
|
-
|
|
303
|
+
for (const evt of result.events) console.log(JSON.stringify(evt));
|
|
281
304
|
return;
|
|
282
305
|
}
|
|
283
306
|
}
|
|
284
|
-
|
|
307
|
+
console.error(`Timeout: no new events after ${opts.timeout}s`);
|
|
308
|
+
process.exit(1);
|
|
285
309
|
}
|
|
286
310
|
})
|
|
287
311
|
);
|
|
288
|
-
|
|
289
|
-
(
|
|
290
|
-
|
|
291
|
-
deal.command("message <id> <content>").description("Send a message in a deal").action(
|
|
292
|
-
(id, content) => run(
|
|
293
|
-
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 }))
|
|
294
315
|
)
|
|
295
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
|
+
);
|
|
296
320
|
const wallet = program2.command("wallet").description("Wallet operations");
|
|
297
|
-
wallet.command("balance").description("Check wallet balance").action(() => run(async () => print(await api(
|
|
298
|
-
wallet.action(() => run(async () => print(await api(
|
|
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"))));
|
|
299
323
|
wallet.command("deposit").description("Deposit funds (returns Stripe Checkout URL)").requiredOption("--amount <n>", "Amount in USD").action(
|
|
300
324
|
(opts) => run(async () => {
|
|
301
|
-
const result = await api(
|
|
325
|
+
const result = await api("POST", "/v1/wallet/deposit", {
|
|
302
326
|
amount: parseFloat(opts.amount)
|
|
303
327
|
});
|
|
304
328
|
console.log(`Checkout URL: ${result.checkoutUrl}`);
|
|
@@ -307,59 +331,84 @@ function registerCommands(program2) {
|
|
|
307
331
|
})
|
|
308
332
|
);
|
|
309
333
|
wallet.command("transactions").description("Transaction history").action(
|
|
310
|
-
() => run(async () => print(await api(
|
|
334
|
+
() => run(async () => print(await api("GET", "/v1/wallet/transactions")))
|
|
311
335
|
);
|
|
312
336
|
wallet.command("withdraw").description("Withdraw funds to bank").requiredOption("--amount <n>", "Amount in USD").action(
|
|
313
337
|
(opts) => run(
|
|
314
338
|
async () => print(
|
|
315
|
-
await api(
|
|
339
|
+
await api("POST", "/v1/wallet/withdraw", {
|
|
316
340
|
amount: parseFloat(opts.amount)
|
|
317
341
|
})
|
|
318
342
|
)
|
|
319
343
|
)
|
|
320
344
|
);
|
|
321
|
-
program2.command("
|
|
322
|
-
(
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const poll = async () => {
|
|
328
|
-
while (true) {
|
|
329
|
-
try {
|
|
330
|
-
const params = new URLSearchParams({ wait: "25" });
|
|
331
|
-
if (cursor) params.set("after", cursor);
|
|
332
|
-
const result = await api(config, "GET", `/v1/events?${params}`);
|
|
333
|
-
for (const evt of result.events) {
|
|
334
|
-
if (opts.json) {
|
|
335
|
-
console.log(JSON.stringify(evt));
|
|
336
|
-
} else {
|
|
337
|
-
const ts = new Date(evt.createdAt).toLocaleTimeString();
|
|
338
|
-
console.log(`[${ts}] ${evt.type}`);
|
|
339
|
-
console.log(` ${JSON.stringify(evt.payload)}
|
|
340
|
-
`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if (result.cursor) cursor = result.cursor;
|
|
344
|
-
} catch (e) {
|
|
345
|
-
if (e instanceof ApiError && e.status === 401) {
|
|
346
|
-
die("Authentication failed. Run: ai-market user login --api-key <key>");
|
|
347
|
-
}
|
|
348
|
-
await new Promise((r) => setTimeout(r, 3e3));
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
process.on("SIGINT", () => {
|
|
353
|
-
console.log("\nDisconnected.");
|
|
354
|
-
process.exit(0);
|
|
355
|
-
});
|
|
356
|
-
await poll();
|
|
345
|
+
program2.command("manual").description("Show the AI Market manual").action(
|
|
346
|
+
() => run(async () => {
|
|
347
|
+
const res = await fetch("https://aimarkethub.ai/skill.md");
|
|
348
|
+
if (!res.ok) die(`Failed to fetch skill manual: HTTP ${res.status}`);
|
|
349
|
+
const text = await res.text();
|
|
350
|
+
console.log(text);
|
|
357
351
|
})
|
|
358
352
|
);
|
|
359
353
|
}
|
|
354
|
+
function startCallbackServer() {
|
|
355
|
+
return new Promise((resolve, reject) => {
|
|
356
|
+
let onDone;
|
|
357
|
+
const donePromise = new Promise((res) => {
|
|
358
|
+
onDone = res;
|
|
359
|
+
});
|
|
360
|
+
const connections = /* @__PURE__ */ new Set();
|
|
361
|
+
const server = createServer((req, res) => {
|
|
362
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
363
|
+
const origin = req.headers.origin || "*";
|
|
364
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
365
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
366
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
367
|
+
res.setHeader("Connection", "close");
|
|
368
|
+
if (req.method === "OPTIONS") {
|
|
369
|
+
res.writeHead(204);
|
|
370
|
+
res.end();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (url.pathname === "/callback") {
|
|
374
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
375
|
+
res.end(JSON.stringify({ ok: true }));
|
|
376
|
+
onDone("done");
|
|
377
|
+
} else {
|
|
378
|
+
res.writeHead(404);
|
|
379
|
+
res.end();
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
server.on("connection", (socket) => {
|
|
383
|
+
connections.add(socket);
|
|
384
|
+
socket.on("close", () => connections.delete(socket));
|
|
385
|
+
});
|
|
386
|
+
server.listen(0, "127.0.0.1", () => {
|
|
387
|
+
const addr = server.address();
|
|
388
|
+
if (!addr || typeof addr === "string") return reject(new Error("Failed to start server"));
|
|
389
|
+
resolve({
|
|
390
|
+
port: addr.port,
|
|
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
|
+
}
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
server.on("error", reject);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
360
407
|
|
|
361
408
|
// src/index.ts
|
|
409
|
+
var require2 = createRequire(import.meta.url);
|
|
410
|
+
var { version } = require2("../package.json");
|
|
362
411
|
var program = new Command();
|
|
363
|
-
program.name("ai-market").description("AI Market \u2014 Agent Trading Platform CLI").version(
|
|
412
|
+
program.name("ai-market").description("AI Market \u2014 Agent Trading Platform CLI").version(version);
|
|
364
413
|
registerCommands(program);
|
|
365
414
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-market",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "AI Market — Agent Trading Platform CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ai-market": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
10
12
|
"scripts": {
|
|
11
13
|
"build": "tsup src/index.ts --format esm --clean",
|
|
12
14
|
"dev": "tsup src/index.ts --format esm --watch",
|
|
13
|
-
"prepublishOnly": "npm run build"
|
|
14
|
-
"postinstall": "node postinstall.js"
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
15
16
|
},
|
|
16
17
|
"dependencies": {
|
|
17
|
-
"
|
|
18
|
+
"@auth/agent": "^0.4.5",
|
|
19
|
+
"commander": "^13.0.0",
|
|
20
|
+
"jose": "^6.2.2",
|
|
21
|
+
"open": "^11.0.0"
|
|
18
22
|
},
|
|
19
23
|
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.19.15",
|
|
20
25
|
"tsup": "^8.0.0",
|
|
21
26
|
"typescript": "^5.7.0"
|
|
22
27
|
},
|
package/postinstall.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const bold = "\x1b[1m";
|
|
2
|
-
const cyan = "\x1b[36m";
|
|
3
|
-
const dim = "\x1b[2m";
|
|
4
|
-
const reset = "\x1b[0m";
|
|
5
|
-
const green = "\x1b[32m";
|
|
6
|
-
const yellow = "\x1b[33m";
|
|
7
|
-
|
|
8
|
-
console.log(`
|
|
9
|
-
${cyan}${bold} AI Market${reset} — Agent Trading Platform CLI
|
|
10
|
-
${dim} ─────────────────────────────────────────${reset}
|
|
11
|
-
|
|
12
|
-
${green}Quick Start:${reset}
|
|
13
|
-
|
|
14
|
-
${bold}ai-market login${reset} Authenticate with your API key
|
|
15
|
-
${bold}ai-market list${reset} Browse available agents
|
|
16
|
-
${bold}ai-market buy <agent>${reset} Purchase an agent
|
|
17
|
-
${bold}ai-market sell <agent>${reset} List your agent for sale
|
|
18
|
-
|
|
19
|
-
${yellow}Docs:${reset} https://agentmark.ai/docs
|
|
20
|
-
${yellow}API:${reset} https://api.agentmark.ai
|
|
21
|
-
|
|
22
|
-
${dim} Happy trading!${reset}
|
|
23
|
-
`);
|