openpond-code 0.1.0
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/CHANGELOG.md +5 -0
- package/README.md +121 -0
- package/RELEASE.md +24 -0
- package/dist/api.d.ts +247 -0
- package/dist/cache.d.ts +22 -0
- package/dist/cli.js +1767 -0
- package/dist/config.d.ts +16 -0
- package/dist/index.d.ts +154 -0
- package/dist/index.js +1346 -0
- package/dist/indicators.d.ts +38 -0
- package/dist/stream.d.ts +20 -0
- package/dist/types.d.ts +58 -0
- package/package.json +59 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1767 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli-package.ts
|
|
4
|
+
import {spawn} from "node:child_process";
|
|
5
|
+
import {promises as fs3, existsSync} from "node:fs";
|
|
6
|
+
import path3 from "node:path";
|
|
7
|
+
import {stdin as input, stdout as output} from "node:process";
|
|
8
|
+
import {createInterface} from "node:readline/promises";
|
|
9
|
+
|
|
10
|
+
// src/api.ts
|
|
11
|
+
async function apiFetch(baseUrl, token, path, init) {
|
|
12
|
+
const headers = new Headers(init?.headers || {});
|
|
13
|
+
headers.set("Content-Type", "application/json");
|
|
14
|
+
const apiKey = process.env.OPENPOND_API_KEY;
|
|
15
|
+
const trimmedToken = token?.trim() || "";
|
|
16
|
+
const tokenIsApiKey = trimmedToken.startsWith("opk_");
|
|
17
|
+
const effectiveApiKey = apiKey || (tokenIsApiKey ? trimmedToken : null);
|
|
18
|
+
if (effectiveApiKey && !headers.has("openpond-api-key")) {
|
|
19
|
+
headers.set("openpond-api-key", effectiveApiKey);
|
|
20
|
+
}
|
|
21
|
+
if (token) {
|
|
22
|
+
headers.set("Authorization", tokenIsApiKey ? `ApiKey ${trimmedToken}` : `Bearer ${token}`);
|
|
23
|
+
} else if (apiKey && !headers.has("Authorization")) {
|
|
24
|
+
headers.set("Authorization", `ApiKey ${apiKey}`);
|
|
25
|
+
}
|
|
26
|
+
return fetch(`${baseUrl}${path}`, {
|
|
27
|
+
...init,
|
|
28
|
+
headers
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function listApps(apiBase, token, options) {
|
|
32
|
+
const params = new URLSearchParams;
|
|
33
|
+
if (options?.handle) {
|
|
34
|
+
params.set("handle", options.handle);
|
|
35
|
+
}
|
|
36
|
+
const query = params.toString();
|
|
37
|
+
const response = await apiFetch(apiBase, token, `/apps/list${query ? `?${query}` : ""}`, { method: "GET" });
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const text = await response.text().catch(() => "");
|
|
40
|
+
throw new Error(`Apps list failed: ${response.status} ${text}`);
|
|
41
|
+
}
|
|
42
|
+
const payload = await response.json().catch(() => ({}));
|
|
43
|
+
return Array.isArray(payload.apps) ? payload.apps : [];
|
|
44
|
+
}
|
|
45
|
+
async function createRepo(apiBase, apiKey, input) {
|
|
46
|
+
const response = await apiFetch(apiBase, apiKey, "/apps/repo/create", {
|
|
47
|
+
method: "POST",
|
|
48
|
+
body: JSON.stringify(input)
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const text = await response.text().catch(() => "");
|
|
52
|
+
throw new Error(`Repo create failed: ${response.status} ${text}`);
|
|
53
|
+
}
|
|
54
|
+
return await response.json();
|
|
55
|
+
}
|
|
56
|
+
async function createHeadlessApps(baseUrl, token, items) {
|
|
57
|
+
const response = await apiFetch(baseUrl, token, "/v4/apps/headless", {
|
|
58
|
+
method: "POST",
|
|
59
|
+
body: JSON.stringify({ items })
|
|
60
|
+
});
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const text = await response.text().catch(() => "");
|
|
63
|
+
throw new Error(`Headless create failed: ${response.status} ${text}`);
|
|
64
|
+
}
|
|
65
|
+
return await response.json();
|
|
66
|
+
}
|
|
67
|
+
async function getTemplateStatus(apiBase, token, appId) {
|
|
68
|
+
const response = await apiFetch(apiBase, token, `/v4/apps/${appId}/template/status`, { method: "GET" });
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const text = await response.text().catch(() => "");
|
|
71
|
+
throw new Error(`Template status failed: ${response.status} ${text}`);
|
|
72
|
+
}
|
|
73
|
+
return await response.json();
|
|
74
|
+
}
|
|
75
|
+
async function listTemplateBranches(apiBase, token, appId) {
|
|
76
|
+
const response = await apiFetch(apiBase, token, `/v4/apps/${appId}/template/branches`, { method: "GET" });
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
const text = await response.text().catch(() => "");
|
|
79
|
+
throw new Error(`Template branches failed: ${response.status} ${text}`);
|
|
80
|
+
}
|
|
81
|
+
return await response.json();
|
|
82
|
+
}
|
|
83
|
+
async function deployLatestTemplate(apiBase, token, appId, input) {
|
|
84
|
+
const response = await apiFetch(apiBase, token, `/v4/apps/${appId}/template/deploy-latest`, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
body: JSON.stringify(input)
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const text = await response.text().catch(() => "");
|
|
90
|
+
throw new Error(`Template deploy failed: ${response.status} ${text}`);
|
|
91
|
+
}
|
|
92
|
+
return await response.json();
|
|
93
|
+
}
|
|
94
|
+
async function updateAppEnvironment(apiBase, token, appId, input) {
|
|
95
|
+
const response = await apiFetch(apiBase, token, `/v4/apps/${appId}/environment`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
body: JSON.stringify(input)
|
|
98
|
+
});
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
const text = await response.text().catch(() => "");
|
|
101
|
+
throw new Error(`Environment update failed: ${response.status} ${text}`);
|
|
102
|
+
}
|
|
103
|
+
return await response.json();
|
|
104
|
+
}
|
|
105
|
+
async function getAppEnvironment(apiBase, token, appId) {
|
|
106
|
+
const response = await apiFetch(apiBase, token, `/v4/apps/${appId}/environment`, {
|
|
107
|
+
method: "GET"
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
const text = await response.text().catch(() => "");
|
|
111
|
+
throw new Error(`Environment get failed: ${response.status} ${text}`);
|
|
112
|
+
}
|
|
113
|
+
return await response.json();
|
|
114
|
+
}
|
|
115
|
+
async function listUserTools(baseUrl, token) {
|
|
116
|
+
const response = await apiFetch(baseUrl, token, "/apps/tools", {
|
|
117
|
+
method: "GET"
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const text = await response.text().catch(() => "");
|
|
121
|
+
throw new Error(`Tools lookup failed: ${response.status} ${text}`);
|
|
122
|
+
}
|
|
123
|
+
return await response.json();
|
|
124
|
+
}
|
|
125
|
+
async function createAgentFromPrompt(baseUrl, token, payload) {
|
|
126
|
+
return apiFetch(baseUrl, token, "/apps/agent/create", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
body: JSON.stringify(payload)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async function getUserPerformance(baseUrl, token, options) {
|
|
132
|
+
const params = new URLSearchParams;
|
|
133
|
+
if (options?.appId) {
|
|
134
|
+
params.set("appId", options.appId);
|
|
135
|
+
}
|
|
136
|
+
const qs = params.toString();
|
|
137
|
+
const response = await apiFetch(baseUrl, token, `/apps/performance${qs ? `?${qs}` : ""}`, {
|
|
138
|
+
method: "GET"
|
|
139
|
+
});
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
const text = await response.text().catch(() => "");
|
|
142
|
+
throw new Error(`Performance lookup failed: ${response.status} ${text}`);
|
|
143
|
+
}
|
|
144
|
+
return await response.json();
|
|
145
|
+
}
|
|
146
|
+
async function executeUserTool(baseUrl, token, body) {
|
|
147
|
+
const response = await apiFetch(baseUrl, token, "/apps/tools/execute", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
body: JSON.stringify(body)
|
|
150
|
+
});
|
|
151
|
+
const text = await response.text().catch(() => "");
|
|
152
|
+
let payload = null;
|
|
153
|
+
try {
|
|
154
|
+
payload = text ? JSON.parse(text) : null;
|
|
155
|
+
} catch {
|
|
156
|
+
payload = text;
|
|
157
|
+
}
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
status: response.status,
|
|
162
|
+
error: payload && typeof payload === "object" && "error" in payload ? String(payload.error) : text || response.statusText
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
status: response.status,
|
|
168
|
+
data: payload
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async function submitPositionsTx(baseUrl, token, params) {
|
|
172
|
+
const qs = params.query && Object.keys(params.query).length > 0 ? `?${new URLSearchParams(params.query).toString()}` : "";
|
|
173
|
+
const response = await apiFetch(baseUrl, token, `/apps/positions/tx${qs}`, {
|
|
174
|
+
method: params.method,
|
|
175
|
+
body: params.method === "POST" && params.body !== undefined ? JSON.stringify(params.body) : undefined
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const text = await response.text().catch(() => "");
|
|
179
|
+
throw new Error(`Positions request failed: ${response.status} ${text}`);
|
|
180
|
+
}
|
|
181
|
+
return await response.json();
|
|
182
|
+
}
|
|
183
|
+
async function deployApp(baseUrl, token, appId, input) {
|
|
184
|
+
const environment = input?.environment ?? "production";
|
|
185
|
+
const response = await apiFetch(baseUrl, token, `/v4/apps/${appId}/deployments`, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
body: JSON.stringify({
|
|
188
|
+
environment,
|
|
189
|
+
...input?.commitSha ? { commitSha: input.commitSha } : {},
|
|
190
|
+
...input?.branch ? { branch: input.branch } : {}
|
|
191
|
+
})
|
|
192
|
+
});
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
const text = await response.text().catch(() => "");
|
|
195
|
+
throw new Error(`Deploy failed: ${response.status} ${text}`);
|
|
196
|
+
}
|
|
197
|
+
return await response.json();
|
|
198
|
+
}
|
|
199
|
+
async function getDeploymentLogs(apiBase, token, deploymentId) {
|
|
200
|
+
const response = await apiFetch(apiBase, token, `/apps/deployments/${deploymentId}/logs`, { method: "GET" });
|
|
201
|
+
if (!response.ok) {
|
|
202
|
+
const text = await response.text().catch(() => "");
|
|
203
|
+
throw new Error(`Deployment logs failed: ${response.status} ${text}`);
|
|
204
|
+
}
|
|
205
|
+
const payload = await response.json().catch(() => ({}));
|
|
206
|
+
const logs = Array.isArray(payload.logs) ? payload.logs : [];
|
|
207
|
+
return logs.map((log) => {
|
|
208
|
+
const createdAt = typeof log.createdAt === "string" ? log.createdAt : log.createdAt instanceof Date ? log.createdAt.toISOString() : new Date().toISOString();
|
|
209
|
+
return {
|
|
210
|
+
id: typeof log.id === "string" ? log.id : `${Math.random()}`,
|
|
211
|
+
type: typeof log.type === "string" ? log.type : undefined,
|
|
212
|
+
message: typeof log.message === "string" ? log.message : "",
|
|
213
|
+
createdAt
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async function getDeploymentStatus(apiBase, token, deploymentId) {
|
|
218
|
+
const response = await apiFetch(apiBase, token, `/apps/deployments/${deploymentId}/status`, { method: "GET" });
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
const text = await response.text().catch(() => "");
|
|
221
|
+
throw new Error(`Deployment status failed: ${response.status} ${text}`);
|
|
222
|
+
}
|
|
223
|
+
const payload = await response.json().catch(() => ({}));
|
|
224
|
+
return { status: payload.deployment?.status };
|
|
225
|
+
}
|
|
226
|
+
async function getLatestDeploymentForApp(apiBase, token, appId, options) {
|
|
227
|
+
const params = new URLSearchParams;
|
|
228
|
+
if (options?.status && options.status.length > 0) {
|
|
229
|
+
params.set("status", options.status.join(","));
|
|
230
|
+
}
|
|
231
|
+
if (options?.createdAfter) {
|
|
232
|
+
params.set("createdAfter", options.createdAfter);
|
|
233
|
+
}
|
|
234
|
+
if (options?.branch) {
|
|
235
|
+
params.set("branch", options.branch);
|
|
236
|
+
}
|
|
237
|
+
const query = params.toString();
|
|
238
|
+
const response = await apiFetch(apiBase, token, `/apps/${appId}/deployments/latest${query ? `?${query}` : ""}`, { method: "GET" });
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
const text = await response.text().catch(() => "");
|
|
241
|
+
throw new Error(`Latest deployment lookup failed: ${response.status} ${text}`);
|
|
242
|
+
}
|
|
243
|
+
const payload = await response.json().catch(() => ({}));
|
|
244
|
+
if (!payload.deployment)
|
|
245
|
+
return null;
|
|
246
|
+
return {
|
|
247
|
+
id: payload.deployment.id,
|
|
248
|
+
status: payload.deployment.status
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async function getDeploymentDetail(apiBase, token, deploymentId) {
|
|
252
|
+
const response = await apiFetch(apiBase, token, `/apps/deployments/${deploymentId}`, { method: "GET" });
|
|
253
|
+
if (!response.ok) {
|
|
254
|
+
const text = await response.text().catch(() => "");
|
|
255
|
+
throw new Error(`Deployment fetch failed: ${response.status} ${text}`);
|
|
256
|
+
}
|
|
257
|
+
const payload = await response.json().catch(() => ({}));
|
|
258
|
+
return payload.deployment ?? null;
|
|
259
|
+
}
|
|
260
|
+
var normalizeToolPathSegment = function(toolName) {
|
|
261
|
+
const trimmed = toolName.trim().replace(/^\/+/, "");
|
|
262
|
+
return encodeURIComponent(trimmed || "tool");
|
|
263
|
+
};
|
|
264
|
+
function resolveWorkerBaseUrl(baseUrl) {
|
|
265
|
+
const trimmed = baseUrl.replace(/\/$/, "");
|
|
266
|
+
const workerEnv = process.env.OPENPOND_TOOL_URL;
|
|
267
|
+
if (workerEnv) {
|
|
268
|
+
return workerEnv.replace(/\/$/, "");
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const url = new URL(trimmed);
|
|
272
|
+
const host = url.hostname.toLowerCase();
|
|
273
|
+
const mappedHost = (() => {
|
|
274
|
+
if (host === "apps.openpond.live") {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
if (host === "api.openpond.ai" || host === "openpond.live" || host === "www.openpond.live") {
|
|
278
|
+
return "https://apps.openpond.live";
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
})();
|
|
282
|
+
if (mappedHost) {
|
|
283
|
+
return mappedHost;
|
|
284
|
+
}
|
|
285
|
+
const isLocal = host === "localhost" || host === "127.0.0.1";
|
|
286
|
+
const port = url.port || (url.protocol === "https:" ? "443" : "80");
|
|
287
|
+
if (isLocal && port === "3000") {
|
|
288
|
+
return trimmed;
|
|
289
|
+
}
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
292
|
+
return trimmed;
|
|
293
|
+
}
|
|
294
|
+
async function executeHostedTool(baseUrl, token, payload) {
|
|
295
|
+
const workerBase = resolveWorkerBaseUrl(baseUrl);
|
|
296
|
+
const toolPath = normalizeToolPathSegment(payload.toolName);
|
|
297
|
+
const deploymentPrefix = payload.deploymentId ? `/${payload.appId}/deployments/${payload.deploymentId}` : `/${payload.appId}`;
|
|
298
|
+
const requestPath = `${deploymentPrefix}/${toolPath}`;
|
|
299
|
+
const headers = new Headers(payload.headers || {});
|
|
300
|
+
const method = payload.method ?? "POST";
|
|
301
|
+
const body = payload.body === undefined || method === "GET" ? undefined : JSON.stringify(payload.body);
|
|
302
|
+
const response = await apiFetch(workerBase, token, requestPath, {
|
|
303
|
+
method,
|
|
304
|
+
body,
|
|
305
|
+
headers
|
|
306
|
+
});
|
|
307
|
+
const text = await response.text().catch(() => "");
|
|
308
|
+
let data = null;
|
|
309
|
+
try {
|
|
310
|
+
data = text ? JSON.parse(text) : null;
|
|
311
|
+
} catch {
|
|
312
|
+
data = text;
|
|
313
|
+
}
|
|
314
|
+
const dataOk = data && typeof data === "object" && "ok" in data ? Boolean(data.ok) : true;
|
|
315
|
+
const ok = response.ok && dataOk;
|
|
316
|
+
const status = data && typeof data === "object" && "status" in data ? Number(data.status) || response.status : response.status;
|
|
317
|
+
const error = data && typeof data === "object" && "error" in data ? String(data.error) : response.ok ? undefined : text || response.statusText;
|
|
318
|
+
const payloadData = data && typeof data === "object" && "data" in data ? data.data : data;
|
|
319
|
+
return { ok, status, data: payloadData, error };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/cache.ts
|
|
323
|
+
import {promises as fs} from "node:fs";
|
|
324
|
+
import os from "node:os";
|
|
325
|
+
import path from "node:path";
|
|
326
|
+
var getCachePath = function() {
|
|
327
|
+
return path.join(os.homedir(), CACHE_DIR, CACHE_FILENAME);
|
|
328
|
+
};
|
|
329
|
+
var buildCacheKey = function(apiBase, apiKey) {
|
|
330
|
+
const trimmed = apiKey.trim();
|
|
331
|
+
const hint = trimmed.length > 12 ? `${trimmed.slice(0, 8)}_${trimmed.slice(-4)}` : trimmed;
|
|
332
|
+
try {
|
|
333
|
+
const host = new URL(apiBase).host;
|
|
334
|
+
return `${host}:${hint}`;
|
|
335
|
+
} catch {
|
|
336
|
+
return `${apiBase}:${hint}`;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
var isFresh = function(updatedAt, ttlMs) {
|
|
340
|
+
const timestamp = Date.parse(updatedAt);
|
|
341
|
+
if (Number.isNaN(timestamp)) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
return Date.now() - timestamp < ttlMs;
|
|
345
|
+
};
|
|
346
|
+
async function loadCache() {
|
|
347
|
+
try {
|
|
348
|
+
const raw = await fs.readFile(getCachePath(), "utf-8");
|
|
349
|
+
const parsed = JSON.parse(raw);
|
|
350
|
+
if (!parsed || typeof parsed !== "object" || !parsed.byKey) {
|
|
351
|
+
return DEFAULT_STORE;
|
|
352
|
+
}
|
|
353
|
+
return parsed;
|
|
354
|
+
} catch {
|
|
355
|
+
return DEFAULT_STORE;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async function saveCache(store) {
|
|
359
|
+
const filePath = getCachePath();
|
|
360
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
361
|
+
await fs.writeFile(filePath, JSON.stringify(store, null, 2), "utf-8");
|
|
362
|
+
}
|
|
363
|
+
async function getCachedApps(params) {
|
|
364
|
+
const ttlMs = params.ttlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
365
|
+
const store = await loadCache();
|
|
366
|
+
const cacheKey = buildCacheKey(params.apiBase, params.apiKey);
|
|
367
|
+
const entry = store.byKey[cacheKey]?.apps;
|
|
368
|
+
if (!entry || !isFresh(entry.updatedAt, ttlMs)) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
return Array.isArray(entry.items) ? entry.items : null;
|
|
372
|
+
}
|
|
373
|
+
async function setCachedApps(params) {
|
|
374
|
+
const store = await loadCache();
|
|
375
|
+
const cacheKey = buildCacheKey(params.apiBase, params.apiKey);
|
|
376
|
+
const bucket = store.byKey[cacheKey] || {};
|
|
377
|
+
bucket.apps = {
|
|
378
|
+
items: params.apps,
|
|
379
|
+
updatedAt: new Date().toISOString()
|
|
380
|
+
};
|
|
381
|
+
store.byKey[cacheKey] = bucket;
|
|
382
|
+
await saveCache(store);
|
|
383
|
+
}
|
|
384
|
+
async function getCachedTools(params) {
|
|
385
|
+
const ttlMs = params.ttlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
386
|
+
const store = await loadCache();
|
|
387
|
+
const cacheKey = buildCacheKey(params.apiBase, params.apiKey);
|
|
388
|
+
const entry = store.byKey[cacheKey]?.tools;
|
|
389
|
+
if (!entry || !isFresh(entry.updatedAt, ttlMs)) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
return Array.isArray(entry.items) ? entry.items : null;
|
|
393
|
+
}
|
|
394
|
+
async function setCachedTools(params) {
|
|
395
|
+
const store = await loadCache();
|
|
396
|
+
const cacheKey = buildCacheKey(params.apiBase, params.apiKey);
|
|
397
|
+
const bucket = store.byKey[cacheKey] || {};
|
|
398
|
+
bucket.tools = {
|
|
399
|
+
items: params.tools,
|
|
400
|
+
updatedAt: new Date().toISOString()
|
|
401
|
+
};
|
|
402
|
+
store.byKey[cacheKey] = bucket;
|
|
403
|
+
await saveCache(store);
|
|
404
|
+
}
|
|
405
|
+
var CACHE_DIR = ".openpond";
|
|
406
|
+
var CACHE_FILENAME = "cache.json";
|
|
407
|
+
var DEFAULT_STORE = { version: 1, byKey: {} };
|
|
408
|
+
var DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
409
|
+
|
|
410
|
+
// src/config.ts
|
|
411
|
+
import {promises as fs2} from "node:fs";
|
|
412
|
+
import os2 from "node:os";
|
|
413
|
+
import path2 from "node:path";
|
|
414
|
+
var getGlobalConfigPath = function() {
|
|
415
|
+
return path2.join(os2.homedir(), GLOBAL_DIRNAME, GLOBAL_CONFIG_FILENAME);
|
|
416
|
+
};
|
|
417
|
+
async function loadConfigFile(filePath) {
|
|
418
|
+
try {
|
|
419
|
+
const raw = await fs2.readFile(filePath, "utf-8");
|
|
420
|
+
return JSON.parse(raw);
|
|
421
|
+
} catch {
|
|
422
|
+
return {};
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
async function loadGlobalConfig() {
|
|
426
|
+
return loadConfigFile(getGlobalConfigPath());
|
|
427
|
+
}
|
|
428
|
+
async function loadConfig() {
|
|
429
|
+
return loadGlobalConfig();
|
|
430
|
+
}
|
|
431
|
+
async function saveGlobalConfig(next) {
|
|
432
|
+
const filePath = getGlobalConfigPath();
|
|
433
|
+
await fs2.mkdir(path2.dirname(filePath), { recursive: true });
|
|
434
|
+
const current = await loadGlobalConfig();
|
|
435
|
+
const merged = { ...current };
|
|
436
|
+
for (const [key, value] of Object.entries(next)) {
|
|
437
|
+
if (value === undefined)
|
|
438
|
+
continue;
|
|
439
|
+
if (value === null) {
|
|
440
|
+
delete merged[key];
|
|
441
|
+
} else {
|
|
442
|
+
merged[key] = value;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const payload = JSON.stringify(merged, null, 2);
|
|
446
|
+
await fs2.writeFile(filePath, payload, "utf-8");
|
|
447
|
+
}
|
|
448
|
+
var GLOBAL_DIRNAME = ".openpond";
|
|
449
|
+
var GLOBAL_CONFIG_FILENAME = "config.json";
|
|
450
|
+
|
|
451
|
+
// src/stream.ts
|
|
452
|
+
function normalizeDataFrames(raw) {
|
|
453
|
+
const frames = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.data) ? raw.data : [raw];
|
|
454
|
+
const items = [];
|
|
455
|
+
let conversationId;
|
|
456
|
+
for (const frame of frames) {
|
|
457
|
+
if (!frame || typeof frame !== "object")
|
|
458
|
+
continue;
|
|
459
|
+
const typed = frame;
|
|
460
|
+
const type = typeof typed.type === "string" ? typed.type : undefined;
|
|
461
|
+
if (type === "conversation-created") {
|
|
462
|
+
const cid = typed.conversationId;
|
|
463
|
+
if (typeof cid === "string") {
|
|
464
|
+
conversationId = cid;
|
|
465
|
+
}
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (type === "response_items" && Array.isArray(typed.items)) {
|
|
469
|
+
items.push(...typed.items);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (type === "response_item" && typed.item) {
|
|
473
|
+
items.push(typed.item);
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (type === "assistant-message" && typeof typed.content === "string") {
|
|
477
|
+
items.push({
|
|
478
|
+
type: "message",
|
|
479
|
+
role: "assistant",
|
|
480
|
+
id: `assistant-${Date.now()}`,
|
|
481
|
+
content: [{ type: "markdown", text: typed.content }],
|
|
482
|
+
createdAt: typeof typed.createdAt === "string" ? typed.createdAt : new Date().toISOString()
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return { conversationId, items };
|
|
487
|
+
}
|
|
488
|
+
var extractUsage = function(raw) {
|
|
489
|
+
const frames = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.data) ? raw.data : [raw];
|
|
490
|
+
for (const frame of frames) {
|
|
491
|
+
if (!frame || typeof frame !== "object")
|
|
492
|
+
continue;
|
|
493
|
+
const typed = frame;
|
|
494
|
+
if (typed.type !== "usage")
|
|
495
|
+
continue;
|
|
496
|
+
const usage = typed.usage;
|
|
497
|
+
if (!usage)
|
|
498
|
+
continue;
|
|
499
|
+
const promptTokens = typeof usage.promptTokens === "number" ? usage.promptTokens : typeof usage.prompt_tokens === "number" ? usage.prompt_tokens : undefined;
|
|
500
|
+
const completionTokens = typeof usage.completionTokens === "number" ? usage.completionTokens : typeof usage.completion_tokens === "number" ? usage.completion_tokens : undefined;
|
|
501
|
+
const totalTokens = typeof usage.totalTokens === "number" ? usage.totalTokens : typeof usage.total_tokens === "number" ? usage.total_tokens : undefined;
|
|
502
|
+
return { promptTokens, completionTokens, totalTokens };
|
|
503
|
+
}
|
|
504
|
+
return null;
|
|
505
|
+
};
|
|
506
|
+
async function consumeStream(response, callbacks) {
|
|
507
|
+
if (!response.body) {
|
|
508
|
+
throw new Error("Missing response body");
|
|
509
|
+
}
|
|
510
|
+
const reader = response.body.getReader();
|
|
511
|
+
const decoder = new TextDecoder;
|
|
512
|
+
let remainder = "";
|
|
513
|
+
const shouldStop = callbacks.shouldStop;
|
|
514
|
+
const stopIfNeeded = async () => {
|
|
515
|
+
if (!shouldStop || !shouldStop()) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
try {
|
|
519
|
+
await reader.cancel();
|
|
520
|
+
} catch {
|
|
521
|
+
}
|
|
522
|
+
callbacks.onStop?.();
|
|
523
|
+
return true;
|
|
524
|
+
};
|
|
525
|
+
while (true) {
|
|
526
|
+
const { done, value } = await reader.read();
|
|
527
|
+
if (done)
|
|
528
|
+
break;
|
|
529
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
530
|
+
const textChunk = remainder + chunk;
|
|
531
|
+
const lines = textChunk.split("\n");
|
|
532
|
+
remainder = lines.pop() || "";
|
|
533
|
+
for (const line of lines) {
|
|
534
|
+
if (!line)
|
|
535
|
+
continue;
|
|
536
|
+
if (line.startsWith("0:")) {
|
|
537
|
+
const payload = line.slice(2).trim();
|
|
538
|
+
if (!payload)
|
|
539
|
+
continue;
|
|
540
|
+
const delta = JSON.parse(payload);
|
|
541
|
+
if (typeof delta === "string") {
|
|
542
|
+
callbacks.onTextDelta?.(delta);
|
|
543
|
+
}
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (line.startsWith("g:")) {
|
|
547
|
+
const payload = line.slice(2).trim();
|
|
548
|
+
if (!payload)
|
|
549
|
+
continue;
|
|
550
|
+
const delta = JSON.parse(payload);
|
|
551
|
+
if (typeof delta === "string") {
|
|
552
|
+
callbacks.onReasoningDelta?.(delta);
|
|
553
|
+
}
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (line.startsWith("d:")) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (line.startsWith("3:")) {
|
|
560
|
+
const payload = line.slice(2).trim();
|
|
561
|
+
if (!payload)
|
|
562
|
+
continue;
|
|
563
|
+
try {
|
|
564
|
+
const message = JSON.parse(payload);
|
|
565
|
+
if (typeof message === "string") {
|
|
566
|
+
throw new Error(message);
|
|
567
|
+
}
|
|
568
|
+
} catch {
|
|
569
|
+
}
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (line.startsWith("2:")) {
|
|
573
|
+
const payload = line.slice(2).trim();
|
|
574
|
+
if (!payload)
|
|
575
|
+
continue;
|
|
576
|
+
try {
|
|
577
|
+
const dataItems = JSON.parse(payload);
|
|
578
|
+
const usage = extractUsage(dataItems);
|
|
579
|
+
if (usage) {
|
|
580
|
+
callbacks.onUsage?.(usage);
|
|
581
|
+
}
|
|
582
|
+
const { conversationId, items } = normalizeDataFrames(dataItems);
|
|
583
|
+
if (conversationId) {
|
|
584
|
+
callbacks.onConversationId?.(conversationId);
|
|
585
|
+
}
|
|
586
|
+
if (items.length > 0) {
|
|
587
|
+
await Promise.resolve(callbacks.onItems?.(items));
|
|
588
|
+
}
|
|
589
|
+
if (await stopIfNeeded()) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
} catch {
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (await stopIfNeeded()) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function formatStreamItem(item) {
|
|
602
|
+
if (!item || typeof item !== "object")
|
|
603
|
+
return null;
|
|
604
|
+
const type = item.type;
|
|
605
|
+
if (typeof type !== "string")
|
|
606
|
+
return null;
|
|
607
|
+
if (type === "app_creation_started") {
|
|
608
|
+
const name = item.appName || item.name || item.appId || "app";
|
|
609
|
+
const templateId = typeof item.templateId === "string" ? item.templateId : null;
|
|
610
|
+
const suffix = templateId ? ` template=${templateId}` : "";
|
|
611
|
+
return `app_creation_started: ${name}${suffix}`;
|
|
612
|
+
}
|
|
613
|
+
if (type === "app_created") {
|
|
614
|
+
const appId = typeof item.appId === "string" ? item.appId : "unknown";
|
|
615
|
+
const name = typeof item.appName === "string" ? item.appName : null;
|
|
616
|
+
return name ? `app_created: ${name} (${appId})` : `app_created: ${appId}`;
|
|
617
|
+
}
|
|
618
|
+
if (type.startsWith("tool_creation_")) {
|
|
619
|
+
const message = item.message || item.content || "";
|
|
620
|
+
return message ? `${type}: ${message}` : type;
|
|
621
|
+
}
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/cli-package.ts
|
|
626
|
+
var parseArgs = function(argv) {
|
|
627
|
+
const args = [...argv];
|
|
628
|
+
const command = args.shift() || "";
|
|
629
|
+
const options = {};
|
|
630
|
+
const rest = [];
|
|
631
|
+
while (args.length > 0) {
|
|
632
|
+
const next = args.shift();
|
|
633
|
+
if (next.startsWith("--")) {
|
|
634
|
+
const rawKey = next.slice(2);
|
|
635
|
+
const key = rawKey.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
636
|
+
const value = args[0] && !args[0].startsWith("--") ? args.shift() : "true";
|
|
637
|
+
options[key] = value;
|
|
638
|
+
} else {
|
|
639
|
+
rest.push(next);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return { command, options, rest };
|
|
643
|
+
};
|
|
644
|
+
var resolveBaseUrl = function(config2) {
|
|
645
|
+
const envBase = process.env.OPENPOND_BASE_URL;
|
|
646
|
+
const base = envBase || config2.baseUrl || "https://openpond.ai";
|
|
647
|
+
return base.replace(/\/$/, "");
|
|
648
|
+
};
|
|
649
|
+
var resolvePublicApiBaseUrl = function() {
|
|
650
|
+
const envBase = process.env.OPENPOND_API_URL;
|
|
651
|
+
const base = envBase || "https://api.openpond.ai";
|
|
652
|
+
return base.replace(/\/$/, "");
|
|
653
|
+
};
|
|
654
|
+
var normalizeTemplateRepoUrl = function(input2, baseUrl) {
|
|
655
|
+
const trimmed = input2.trim();
|
|
656
|
+
if (!trimmed) {
|
|
657
|
+
throw new Error("template must be non-empty");
|
|
658
|
+
}
|
|
659
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
660
|
+
return trimmed.endsWith(".git") ? trimmed : `${trimmed}.git`;
|
|
661
|
+
}
|
|
662
|
+
const normalizedBase = baseUrl.replace(/\/$/, "");
|
|
663
|
+
const [owner, repoRaw] = trimmed.includes("/") ? trimmed.split("/", 2) : ["openpondai", trimmed];
|
|
664
|
+
const repo = repoRaw.endsWith(".git") ? repoRaw.slice(0, -4) : repoRaw;
|
|
665
|
+
if (!owner || !repo) {
|
|
666
|
+
throw new Error("template must be <owner>/<repo> or a full https URL");
|
|
667
|
+
}
|
|
668
|
+
return `${normalizedBase}/${owner}/${repo}.git`;
|
|
669
|
+
};
|
|
670
|
+
var parseJsonOption = function(value, label) {
|
|
671
|
+
try {
|
|
672
|
+
return JSON.parse(value);
|
|
673
|
+
} catch {
|
|
674
|
+
throw new Error(`${label} must be valid JSON`);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
var parseBooleanOption = function(value) {
|
|
678
|
+
if (value === true)
|
|
679
|
+
return true;
|
|
680
|
+
if (typeof value === "string") {
|
|
681
|
+
return value.toLowerCase() === "true";
|
|
682
|
+
}
|
|
683
|
+
return false;
|
|
684
|
+
};
|
|
685
|
+
var parseTimeOption = function(value, label) {
|
|
686
|
+
if (typeof value !== "string")
|
|
687
|
+
return;
|
|
688
|
+
const trimmed = value.trim();
|
|
689
|
+
if (!trimmed)
|
|
690
|
+
return;
|
|
691
|
+
if (/^\d+$/.test(trimmed))
|
|
692
|
+
return trimmed;
|
|
693
|
+
const parsed = Date.parse(trimmed);
|
|
694
|
+
if (!Number.isNaN(parsed)) {
|
|
695
|
+
return String(parsed);
|
|
696
|
+
}
|
|
697
|
+
throw new Error(`${label} must be a unix ms timestamp or ISO date`);
|
|
698
|
+
};
|
|
699
|
+
var resolveApiKey = function(config2) {
|
|
700
|
+
const envKey = process.env.OPENPOND_API_KEY?.trim();
|
|
701
|
+
if (envKey)
|
|
702
|
+
return envKey;
|
|
703
|
+
const stored = config2.apiKey?.trim();
|
|
704
|
+
if (stored)
|
|
705
|
+
return stored;
|
|
706
|
+
const legacy = config2.token?.trim();
|
|
707
|
+
if (legacy && legacy.startsWith("opk_"))
|
|
708
|
+
return legacy;
|
|
709
|
+
return null;
|
|
710
|
+
};
|
|
711
|
+
var resolveTemplateEnvironment = function(value) {
|
|
712
|
+
if (!value)
|
|
713
|
+
return "production";
|
|
714
|
+
const normalized = value.toLowerCase();
|
|
715
|
+
if (normalized === "preview" || normalized === "production") {
|
|
716
|
+
return normalized;
|
|
717
|
+
}
|
|
718
|
+
throw new Error("env must be preview or production");
|
|
719
|
+
};
|
|
720
|
+
async function promptForApiKey() {
|
|
721
|
+
console.log("Open the OpenPond UI to create an API key:");
|
|
722
|
+
console.log(UI_API_KEY_URL);
|
|
723
|
+
const rl = createInterface({ input, output });
|
|
724
|
+
try {
|
|
725
|
+
const value = (await rl.question("Paste your OpenPond API key: ")).trim();
|
|
726
|
+
if (!value) {
|
|
727
|
+
throw new Error("API key is required");
|
|
728
|
+
}
|
|
729
|
+
if (!value.startsWith("opk_")) {
|
|
730
|
+
console.log("warning: API keys usually start with opk_.");
|
|
731
|
+
}
|
|
732
|
+
return value;
|
|
733
|
+
} finally {
|
|
734
|
+
rl.close();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
async function ensureApiKey(config2, baseUrl) {
|
|
738
|
+
const existing = resolveApiKey(config2);
|
|
739
|
+
if (existing)
|
|
740
|
+
return existing;
|
|
741
|
+
const apiKey = await promptForApiKey();
|
|
742
|
+
await saveGlobalConfig({ apiKey, baseUrl });
|
|
743
|
+
console.log("saved api key to ~/.openpond/config.json");
|
|
744
|
+
return apiKey;
|
|
745
|
+
}
|
|
746
|
+
async function promptConfirm(question, defaultValue = false) {
|
|
747
|
+
const rl = createInterface({ input, output });
|
|
748
|
+
try {
|
|
749
|
+
const suffix = defaultValue ? "[Y/n]" : "[y/N]";
|
|
750
|
+
const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();
|
|
751
|
+
if (!answer)
|
|
752
|
+
return defaultValue;
|
|
753
|
+
return answer === "y" || answer === "yes";
|
|
754
|
+
} finally {
|
|
755
|
+
rl.close();
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
async function promptForPath(defaultPath) {
|
|
759
|
+
const rl = createInterface({ input, output });
|
|
760
|
+
try {
|
|
761
|
+
const answer = (await rl.question(`Local path (default: ${defaultPath}): `)).trim();
|
|
762
|
+
return answer || defaultPath;
|
|
763
|
+
} finally {
|
|
764
|
+
rl.close();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async function runCommand(command, args, options = {}) {
|
|
768
|
+
return new Promise((resolve, reject) => {
|
|
769
|
+
const proc = spawn(command, args, {
|
|
770
|
+
cwd: options.cwd,
|
|
771
|
+
stdio: options.inherit ? "inherit" : "pipe"
|
|
772
|
+
});
|
|
773
|
+
let stdout = "";
|
|
774
|
+
let stderr = "";
|
|
775
|
+
if (!options.inherit) {
|
|
776
|
+
proc.stdout?.on("data", (chunk) => {
|
|
777
|
+
stdout += String(chunk);
|
|
778
|
+
});
|
|
779
|
+
proc.stderr?.on("data", (chunk) => {
|
|
780
|
+
stderr += String(chunk);
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
proc.on("error", reject);
|
|
784
|
+
proc.on("close", (code) => {
|
|
785
|
+
resolve({ code, stdout, stderr });
|
|
786
|
+
});
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
async function getGitRemoteUrl(cwd, remoteName) {
|
|
790
|
+
const result = await runCommand("git", ["remote", "get-url", remoteName], { cwd });
|
|
791
|
+
if (result.code !== 0)
|
|
792
|
+
return null;
|
|
793
|
+
const url = result.stdout.trim();
|
|
794
|
+
return url.length > 0 ? url : null;
|
|
795
|
+
}
|
|
796
|
+
var resolveRepoUrl = function(response) {
|
|
797
|
+
if (response.repoUrl)
|
|
798
|
+
return response.repoUrl;
|
|
799
|
+
if (response.gitHost && response.gitOwner && response.gitRepo) {
|
|
800
|
+
return `https://${response.gitHost}/${response.gitOwner}/${response.gitRepo}.git`;
|
|
801
|
+
}
|
|
802
|
+
throw new Error("repoUrl missing from API response");
|
|
803
|
+
};
|
|
804
|
+
var formatTokenizedRepoUrl = function(repoUrl, token) {
|
|
805
|
+
const url = new URL(repoUrl);
|
|
806
|
+
const encodedToken = encodeURIComponent(token);
|
|
807
|
+
return `${url.protocol}//x-access-token:${encodedToken}@${url.host}${url.pathname}`;
|
|
808
|
+
};
|
|
809
|
+
var formatTokenizedRepoUrlForPrint = function(repoUrl) {
|
|
810
|
+
const url = new URL(repoUrl);
|
|
811
|
+
return `${url.protocol}//x-access-token:\$OPENPOND_API_KEY@${url.host}${url.pathname}`;
|
|
812
|
+
};
|
|
813
|
+
var redactToken = function(value) {
|
|
814
|
+
return value.replace(/x-access-token:[^@]+@/g, "x-access-token:***@");
|
|
815
|
+
};
|
|
816
|
+
var warnOnRepoHostMismatch = function(repoUrl) {
|
|
817
|
+
const envBase = process.env.OPENPOND_BASE_URL;
|
|
818
|
+
if (!envBase)
|
|
819
|
+
return;
|
|
820
|
+
try {
|
|
821
|
+
const baseHost = new URL(envBase).hostname;
|
|
822
|
+
const repoHost = new URL(repoUrl).hostname;
|
|
823
|
+
if (baseHost && repoHost && baseHost !== repoHost) {
|
|
824
|
+
console.warn(`warning: repo host (${repoHost}) does not match OPENPOND_BASE_URL (${baseHost})`);
|
|
825
|
+
console.warn("warning: if this is staging, ensure INTERNAL_GIT_HOST is set on the deployment worker.");
|
|
826
|
+
}
|
|
827
|
+
} catch {
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
var parseHandleRepo = function(value) {
|
|
831
|
+
const parts = value.split("/").filter(Boolean);
|
|
832
|
+
if (parts.length !== 2) {
|
|
833
|
+
throw new Error("expected <handle>/<repo>");
|
|
834
|
+
}
|
|
835
|
+
return { handle: parts[0], repo: parts[1] };
|
|
836
|
+
};
|
|
837
|
+
var normalizeRepoName = function(value) {
|
|
838
|
+
return (value || "").trim().toLowerCase();
|
|
839
|
+
};
|
|
840
|
+
async function fetchAppsWithCache(params) {
|
|
841
|
+
if (!params.forceRefresh) {
|
|
842
|
+
const cached = await getCachedApps({
|
|
843
|
+
apiBase: params.apiBase,
|
|
844
|
+
apiKey: params.apiKey,
|
|
845
|
+
ttlMs: DEFAULT_CACHE_TTL_MS
|
|
846
|
+
});
|
|
847
|
+
if (cached) {
|
|
848
|
+
return cached;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const apps = await listApps(params.apiBase, params.apiKey);
|
|
852
|
+
await setCachedApps({
|
|
853
|
+
apiBase: params.apiBase,
|
|
854
|
+
apiKey: params.apiKey,
|
|
855
|
+
apps
|
|
856
|
+
});
|
|
857
|
+
return apps;
|
|
858
|
+
}
|
|
859
|
+
async function fetchToolsWithCache(params) {
|
|
860
|
+
if (!params.forceRefresh) {
|
|
861
|
+
const cached = await getCachedTools({
|
|
862
|
+
apiBase: params.apiBase,
|
|
863
|
+
apiKey: params.apiKey,
|
|
864
|
+
ttlMs: DEFAULT_CACHE_TTL_MS
|
|
865
|
+
});
|
|
866
|
+
if (cached) {
|
|
867
|
+
return cached;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const result = await listUserTools(params.apiBase, params.apiKey);
|
|
871
|
+
const tools = Array.isArray(result.tools) ? result.tools : [];
|
|
872
|
+
await setCachedTools({
|
|
873
|
+
apiBase: params.apiBase,
|
|
874
|
+
apiKey: params.apiKey,
|
|
875
|
+
tools
|
|
876
|
+
});
|
|
877
|
+
return tools;
|
|
878
|
+
}
|
|
879
|
+
async function resolveAppTarget(apiBase, apiKey, target) {
|
|
880
|
+
const { handle, repo } = parseHandleRepo(target);
|
|
881
|
+
const apps = await fetchAppsWithCache({ apiBase, apiKey });
|
|
882
|
+
const normalizedRepo = normalizeRepoName(repo);
|
|
883
|
+
const match = apps.find((app) => {
|
|
884
|
+
if (app.handle && app.handle !== handle) {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
const candidates = [
|
|
888
|
+
app.repo,
|
|
889
|
+
app.gitRepo,
|
|
890
|
+
app.internalToolName,
|
|
891
|
+
app.name,
|
|
892
|
+
app.id
|
|
893
|
+
].map(normalizeRepoName);
|
|
894
|
+
return candidates.includes(normalizedRepo);
|
|
895
|
+
});
|
|
896
|
+
if (!match) {
|
|
897
|
+
throw new Error(`app not found for ${handle}/${repo}`);
|
|
898
|
+
}
|
|
899
|
+
return { app: match, handle, repo };
|
|
900
|
+
}
|
|
901
|
+
async function pollDeploymentLogs(params) {
|
|
902
|
+
const intervalMs = params.intervalMs ?? 5000;
|
|
903
|
+
const timeoutMs = params.timeoutMs ?? 4 * 60 * 1000;
|
|
904
|
+
const seen = new Set;
|
|
905
|
+
const startedAt = Date.now();
|
|
906
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
907
|
+
const logs = await getDeploymentLogs(params.baseUrl, params.apiKey, params.deploymentId);
|
|
908
|
+
const newLogs = logs.filter((log) => !seen.has(log.id));
|
|
909
|
+
for (const log of newLogs) {
|
|
910
|
+
seen.add(log.id);
|
|
911
|
+
}
|
|
912
|
+
for (const log of newLogs) {
|
|
913
|
+
console.log(`${params.prefix}${log.message}`);
|
|
914
|
+
}
|
|
915
|
+
const status = await getDeploymentStatus(params.baseUrl, params.apiKey, params.deploymentId);
|
|
916
|
+
if (status.status === "failed") {
|
|
917
|
+
console.log(`${params.prefix}deployment failed`);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (status.status === "running" || status.status === "deployed") {
|
|
921
|
+
console.log(`${params.prefix}deployment complete`);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
925
|
+
}
|
|
926
|
+
console.log(`${params.prefix}deployment still in progress`);
|
|
927
|
+
}
|
|
928
|
+
async function runTemplateStatus(_options, target) {
|
|
929
|
+
const config2 = await loadConfig();
|
|
930
|
+
const uiBase = resolveBaseUrl(config2);
|
|
931
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
932
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
933
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
934
|
+
const status = await getTemplateStatus(apiBase, apiKey, app.id);
|
|
935
|
+
console.log(JSON.stringify(status, null, 2));
|
|
936
|
+
}
|
|
937
|
+
async function runTemplateBranches(_options, target) {
|
|
938
|
+
const config2 = await loadConfig();
|
|
939
|
+
const uiBase = resolveBaseUrl(config2);
|
|
940
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
941
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
942
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
943
|
+
const branches = await listTemplateBranches(apiBase, apiKey, app.id);
|
|
944
|
+
console.log(JSON.stringify(branches, null, 2));
|
|
945
|
+
}
|
|
946
|
+
async function runTemplateUpdate(options, target) {
|
|
947
|
+
const config2 = await loadConfig();
|
|
948
|
+
const uiBase = resolveBaseUrl(config2);
|
|
949
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
950
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
951
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
952
|
+
const envRaw = typeof options.env === "string" ? options.env : typeof options.environment === "string" ? options.environment : undefined;
|
|
953
|
+
const environment = resolveTemplateEnvironment(envRaw);
|
|
954
|
+
const result = await deployLatestTemplate(apiBase, apiKey, app.id, {
|
|
955
|
+
environment
|
|
956
|
+
});
|
|
957
|
+
console.log(JSON.stringify(result, null, 2));
|
|
958
|
+
}
|
|
959
|
+
var printHelp = function() {
|
|
960
|
+
console.log("OpenPond CLI (API key only)");
|
|
961
|
+
console.log("");
|
|
962
|
+
console.log("Usage:");
|
|
963
|
+
console.log(" openpond login [--api-key <key>]");
|
|
964
|
+
console.log(" openpond tool list <handle>/<repo>");
|
|
965
|
+
console.log(" openpond tool run <handle>/<repo> <tool> [--body <json>] [--method <METHOD>]");
|
|
966
|
+
console.log(" openpond deploy watch <handle>/<repo> [--branch <branch>]");
|
|
967
|
+
console.log(" openpond template status <handle>/<repo>");
|
|
968
|
+
console.log(" openpond template branches <handle>/<repo>");
|
|
969
|
+
console.log(" openpond template update <handle>/<repo> [--env preview|production]");
|
|
970
|
+
console.log(" openpond repo create --name <name> [--path <dir>] [--template <owner/repo|url>] [--template-branch <branch>] [--env <json>] [--empty|--opentool] [--token] [--auto-schedule-migration <true|false>]");
|
|
971
|
+
console.log(" openpond repo push [--path <dir>] [--branch <branch>]");
|
|
972
|
+
console.log(" openpond apps list [--handle <handle>] [--refresh]");
|
|
973
|
+
console.log(" openpond apps tools");
|
|
974
|
+
console.log(" openpond apps deploy <handle>/<repo> [--env preview|production] [--watch]");
|
|
975
|
+
console.log(" openpond apps env get <handle>/<repo>");
|
|
976
|
+
console.log(" openpond apps env set <handle>/<repo> --env <json>");
|
|
977
|
+
console.log(" openpond apps performance [--app-id <id>]");
|
|
978
|
+
console.log(" openpond apps store events [--source <source>] [--status <csv>] [--symbol <symbol>] [--wallet-address <0x...>] [--since <ms|iso>] [--until <ms|iso>] [--limit <n>] [--cursor <cursor>] [--history <true|false>] [--params <json>]");
|
|
979
|
+
console.log(" openpond apps trade-facts [--app-id <id>]");
|
|
980
|
+
console.log(" openpond apps agent create --prompt <text> [--template-id <id>]");
|
|
981
|
+
console.log(" openpond apps tools execute <appId> <deploymentId> <tool> [--body <json>] [--method <METHOD>] [--headers <json>]");
|
|
982
|
+
console.log(" openpond apps positions tx [--method <GET|POST>] [--body <json>] [--params <json>]");
|
|
983
|
+
console.log(" openpond opentool <init|validate|build> [args]");
|
|
984
|
+
console.log("");
|
|
985
|
+
console.log("Env:");
|
|
986
|
+
console.log(" OPENPOND_API_KEY, OPENPOND_BASE_URL, OPENPOND_API_URL, OPENPOND_TOOL_URL");
|
|
987
|
+
};
|
|
988
|
+
async function runLogin(options) {
|
|
989
|
+
const config2 = await loadConfig();
|
|
990
|
+
const baseUrl = resolveBaseUrl(config2);
|
|
991
|
+
const rawApiKey = typeof options.apiKey === "string" ? options.apiKey : typeof options.key === "string" ? options.key : null;
|
|
992
|
+
const apiKey = rawApiKey ? rawApiKey.trim() : await promptForApiKey();
|
|
993
|
+
if (!apiKey) {
|
|
994
|
+
throw new Error("API key is required");
|
|
995
|
+
}
|
|
996
|
+
if (!apiKey.startsWith("opk_")) {
|
|
997
|
+
console.log("warning: API keys usually start with opk_.");
|
|
998
|
+
}
|
|
999
|
+
await saveGlobalConfig({ apiKey, baseUrl });
|
|
1000
|
+
console.log("saved api key to ~/.openpond/config.json");
|
|
1001
|
+
}
|
|
1002
|
+
async function runToolList(options, target) {
|
|
1003
|
+
const config2 = await loadConfig();
|
|
1004
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1005
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1006
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1007
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
1008
|
+
const branch = typeof options.branch === "string" ? String(options.branch) : undefined;
|
|
1009
|
+
const latest = await getLatestDeploymentForApp(apiBase, apiKey, app.id, { branch });
|
|
1010
|
+
if (!latest?.id) {
|
|
1011
|
+
console.log("no deployments found");
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const detail = await getDeploymentDetail(apiBase, apiKey, latest.id);
|
|
1015
|
+
const toolsRaw = (detail && Array.isArray(detail.toolsJson) ? detail.toolsJson : null) || (detail && typeof detail.metadataJson === "object" && detail.metadataJson ? detail.metadataJson.tools : null);
|
|
1016
|
+
const tools = Array.isArray(toolsRaw) ? toolsRaw : [];
|
|
1017
|
+
if (tools.length === 0) {
|
|
1018
|
+
console.log("no tools found");
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
for (const tool of tools) {
|
|
1022
|
+
const record = tool;
|
|
1023
|
+
const profile = record.profile || record.function;
|
|
1024
|
+
const name = record.name || profile?.name || "unknown";
|
|
1025
|
+
const description = record.description || profile?.description || "";
|
|
1026
|
+
console.log(description ? `${name} - ${description}` : name);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
async function runToolRun(options, target, toolName) {
|
|
1030
|
+
const config2 = await loadConfig();
|
|
1031
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1032
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1033
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1034
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
1035
|
+
const branch = typeof options.branch === "string" ? String(options.branch) : undefined;
|
|
1036
|
+
const latest = await getLatestDeploymentForApp(apiBase, apiKey, app.id, { branch });
|
|
1037
|
+
if (!latest?.id) {
|
|
1038
|
+
throw new Error("no deployments found");
|
|
1039
|
+
}
|
|
1040
|
+
let body = undefined;
|
|
1041
|
+
if (typeof options.body === "string") {
|
|
1042
|
+
try {
|
|
1043
|
+
body = JSON.parse(options.body);
|
|
1044
|
+
} catch {
|
|
1045
|
+
throw new Error("tool body must be valid JSON");
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
const method = typeof options.method === "string" ? String(options.method).toUpperCase() : "POST";
|
|
1049
|
+
const result = await executeHostedTool(uiBase, apiKey, {
|
|
1050
|
+
appId: app.id,
|
|
1051
|
+
deploymentId: latest.id,
|
|
1052
|
+
toolName,
|
|
1053
|
+
method,
|
|
1054
|
+
body,
|
|
1055
|
+
headers: apiKey ? { "openpond-api-key": apiKey } : undefined
|
|
1056
|
+
});
|
|
1057
|
+
if (!result.ok) {
|
|
1058
|
+
throw new Error(result.error || `tool failed (${result.status})`);
|
|
1059
|
+
}
|
|
1060
|
+
const output2 = result.data ?? { ok: true };
|
|
1061
|
+
console.log(JSON.stringify(output2, null, 2));
|
|
1062
|
+
}
|
|
1063
|
+
async function runDeployWatch(options, target) {
|
|
1064
|
+
const config2 = await loadConfig();
|
|
1065
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1066
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1067
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1068
|
+
const { app, handle, repo } = await resolveAppTarget(apiBase, apiKey, target);
|
|
1069
|
+
const branch = typeof options.branch === "string" ? String(options.branch) : undefined;
|
|
1070
|
+
const deploymentId = typeof options.deploymentId === "string" ? String(options.deploymentId) : undefined;
|
|
1071
|
+
const latest = deploymentId ? { id: deploymentId } : await getLatestDeploymentForApp(apiBase, apiKey, app.id, {
|
|
1072
|
+
branch
|
|
1073
|
+
});
|
|
1074
|
+
if (!latest?.id) {
|
|
1075
|
+
console.log("no deployments found");
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
await pollDeploymentLogs({
|
|
1079
|
+
baseUrl: apiBase,
|
|
1080
|
+
apiKey,
|
|
1081
|
+
deploymentId: latest.id,
|
|
1082
|
+
prefix: `[${handle}/${repo}] `,
|
|
1083
|
+
intervalMs: options.interval ? Number(options.interval) : undefined,
|
|
1084
|
+
timeoutMs: options.timeout ? Number(options.timeout) : undefined
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
async function runRepoCreate(options, nameParts) {
|
|
1088
|
+
const name = (typeof options.name === "string" ? options.name : null) || nameParts.join(" ");
|
|
1089
|
+
const trimmedName = name.trim();
|
|
1090
|
+
if (!trimmedName) {
|
|
1091
|
+
throw new Error("usage: repo create --name <name> [--path <dir>] [--template <owner/repo|url>] [--template-branch <branch>] [--empty|--opentool] [--token] [--auto-schedule-migration <true|false>]");
|
|
1092
|
+
}
|
|
1093
|
+
const config2 = await loadConfig();
|
|
1094
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1095
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1096
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1097
|
+
const templateInput = typeof options.template === "string" ? options.template.trim() : "";
|
|
1098
|
+
if (templateInput && (options.empty === "true" || options.opentool === "true")) {
|
|
1099
|
+
throw new Error("choose one: --template or --empty/--opentool");
|
|
1100
|
+
}
|
|
1101
|
+
if (options.empty === "true" && options.opentool === "true") {
|
|
1102
|
+
throw new Error("choose one: --empty or --opentool");
|
|
1103
|
+
}
|
|
1104
|
+
const description = typeof options.description === "string" ? options.description.trim() : undefined;
|
|
1105
|
+
const templateBranch = typeof options.templateBranch === "string" && options.templateBranch.trim().length > 0 ? options.templateBranch.trim() : undefined;
|
|
1106
|
+
const envVars = typeof options.env === "string" ? parseJsonOption(options.env, "env") : undefined;
|
|
1107
|
+
if (envVars) {
|
|
1108
|
+
if (typeof envVars !== "object" || Array.isArray(envVars)) {
|
|
1109
|
+
throw new Error("env must be a JSON object");
|
|
1110
|
+
}
|
|
1111
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
1112
|
+
if (typeof value !== "string") {
|
|
1113
|
+
throw new Error(`env value for ${key} must be a string`);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (templateInput) {
|
|
1118
|
+
if (options.deployOnPush !== undefined) {
|
|
1119
|
+
console.warn("deploy-on-push is not used for template create (auto deploys)");
|
|
1120
|
+
}
|
|
1121
|
+
const templateRepoUrl = normalizeTemplateRepoUrl(templateInput, uiBase);
|
|
1122
|
+
const response2 = await createHeadlessApps(apiBase, apiKey, [
|
|
1123
|
+
{
|
|
1124
|
+
name: trimmedName,
|
|
1125
|
+
...description ? { description } : {},
|
|
1126
|
+
templateRepoUrl,
|
|
1127
|
+
...templateBranch ? { templateBranch } : {},
|
|
1128
|
+
...envVars ? { envVars } : {}
|
|
1129
|
+
}
|
|
1130
|
+
]);
|
|
1131
|
+
const item = response2.items?.[0];
|
|
1132
|
+
if (!item || item.status !== "ok" || !item.appId) {
|
|
1133
|
+
throw new Error(item?.error || "Template create failed");
|
|
1134
|
+
}
|
|
1135
|
+
console.log(`app_id: ${item.appId}`);
|
|
1136
|
+
if (item.deploymentId) {
|
|
1137
|
+
console.log(`deployment_id: ${item.deploymentId}`);
|
|
1138
|
+
}
|
|
1139
|
+
if (item.conversationId) {
|
|
1140
|
+
console.log(`conversation_id: ${item.conversationId}`);
|
|
1141
|
+
}
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
const defaultPath = process.cwd();
|
|
1145
|
+
const rawPath = typeof options.path === "string" ? options.path : typeof options.dir === "string" ? options.dir : null;
|
|
1146
|
+
const targetPath = rawPath && rawPath.trim().length > 0 ? rawPath.trim() : input.isTTY ? await promptForPath(defaultPath) : defaultPath;
|
|
1147
|
+
const repoPath = path3.resolve(targetPath);
|
|
1148
|
+
if (existsSync(repoPath)) {
|
|
1149
|
+
const stats = await fs3.stat(repoPath);
|
|
1150
|
+
if (!stats.isDirectory()) {
|
|
1151
|
+
throw new Error(`path is not a directory: ${repoPath}`);
|
|
1152
|
+
}
|
|
1153
|
+
} else {
|
|
1154
|
+
await fs3.mkdir(repoPath, { recursive: true });
|
|
1155
|
+
}
|
|
1156
|
+
const entries = await fs3.readdir(repoPath);
|
|
1157
|
+
const nonGitEntries = entries.filter((entry) => entry !== ".git");
|
|
1158
|
+
const isEmpty = nonGitEntries.length === 0;
|
|
1159
|
+
const force = parseBooleanOption(options.yes) || parseBooleanOption(options.force);
|
|
1160
|
+
if (!isEmpty && !force) {
|
|
1161
|
+
const proceed = await promptConfirm(`Directory is not empty (${repoPath}). Continue?`, false);
|
|
1162
|
+
if (!proceed) {
|
|
1163
|
+
console.log("aborted");
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
const gitDir = path3.join(repoPath, ".git");
|
|
1168
|
+
const hasGit = existsSync(gitDir);
|
|
1169
|
+
if (!hasGit) {
|
|
1170
|
+
const proceed = force ? true : await promptConfirm("Initialize git repository here?", true);
|
|
1171
|
+
if (!proceed) {
|
|
1172
|
+
console.log("aborted");
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
const result = await runCommand("git", ["init"], { cwd: repoPath });
|
|
1176
|
+
if (result.code !== 0) {
|
|
1177
|
+
throw new Error(`git init failed: ${result.stderr.trim() || result.stdout.trim() || "unknown error"}`);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const originUrl = await getGitRemoteUrl(repoPath, "origin");
|
|
1181
|
+
if (originUrl && !force) {
|
|
1182
|
+
const proceed = await promptConfirm(`Remote "origin" already set (${originUrl}). Replace it?`, false);
|
|
1183
|
+
if (!proceed) {
|
|
1184
|
+
console.log("aborted");
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
const repoInit = options.opentool === "true" ? "opentool" : "empty";
|
|
1189
|
+
const deployOnPush = parseBooleanOption(options.deployOnPush);
|
|
1190
|
+
const autoScheduleMigrationOption = options.autoScheduleMigration;
|
|
1191
|
+
const autoScheduleMigrationSpecified = typeof autoScheduleMigrationOption === "string" || typeof autoScheduleMigrationOption === "boolean";
|
|
1192
|
+
const autoScheduleMigration = autoScheduleMigrationSpecified ? parseBooleanOption(autoScheduleMigrationOption) : undefined;
|
|
1193
|
+
const response = await createRepo(apiBase, apiKey, {
|
|
1194
|
+
name: trimmedName,
|
|
1195
|
+
...description ? { description } : {},
|
|
1196
|
+
...repoInit ? { repoInit } : {},
|
|
1197
|
+
...envVars ? { envVars } : {},
|
|
1198
|
+
...deployOnPush ? { deployOnPush: true } : {},
|
|
1199
|
+
...autoScheduleMigrationSpecified ? { autoScheduleMigration } : {}
|
|
1200
|
+
});
|
|
1201
|
+
const repoUrl = resolveRepoUrl(response);
|
|
1202
|
+
warnOnRepoHostMismatch(repoUrl);
|
|
1203
|
+
const useTokenRemote = parseBooleanOption(options.token) || parseBooleanOption(options.setRemoteToken);
|
|
1204
|
+
const tokenRemote = formatTokenizedRepoUrl(repoUrl, apiKey);
|
|
1205
|
+
const remoteUrl = useTokenRemote ? tokenRemote : repoUrl;
|
|
1206
|
+
const remoteArgs = originUrl ? ["remote", "set-url", "origin", remoteUrl] : ["remote", "add", "origin", remoteUrl];
|
|
1207
|
+
const remoteResult = await runCommand("git", remoteArgs, { cwd: repoPath });
|
|
1208
|
+
if (remoteResult.code !== 0) {
|
|
1209
|
+
throw new Error(`git remote failed: ${redactToken(remoteResult.stderr.trim() || remoteResult.stdout.trim() || "unknown error")}`);
|
|
1210
|
+
}
|
|
1211
|
+
const displayRemote = useTokenRemote ? formatTokenizedRepoUrlForPrint(repoUrl) : repoUrl;
|
|
1212
|
+
console.log(`app_id: ${response.appId}`);
|
|
1213
|
+
if (response.gitOwner && response.gitRepo) {
|
|
1214
|
+
console.log(`repo: ${response.gitOwner}/${response.gitRepo}`);
|
|
1215
|
+
}
|
|
1216
|
+
console.log(`remote: ${displayRemote}`);
|
|
1217
|
+
console.log("next: git add . && git commit -m \"init\"");
|
|
1218
|
+
const defaultBranch = response.defaultBranch || "master";
|
|
1219
|
+
console.log(`next: openpond repo push --path ${repoPath} --branch ${defaultBranch}`);
|
|
1220
|
+
if (!useTokenRemote) {
|
|
1221
|
+
console.log(`token-remote (non-interactive): git -C ${repoPath} remote set-url origin ${formatTokenizedRepoUrlForPrint(repoUrl)}`);
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
await fetchAppsWithCache({ apiBase, apiKey, forceRefresh: true });
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
console.warn("cache refresh failed", error);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
async function resolveGitBranch(repoPath) {
|
|
1230
|
+
const result = await runCommand("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
1231
|
+
cwd: repoPath
|
|
1232
|
+
});
|
|
1233
|
+
if (result.code !== 0)
|
|
1234
|
+
return null;
|
|
1235
|
+
const branch = result.stdout.trim();
|
|
1236
|
+
return branch.length > 0 ? branch : null;
|
|
1237
|
+
}
|
|
1238
|
+
async function runRepoPush(options) {
|
|
1239
|
+
const config2 = await loadConfig();
|
|
1240
|
+
const baseUrl = resolveBaseUrl(config2);
|
|
1241
|
+
const apiKey = await ensureApiKey(config2, baseUrl);
|
|
1242
|
+
const rawPath = typeof options.path === "string" ? options.path : typeof options.dir === "string" ? options.dir : null;
|
|
1243
|
+
const repoPath = path3.resolve(rawPath && rawPath.trim().length > 0 ? rawPath.trim() : ".");
|
|
1244
|
+
const gitDir = path3.join(repoPath, ".git");
|
|
1245
|
+
if (!existsSync(gitDir)) {
|
|
1246
|
+
throw new Error(`git repo not found at ${repoPath} (missing .git)`);
|
|
1247
|
+
}
|
|
1248
|
+
const originUrl = await getGitRemoteUrl(repoPath, "origin");
|
|
1249
|
+
if (!originUrl) {
|
|
1250
|
+
throw new Error("origin remote not set; run `openpond repo create` first");
|
|
1251
|
+
}
|
|
1252
|
+
warnOnRepoHostMismatch(originUrl);
|
|
1253
|
+
const branchOption = typeof options.branch === "string" ? options.branch.trim() : "";
|
|
1254
|
+
const resolvedBranch = branchOption || await resolveGitBranch(repoPath);
|
|
1255
|
+
if (!resolvedBranch) {
|
|
1256
|
+
throw new Error("unable to resolve git branch; pass --branch");
|
|
1257
|
+
}
|
|
1258
|
+
let tokenRemote;
|
|
1259
|
+
try {
|
|
1260
|
+
tokenRemote = formatTokenizedRepoUrl(originUrl, apiKey);
|
|
1261
|
+
} catch {
|
|
1262
|
+
throw new Error("origin remote must be https for tokenized pushes");
|
|
1263
|
+
}
|
|
1264
|
+
const keepTokenRemote = parseBooleanOption(options.keepTokenRemote) || parseBooleanOption(options.token) || parseBooleanOption(options.setRemoteToken);
|
|
1265
|
+
const alreadyTokenized = originUrl.includes("x-access-token:");
|
|
1266
|
+
const restoreUrl = !keepTokenRemote && !alreadyTokenized ? originUrl : null;
|
|
1267
|
+
const previousPrompt = process.env.GIT_TERMINAL_PROMPT;
|
|
1268
|
+
process.env.GIT_TERMINAL_PROMPT = "0";
|
|
1269
|
+
try {
|
|
1270
|
+
if (!alreadyTokenized) {
|
|
1271
|
+
const setResult = await runCommand("git", ["remote", "set-url", "origin", tokenRemote], { cwd: repoPath });
|
|
1272
|
+
if (setResult.code !== 0) {
|
|
1273
|
+
throw new Error(`git remote set-url failed: ${redactToken(setResult.stderr.trim() || setResult.stdout.trim() || "unknown error")}`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
const pushResult = await runCommand("git", ["push", "-u", "origin", resolvedBranch], { cwd: repoPath, inherit: true });
|
|
1277
|
+
if (pushResult.code !== 0) {
|
|
1278
|
+
throw new Error("git push failed");
|
|
1279
|
+
}
|
|
1280
|
+
} finally {
|
|
1281
|
+
if (restoreUrl) {
|
|
1282
|
+
await runCommand("git", ["remote", "set-url", "origin", restoreUrl], {
|
|
1283
|
+
cwd: repoPath
|
|
1284
|
+
}).catch(() => null);
|
|
1285
|
+
}
|
|
1286
|
+
if (previousPrompt === undefined) {
|
|
1287
|
+
delete process.env.GIT_TERMINAL_PROMPT;
|
|
1288
|
+
} else {
|
|
1289
|
+
process.env.GIT_TERMINAL_PROMPT = previousPrompt;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
async function runOpentool(rawArgs) {
|
|
1294
|
+
if (rawArgs.length === 0) {
|
|
1295
|
+
throw new Error("usage: opentool <init|validate|build> [args]");
|
|
1296
|
+
}
|
|
1297
|
+
const command = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
1298
|
+
const result = await runCommand(command, ["opentool", ...rawArgs], {
|
|
1299
|
+
inherit: true
|
|
1300
|
+
});
|
|
1301
|
+
if (result.code !== 0) {
|
|
1302
|
+
throw new Error("opentool command failed");
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
async function runAppsTools() {
|
|
1306
|
+
const config2 = await loadConfig();
|
|
1307
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1308
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1309
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1310
|
+
const tools = await fetchToolsWithCache({ apiBase, apiKey });
|
|
1311
|
+
console.log(JSON.stringify(tools, null, 2));
|
|
1312
|
+
}
|
|
1313
|
+
async function runAppsList(options) {
|
|
1314
|
+
const config2 = await loadConfig();
|
|
1315
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1316
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1317
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1318
|
+
const handle = typeof options.handle === "string" ? String(options.handle) : undefined;
|
|
1319
|
+
const normalizedHandle = handle ? normalizeRepoName(handle) : null;
|
|
1320
|
+
const forceRefresh = options.refresh !== undefined ? parseBooleanOption(options.refresh) : undefined;
|
|
1321
|
+
const apps = await fetchAppsWithCache({
|
|
1322
|
+
apiBase,
|
|
1323
|
+
apiKey,
|
|
1324
|
+
forceRefresh
|
|
1325
|
+
});
|
|
1326
|
+
const filtered = normalizedHandle ? apps.filter((app) => {
|
|
1327
|
+
const candidate = normalizeRepoName(app.handle || app.gitOwner);
|
|
1328
|
+
return candidate === normalizedHandle;
|
|
1329
|
+
}) : apps;
|
|
1330
|
+
if (filtered.length === 0) {
|
|
1331
|
+
console.log("no apps found");
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
for (const app of filtered) {
|
|
1335
|
+
const owner = app.handle || app.gitOwner || "unknown";
|
|
1336
|
+
const repo = app.repo || app.gitRepo || app.name || app.id;
|
|
1337
|
+
const status = app.latestDeployment?.status || "no-deploy";
|
|
1338
|
+
const branch = app.latestDeployment?.gitBranch || app.defaultBranch || "-";
|
|
1339
|
+
console.log(`${owner}/${repo} ${status} ${branch} ${app.id}`);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
async function runAppsPerformance(options) {
|
|
1343
|
+
const config2 = await loadConfig();
|
|
1344
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1345
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1346
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1347
|
+
const appId = typeof options.appId === "string" ? String(options.appId) : undefined;
|
|
1348
|
+
const performance = await getUserPerformance(apiBase, apiKey, { appId });
|
|
1349
|
+
console.log(JSON.stringify(performance, null, 2));
|
|
1350
|
+
}
|
|
1351
|
+
async function runAppsAgentCreate(options, contentParts) {
|
|
1352
|
+
const config2 = await loadConfig();
|
|
1353
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1354
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1355
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1356
|
+
const prompt = (typeof options.prompt === "string" ? options.prompt : null) || contentParts.join(" ");
|
|
1357
|
+
if (!prompt.trim()) {
|
|
1358
|
+
throw new Error("usage: apps agent create --prompt <text>");
|
|
1359
|
+
}
|
|
1360
|
+
const templateId = typeof options.templateId === "string" ? options.templateId : undefined;
|
|
1361
|
+
const templateRepoUrl = typeof options.templateRepoUrl === "string" ? options.templateRepoUrl : undefined;
|
|
1362
|
+
const templateBranch = typeof options.templateBranch === "string" ? options.templateBranch : undefined;
|
|
1363
|
+
const templateLocalPath = typeof options.templateLocalPath === "string" ? options.templateLocalPath : undefined;
|
|
1364
|
+
if (templateLocalPath && String(templateLocalPath).trim().length > 0) {
|
|
1365
|
+
throw new Error("templateLocalPath is not supported; use templateId or templateRepoUrl");
|
|
1366
|
+
}
|
|
1367
|
+
const envVars = typeof options.env === "string" ? parseJsonOption(options.env, "env") : undefined;
|
|
1368
|
+
if (envVars) {
|
|
1369
|
+
if (typeof envVars !== "object" || Array.isArray(envVars)) {
|
|
1370
|
+
throw new Error("env must be a JSON object");
|
|
1371
|
+
}
|
|
1372
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
1373
|
+
if (typeof value !== "string") {
|
|
1374
|
+
throw new Error(`env value for ${key} must be a string`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
const template = templateId || templateRepoUrl || templateBranch || envVars ? {
|
|
1379
|
+
templateId,
|
|
1380
|
+
templateRepoUrl,
|
|
1381
|
+
templateBranch,
|
|
1382
|
+
envVars
|
|
1383
|
+
} : undefined;
|
|
1384
|
+
const deployEnvironment = typeof options.deployEnvironment === "string" ? options.deployEnvironment === "preview" ? "preview" : "production" : undefined;
|
|
1385
|
+
const deployDisabled = options.deployDisabled !== undefined ? parseBooleanOption(options.deployDisabled) : undefined;
|
|
1386
|
+
const streamDeployLogs = options.streamDeployLogs !== undefined ? parseBooleanOption(options.streamDeployLogs) : true;
|
|
1387
|
+
const response = await createAgentFromPrompt(apiBase, apiKey, {
|
|
1388
|
+
prompt: prompt.trim(),
|
|
1389
|
+
...template ? { template } : {},
|
|
1390
|
+
...deployEnvironment ? { deployEnvironment } : {},
|
|
1391
|
+
...deployDisabled !== undefined ? { deployDisabled } : {},
|
|
1392
|
+
streamDeployLogs
|
|
1393
|
+
});
|
|
1394
|
+
if (!response.ok) {
|
|
1395
|
+
const text = await response.text().catch(() => "");
|
|
1396
|
+
throw new Error(`agent create failed: ${response.status} ${text}`);
|
|
1397
|
+
}
|
|
1398
|
+
let conversationId = null;
|
|
1399
|
+
let appId = null;
|
|
1400
|
+
let deploymentId = null;
|
|
1401
|
+
await consumeStream(response, {
|
|
1402
|
+
onConversationId: (id) => {
|
|
1403
|
+
conversationId = id;
|
|
1404
|
+
},
|
|
1405
|
+
onItems: (items) => {
|
|
1406
|
+
for (const item of items) {
|
|
1407
|
+
const line = formatStreamItem(item);
|
|
1408
|
+
if (line) {
|
|
1409
|
+
console.log(line);
|
|
1410
|
+
}
|
|
1411
|
+
const typed = item;
|
|
1412
|
+
if (!appId && typeof typed.appId === "string") {
|
|
1413
|
+
appId = typed.appId;
|
|
1414
|
+
}
|
|
1415
|
+
if (!deploymentId && typeof typed.deploymentId === "string") {
|
|
1416
|
+
deploymentId = typed.deploymentId;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1421
|
+
if (conversationId) {
|
|
1422
|
+
console.log(`conversation_id: ${conversationId}`);
|
|
1423
|
+
}
|
|
1424
|
+
if (appId) {
|
|
1425
|
+
console.log(`app_id: ${appId}`);
|
|
1426
|
+
}
|
|
1427
|
+
if (deploymentId) {
|
|
1428
|
+
console.log(`deployment_id: ${deploymentId}`);
|
|
1429
|
+
}
|
|
1430
|
+
try {
|
|
1431
|
+
await fetchAppsWithCache({ apiBase, apiKey, forceRefresh: true });
|
|
1432
|
+
await fetchToolsWithCache({ apiBase, apiKey, forceRefresh: true });
|
|
1433
|
+
} catch (error) {
|
|
1434
|
+
console.warn("cache refresh failed", error);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
async function runAppsToolsExecute(options, appId, deploymentId, toolName) {
|
|
1438
|
+
const config2 = await loadConfig();
|
|
1439
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1440
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1441
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1442
|
+
const methodRaw = typeof options.method === "string" ? String(options.method).toUpperCase() : undefined;
|
|
1443
|
+
const method = methodRaw && ["GET", "POST", "PUT", "DELETE"].includes(methodRaw) ? methodRaw : undefined;
|
|
1444
|
+
if (methodRaw && !method) {
|
|
1445
|
+
throw new Error("method must be GET, POST, PUT, or DELETE");
|
|
1446
|
+
}
|
|
1447
|
+
const body = typeof options.body === "string" ? parseJsonOption(String(options.body), "body") : undefined;
|
|
1448
|
+
const headers = typeof options.headers === "string" ? parseJsonOption(String(options.headers), "headers") : undefined;
|
|
1449
|
+
const scheduleId = typeof options.scheduleId === "string" ? String(options.scheduleId) : undefined;
|
|
1450
|
+
const notifyEmail = parseBooleanOption(options.notifyEmail);
|
|
1451
|
+
const result = await executeUserTool(apiBase, apiKey, {
|
|
1452
|
+
appId,
|
|
1453
|
+
deploymentId,
|
|
1454
|
+
toolName,
|
|
1455
|
+
scheduleId,
|
|
1456
|
+
method,
|
|
1457
|
+
body,
|
|
1458
|
+
headers,
|
|
1459
|
+
notifyEmail: notifyEmail || undefined
|
|
1460
|
+
});
|
|
1461
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1462
|
+
}
|
|
1463
|
+
async function runAppsEnvSet(options, target) {
|
|
1464
|
+
const rawEnv = typeof options.env === "string" ? options.env : typeof options.vars === "string" ? options.vars : typeof options.envVars === "string" ? options.envVars : null;
|
|
1465
|
+
if (!rawEnv) {
|
|
1466
|
+
throw new Error("usage: apps env set <handle>/<repo> --env <json>");
|
|
1467
|
+
}
|
|
1468
|
+
const parsed = parseJsonOption(rawEnv, "env");
|
|
1469
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1470
|
+
throw new Error("env must be a JSON object");
|
|
1471
|
+
}
|
|
1472
|
+
const envVars = {};
|
|
1473
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1474
|
+
if (typeof value !== "string") {
|
|
1475
|
+
throw new Error(`env value for ${key} must be a string`);
|
|
1476
|
+
}
|
|
1477
|
+
if (!key.startsWith("OPENTOOL_PUBLIC_")) {
|
|
1478
|
+
throw new Error("only OPENTOOL_PUBLIC_ env vars can be set");
|
|
1479
|
+
}
|
|
1480
|
+
envVars[key] = value;
|
|
1481
|
+
}
|
|
1482
|
+
const config2 = await loadConfig();
|
|
1483
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1484
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1485
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1486
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
1487
|
+
const result = await updateAppEnvironment(apiBase, apiKey, app.id, { envVars });
|
|
1488
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1489
|
+
}
|
|
1490
|
+
async function runAppsEnvGet(_options, target) {
|
|
1491
|
+
const config2 = await loadConfig();
|
|
1492
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1493
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1494
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1495
|
+
const { app } = await resolveAppTarget(apiBase, apiKey, target);
|
|
1496
|
+
const result = await getAppEnvironment(apiBase, apiKey, app.id);
|
|
1497
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1498
|
+
}
|
|
1499
|
+
async function runAppsDeploy(options, target) {
|
|
1500
|
+
const config2 = await loadConfig();
|
|
1501
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1502
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1503
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1504
|
+
const { app, handle, repo } = await resolveAppTarget(apiBase, apiKey, target);
|
|
1505
|
+
const envRaw = typeof options.env === "string" ? options.env : typeof options.environment === "string" ? options.environment : undefined;
|
|
1506
|
+
const environment = resolveTemplateEnvironment(envRaw);
|
|
1507
|
+
const result = await deployApp(apiBase, apiKey, app.id, { environment });
|
|
1508
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1509
|
+
const shouldWatch = parseBooleanOption(options.watch) || parseBooleanOption(options.wait) || parseBooleanOption(options.follow);
|
|
1510
|
+
if (!shouldWatch)
|
|
1511
|
+
return;
|
|
1512
|
+
await pollDeploymentLogs({
|
|
1513
|
+
baseUrl: apiBase,
|
|
1514
|
+
apiKey,
|
|
1515
|
+
deploymentId: result.deploymentId,
|
|
1516
|
+
prefix: `[${handle}/${repo}] `,
|
|
1517
|
+
intervalMs: options.interval ? Number(options.interval) : undefined,
|
|
1518
|
+
timeoutMs: options.timeout ? Number(options.timeout) : undefined
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
async function runAppsPositionsTx(options) {
|
|
1522
|
+
const config2 = await loadConfig();
|
|
1523
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1524
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1525
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1526
|
+
const methodRaw = typeof options.method === "string" ? String(options.method).toUpperCase() : "POST";
|
|
1527
|
+
const method = methodRaw === "GET" ? "GET" : "POST";
|
|
1528
|
+
if (methodRaw !== "GET" && methodRaw !== "POST") {
|
|
1529
|
+
throw new Error("method must be GET or POST");
|
|
1530
|
+
}
|
|
1531
|
+
let query;
|
|
1532
|
+
if (method === "GET" && typeof options.params === "string") {
|
|
1533
|
+
const parsed = parseJsonOption(String(options.params), "params");
|
|
1534
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1535
|
+
throw new Error("params must be a JSON object");
|
|
1536
|
+
}
|
|
1537
|
+
query = {};
|
|
1538
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1539
|
+
if (value === undefined)
|
|
1540
|
+
continue;
|
|
1541
|
+
query[key] = typeof value === "string" ? value : JSON.stringify(value);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
const body = method === "POST" && typeof options.body === "string" ? parseJsonOption(String(options.body), "body") : undefined;
|
|
1545
|
+
const result = await submitPositionsTx(apiBase, apiKey, {
|
|
1546
|
+
method,
|
|
1547
|
+
body,
|
|
1548
|
+
query
|
|
1549
|
+
});
|
|
1550
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1551
|
+
}
|
|
1552
|
+
var resolveStoreEventsParams = function(options) {
|
|
1553
|
+
let params = {};
|
|
1554
|
+
if (typeof options.params === "string") {
|
|
1555
|
+
const parsed = parseJsonOption(String(options.params), "params");
|
|
1556
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1557
|
+
throw new Error("params must be a JSON object");
|
|
1558
|
+
}
|
|
1559
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1560
|
+
if (value === undefined)
|
|
1561
|
+
continue;
|
|
1562
|
+
params[key] = typeof value === "string" ? value : JSON.stringify(value);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
const addParam = (key, value) => {
|
|
1566
|
+
if (value === undefined || value === "")
|
|
1567
|
+
return;
|
|
1568
|
+
params[key] = value;
|
|
1569
|
+
};
|
|
1570
|
+
addParam("source", typeof options.source === "string" ? options.source.trim() : undefined);
|
|
1571
|
+
addParam("walletAddress", typeof options.walletAddress === "string" ? options.walletAddress.trim() : undefined);
|
|
1572
|
+
addParam("symbol", typeof options.symbol === "string" ? options.symbol.trim() : undefined);
|
|
1573
|
+
addParam("cursor", typeof options.cursor === "string" ? options.cursor.trim() : undefined);
|
|
1574
|
+
addParam("status", typeof options.status === "string" ? options.status.trim() : undefined);
|
|
1575
|
+
addParam("since", parseTimeOption(options.since, "since"));
|
|
1576
|
+
addParam("until", parseTimeOption(options.until, "until"));
|
|
1577
|
+
if (typeof options.limit === "string" && options.limit.trim().length > 0) {
|
|
1578
|
+
const parsed = Number.parseInt(options.limit, 10);
|
|
1579
|
+
if (!Number.isFinite(parsed)) {
|
|
1580
|
+
throw new Error("limit must be a number");
|
|
1581
|
+
}
|
|
1582
|
+
addParam("limit", String(parsed));
|
|
1583
|
+
}
|
|
1584
|
+
if (options.history !== undefined) {
|
|
1585
|
+
addParam("history", parseBooleanOption(options.history) ? "true" : "false");
|
|
1586
|
+
}
|
|
1587
|
+
return Object.keys(params).length ? params : undefined;
|
|
1588
|
+
};
|
|
1589
|
+
async function runAppsStoreEvents(options) {
|
|
1590
|
+
const config2 = await loadConfig();
|
|
1591
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1592
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1593
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1594
|
+
const query = resolveStoreEventsParams(options);
|
|
1595
|
+
const result = await submitPositionsTx(apiBase, apiKey, {
|
|
1596
|
+
method: "GET",
|
|
1597
|
+
query
|
|
1598
|
+
});
|
|
1599
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1600
|
+
}
|
|
1601
|
+
async function runAppsTradeFacts(options) {
|
|
1602
|
+
const config2 = await loadConfig();
|
|
1603
|
+
const uiBase = resolveBaseUrl(config2);
|
|
1604
|
+
const apiKey = await ensureApiKey(config2, uiBase);
|
|
1605
|
+
const apiBase = resolvePublicApiBaseUrl();
|
|
1606
|
+
const appId = typeof options.appId === "string" ? options.appId : undefined;
|
|
1607
|
+
const performance = await getUserPerformance(apiBase, apiKey, { appId });
|
|
1608
|
+
if (performance && typeof performance === "object" && "trades" in performance && Array.isArray(performance.trades)) {
|
|
1609
|
+
console.log(JSON.stringify(performance.trades, null, 2));
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
console.log(JSON.stringify(performance, null, 2));
|
|
1613
|
+
}
|
|
1614
|
+
async function main() {
|
|
1615
|
+
const { command, options, rest } = parseArgs(process.argv.slice(2));
|
|
1616
|
+
if (!command || command === "help") {
|
|
1617
|
+
printHelp();
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (command === "login") {
|
|
1621
|
+
await runLogin(options);
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
if (command === "tool") {
|
|
1625
|
+
const subcommand = rest[0];
|
|
1626
|
+
if (subcommand === "list") {
|
|
1627
|
+
const target = rest[1];
|
|
1628
|
+
if (!target) {
|
|
1629
|
+
throw new Error("usage: tool list <handle>/<repo>");
|
|
1630
|
+
}
|
|
1631
|
+
await runToolList(options, target);
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (subcommand === "run") {
|
|
1635
|
+
const target = rest[1];
|
|
1636
|
+
const toolName = rest[2];
|
|
1637
|
+
if (!target || !toolName) {
|
|
1638
|
+
throw new Error("usage: tool run <handle>/<repo> <tool> [--body <json>]");
|
|
1639
|
+
}
|
|
1640
|
+
await runToolRun(options, target, toolName);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
throw new Error("usage: tool <list|run> <handle>/<repo> [args]");
|
|
1644
|
+
}
|
|
1645
|
+
if (command === "deploy") {
|
|
1646
|
+
const subcommand = rest[0] || "watch";
|
|
1647
|
+
if (subcommand !== "watch") {
|
|
1648
|
+
throw new Error("usage: deploy watch <handle>/<repo> [--branch <branch>]");
|
|
1649
|
+
}
|
|
1650
|
+
const target = rest[1];
|
|
1651
|
+
if (!target) {
|
|
1652
|
+
throw new Error("usage: deploy watch <handle>/<repo> [--branch <branch>]");
|
|
1653
|
+
}
|
|
1654
|
+
await runDeployWatch(options, target);
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
if (command === "template") {
|
|
1658
|
+
const subcommand = rest[0] || "status";
|
|
1659
|
+
const target = rest[1];
|
|
1660
|
+
if (!target) {
|
|
1661
|
+
throw new Error("usage: template <status|branches|update> <handle>/<repo> [--env preview|production]");
|
|
1662
|
+
}
|
|
1663
|
+
if (subcommand === "status") {
|
|
1664
|
+
await runTemplateStatus(options, target);
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
if (subcommand === "branches") {
|
|
1668
|
+
await runTemplateBranches(options, target);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
if (subcommand === "update") {
|
|
1672
|
+
await runTemplateUpdate(options, target);
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
throw new Error("usage: template <status|branches|update> <handle>/<repo> [--env preview|production]");
|
|
1676
|
+
}
|
|
1677
|
+
if (command === "repo") {
|
|
1678
|
+
const subcommand = rest[0] || "create";
|
|
1679
|
+
if (subcommand === "create") {
|
|
1680
|
+
await runRepoCreate(options, rest.slice(1));
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
if (subcommand === "push") {
|
|
1684
|
+
await runRepoPush(options);
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
throw new Error("usage: repo <create|push> [--name <name>] [--path <dir>] [--branch <branch>]");
|
|
1688
|
+
}
|
|
1689
|
+
if (command === "apps") {
|
|
1690
|
+
const subcommand = rest[0];
|
|
1691
|
+
if (subcommand === "list") {
|
|
1692
|
+
await runAppsList(options);
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (subcommand === "tools") {
|
|
1696
|
+
if (rest[1] === "execute") {
|
|
1697
|
+
const appId = rest[2];
|
|
1698
|
+
const deploymentId = rest[3];
|
|
1699
|
+
const toolName = rest[4];
|
|
1700
|
+
if (!appId || !deploymentId || !toolName) {
|
|
1701
|
+
throw new Error("usage: apps tools execute <appId> <deploymentId> <tool> [--body <json>]");
|
|
1702
|
+
}
|
|
1703
|
+
await runAppsToolsExecute(options, appId, deploymentId, toolName);
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
await runAppsTools();
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
if (subcommand === "deploy") {
|
|
1710
|
+
const target = rest[1];
|
|
1711
|
+
if (!target) {
|
|
1712
|
+
throw new Error("usage: apps deploy <handle>/<repo> [--env preview|production] [--watch]");
|
|
1713
|
+
}
|
|
1714
|
+
await runAppsDeploy(options, target);
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
if (subcommand === "env" && rest[1] === "get") {
|
|
1718
|
+
const target = rest[2];
|
|
1719
|
+
if (!target) {
|
|
1720
|
+
throw new Error("usage: apps env get <handle>/<repo>");
|
|
1721
|
+
}
|
|
1722
|
+
await runAppsEnvGet(options, target);
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
if (subcommand === "env" && rest[1] === "set") {
|
|
1726
|
+
const target = rest[2];
|
|
1727
|
+
if (!target) {
|
|
1728
|
+
throw new Error("usage: apps env set <handle>/<repo> --env <json>");
|
|
1729
|
+
}
|
|
1730
|
+
await runAppsEnvSet(options, target);
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
if (subcommand === "performance") {
|
|
1734
|
+
await runAppsPerformance(options);
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
if (subcommand === "store" && rest[1] === "events") {
|
|
1738
|
+
await runAppsStoreEvents(options);
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
if (subcommand === "trade-facts") {
|
|
1742
|
+
await runAppsTradeFacts(options);
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
if (subcommand === "agent" && rest[1] === "create") {
|
|
1746
|
+
await runAppsAgentCreate(options, rest.slice(2));
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
if (subcommand === "positions" && rest[1] === "tx") {
|
|
1750
|
+
await runAppsPositionsTx(options);
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
throw new Error("usage: apps <list|tools|deploy|env get|env set|performance|store events|trade-facts|agent create|positions tx> [args]");
|
|
1754
|
+
}
|
|
1755
|
+
if (command === "opentool") {
|
|
1756
|
+
await runOpentool(process.argv.slice(3));
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
printHelp();
|
|
1760
|
+
process.exit(1);
|
|
1761
|
+
}
|
|
1762
|
+
var UI_API_KEY_URL = "https://openpond.ai/settings/api-keys";
|
|
1763
|
+
main().catch((error) => {
|
|
1764
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1765
|
+
console.error(message);
|
|
1766
|
+
process.exit(1);
|
|
1767
|
+
});
|