@tpmjs/cli 0.1.2 → 0.1.4
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/commands/agent/chat.js +68 -2
- package/dist/commands/agent/chat.js.map +1 -1
- package/dist/commands/agent/create.js +68 -2
- package/dist/commands/agent/create.js.map +1 -1
- package/dist/commands/agent/delete.js +68 -2
- package/dist/commands/agent/delete.js.map +1 -1
- package/dist/commands/agent/list.js +68 -2
- package/dist/commands/agent/list.js.map +1 -1
- package/dist/commands/agent/update.js +68 -2
- package/dist/commands/agent/update.js.map +1 -1
- package/dist/commands/auth/login.js +99 -17
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.js +16 -0
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/status.js +68 -2
- package/dist/commands/auth/status.js.map +1 -1
- package/dist/commands/auth/whoami.js +68 -2
- package/dist/commands/auth/whoami.js.map +1 -1
- package/dist/commands/collection/add.js +68 -2
- package/dist/commands/collection/add.js.map +1 -1
- package/dist/commands/collection/create.js +68 -2
- package/dist/commands/collection/create.js.map +1 -1
- package/dist/commands/collection/delete.js +68 -2
- package/dist/commands/collection/delete.js.map +1 -1
- package/dist/commands/collection/import.js +68 -2
- package/dist/commands/collection/import.js.map +1 -1
- package/dist/commands/collection/list.js +68 -2
- package/dist/commands/collection/list.js.map +1 -1
- package/dist/commands/collection/remove.js +68 -2
- package/dist/commands/collection/remove.js.map +1 -1
- package/dist/commands/collection/update.js +68 -2
- package/dist/commands/collection/update.js.map +1 -1
- package/dist/commands/doctor.js +68 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/mcp/config.js +16 -0
- package/dist/commands/mcp/config.js.map +1 -1
- package/dist/commands/mcp/serve.js +68 -2
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/playground.js +68 -2
- package/dist/commands/playground.js.map +1 -1
- package/dist/commands/publish/check.js +68 -2
- package/dist/commands/publish/check.js.map +1 -1
- package/dist/commands/publish/preview.js +16 -0
- package/dist/commands/publish/preview.js.map +1 -1
- package/dist/commands/scenario/generate.d.ts +19 -0
- package/dist/commands/scenario/generate.js +633 -0
- package/dist/commands/scenario/generate.js.map +1 -0
- package/dist/commands/scenario/info.d.ts +18 -0
- package/dist/commands/scenario/info.js +636 -0
- package/dist/commands/scenario/info.js.map +1 -0
- package/dist/commands/scenario/list.d.ts +20 -0
- package/dist/commands/scenario/list.js +652 -0
- package/dist/commands/scenario/list.js.map +1 -0
- package/dist/commands/scenario/run.d.ts +18 -0
- package/dist/commands/scenario/run.js +663 -0
- package/dist/commands/scenario/run.js.map +1 -0
- package/dist/commands/scenario/test.d.ts +17 -0
- package/dist/commands/scenario/test.js +620 -0
- package/dist/commands/scenario/test.js.map +1 -0
- package/dist/commands/tool/execute.js +68 -2
- package/dist/commands/tool/execute.js.map +1 -1
- package/dist/commands/tool/info.js +68 -2
- package/dist/commands/tool/info.js.map +1 -1
- package/dist/commands/tool/init.js +16 -0
- package/dist/commands/tool/init.js.map +1 -1
- package/dist/commands/tool/search.js +68 -2
- package/dist/commands/tool/search.js.map +1 -1
- package/dist/commands/tool/trending.js +68 -2
- package/dist/commands/tool/trending.js.map +1 -1
- package/dist/commands/tool/validate.js +68 -2
- package/dist/commands/tool/validate.js.map +1 -1
- package/dist/commands/update.js +16 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/index.d.ts +80 -0
- package/dist/index.js +68 -2
- package/dist/index.js.map +1 -1
- package/oclif.manifest.json +290 -1
- package/package.json +4 -1
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
+
import Conf from 'conf';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import Table from 'cli-table3';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import pc from 'picocolors';
|
|
9
|
+
|
|
10
|
+
// src/commands/scenario/run.ts
|
|
11
|
+
var CONFIG_DIR = path.join(os.homedir(), ".tpmjs");
|
|
12
|
+
var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
|
|
13
|
+
path.join(CONFIG_DIR, "history");
|
|
14
|
+
function ensureConfigDir() {
|
|
15
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
16
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
var configStore = new Conf({
|
|
20
|
+
projectName: "tpmjs",
|
|
21
|
+
cwd: CONFIG_DIR,
|
|
22
|
+
configName: "config",
|
|
23
|
+
defaults: {
|
|
24
|
+
apiUrl: "https://tpmjs.com/api",
|
|
25
|
+
defaultOutput: "human",
|
|
26
|
+
verbose: false,
|
|
27
|
+
analytics: false
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
function getConfigValue(key) {
|
|
31
|
+
return configStore.get(key);
|
|
32
|
+
}
|
|
33
|
+
function loadCredentials() {
|
|
34
|
+
ensureConfigDir();
|
|
35
|
+
if (!fs.existsSync(CREDENTIALS_FILE)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const content = fs.readFileSync(CREDENTIALS_FILE, "utf-8");
|
|
40
|
+
return JSON.parse(content);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function getApiKey() {
|
|
46
|
+
if (process.env.TPMJS_API_KEY) {
|
|
47
|
+
return process.env.TPMJS_API_KEY;
|
|
48
|
+
}
|
|
49
|
+
const creds = loadCredentials();
|
|
50
|
+
if (creds?.apiKey) {
|
|
51
|
+
return creds.apiKey;
|
|
52
|
+
}
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
function getApiUrl() {
|
|
56
|
+
return process.env.TPMJS_API_URL ?? getConfigValue("apiUrl") ?? "https://tpmjs.com/api";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/lib/api-client.ts
|
|
60
|
+
var TpmClient = class {
|
|
61
|
+
baseUrl;
|
|
62
|
+
apiKey;
|
|
63
|
+
timeout;
|
|
64
|
+
constructor(options = {}) {
|
|
65
|
+
this.baseUrl = options.baseUrl ?? getApiUrl();
|
|
66
|
+
this.apiKey = options.apiKey ?? getApiKey();
|
|
67
|
+
this.timeout = options.timeout ?? 3e4;
|
|
68
|
+
}
|
|
69
|
+
async request(endpoint, options = {}) {
|
|
70
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
71
|
+
const headers = {
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
...options.headers
|
|
74
|
+
};
|
|
75
|
+
if (this.apiKey) {
|
|
76
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
77
|
+
}
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(url, {
|
|
82
|
+
...options,
|
|
83
|
+
headers,
|
|
84
|
+
signal: controller.signal
|
|
85
|
+
});
|
|
86
|
+
const data = await response.json();
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new ApiError(
|
|
89
|
+
data.message || data.error || `HTTP ${response.status}`,
|
|
90
|
+
response.status,
|
|
91
|
+
data
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return data;
|
|
95
|
+
} finally {
|
|
96
|
+
clearTimeout(timeoutId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Health check
|
|
100
|
+
async health() {
|
|
101
|
+
return this.request("/health");
|
|
102
|
+
}
|
|
103
|
+
// Stats
|
|
104
|
+
async getStats() {
|
|
105
|
+
return this.request("/stats");
|
|
106
|
+
}
|
|
107
|
+
// Tools
|
|
108
|
+
async searchTools(options = {}) {
|
|
109
|
+
const params = new URLSearchParams();
|
|
110
|
+
if (options.query) params.set("q", options.query);
|
|
111
|
+
if (options.category) params.set("category", options.category);
|
|
112
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
113
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
114
|
+
const queryString = params.toString();
|
|
115
|
+
const endpoint = queryString ? `/tools?${queryString}` : "/tools";
|
|
116
|
+
return this.request(endpoint);
|
|
117
|
+
}
|
|
118
|
+
async getTool(packageName, toolName) {
|
|
119
|
+
return this.request(
|
|
120
|
+
`/tools/${encodeURIComponent(packageName)}/${encodeURIComponent(toolName)}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
async getToolBySlug(slug) {
|
|
124
|
+
const searchResult = await this.searchTools({ query: slug, limit: 1 });
|
|
125
|
+
if (searchResult.data && searchResult.data.length > 0) {
|
|
126
|
+
const tool = searchResult.data.find((t) => t.slug === slug) || searchResult.data[0];
|
|
127
|
+
return { success: true, data: tool };
|
|
128
|
+
}
|
|
129
|
+
return { success: false, error: "Tool not found" };
|
|
130
|
+
}
|
|
131
|
+
async getTrendingTools(options = {}) {
|
|
132
|
+
const params = new URLSearchParams();
|
|
133
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
134
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
135
|
+
const queryString = params.toString();
|
|
136
|
+
const endpoint = queryString ? `/tools/trending?${queryString}` : "/tools/trending";
|
|
137
|
+
return this.request(endpoint);
|
|
138
|
+
}
|
|
139
|
+
async validateTpmjsField(field) {
|
|
140
|
+
return this.request("/tools/validate", {
|
|
141
|
+
method: "POST",
|
|
142
|
+
body: JSON.stringify(field)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async executeTool(slug, params) {
|
|
146
|
+
return this.request(`/tools/${encodeURIComponent(slug)}/execute`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
body: JSON.stringify(params)
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async *executeToolStream(slug, params) {
|
|
152
|
+
const url = `${this.baseUrl}/tools/${encodeURIComponent(slug)}/execute`;
|
|
153
|
+
const headers = {
|
|
154
|
+
"Content-Type": "application/json",
|
|
155
|
+
Accept: "text/event-stream"
|
|
156
|
+
};
|
|
157
|
+
if (this.apiKey) {
|
|
158
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
159
|
+
}
|
|
160
|
+
const response = await fetch(url, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers,
|
|
163
|
+
body: JSON.stringify({ ...params, stream: true })
|
|
164
|
+
});
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
const errorText = await response.text();
|
|
167
|
+
throw new ApiError(errorText || `HTTP ${response.status}`, response.status);
|
|
168
|
+
}
|
|
169
|
+
if (!response.body) {
|
|
170
|
+
throw new ApiError("No response body", 0);
|
|
171
|
+
}
|
|
172
|
+
const reader = response.body.getReader();
|
|
173
|
+
const decoder = new TextDecoder();
|
|
174
|
+
let buffer = "";
|
|
175
|
+
try {
|
|
176
|
+
while (true) {
|
|
177
|
+
const { done, value } = await reader.read();
|
|
178
|
+
if (done) {
|
|
179
|
+
yield { type: "done", data: "" };
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
buffer += decoder.decode(value, { stream: true });
|
|
183
|
+
const lines = buffer.split("\n");
|
|
184
|
+
buffer = lines.pop() ?? "";
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
if (line.startsWith("data: ")) {
|
|
187
|
+
const data = line.slice(6);
|
|
188
|
+
if (data === "[DONE]") {
|
|
189
|
+
yield { type: "done", data: "" };
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(data);
|
|
194
|
+
yield { type: parsed.type || "text", data: parsed.content || parsed.data || data };
|
|
195
|
+
} catch {
|
|
196
|
+
yield { type: "text", data };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} finally {
|
|
202
|
+
reader.releaseLock();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Agents
|
|
206
|
+
async listAgents(options = {}) {
|
|
207
|
+
const params = new URLSearchParams();
|
|
208
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
209
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
210
|
+
const queryString = params.toString();
|
|
211
|
+
const endpoint = queryString ? `/agents?${queryString}` : "/agents";
|
|
212
|
+
return this.request(endpoint);
|
|
213
|
+
}
|
|
214
|
+
async getAgent(id) {
|
|
215
|
+
return this.request(`/agents/${id}`);
|
|
216
|
+
}
|
|
217
|
+
async createAgent(input) {
|
|
218
|
+
return this.request("/agents", {
|
|
219
|
+
method: "POST",
|
|
220
|
+
body: JSON.stringify(input)
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async updateAgent(id, input) {
|
|
224
|
+
return this.request(`/agents/${id}`, {
|
|
225
|
+
method: "PATCH",
|
|
226
|
+
body: JSON.stringify(input)
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async deleteAgent(id) {
|
|
230
|
+
return this.request(`/agents/${id}`, {
|
|
231
|
+
method: "DELETE"
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// Collections
|
|
235
|
+
async listCollections(options = {}) {
|
|
236
|
+
const params = new URLSearchParams();
|
|
237
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
238
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
239
|
+
const queryString = params.toString();
|
|
240
|
+
const endpoint = queryString ? `/collections?${queryString}` : "/collections";
|
|
241
|
+
return this.request(endpoint);
|
|
242
|
+
}
|
|
243
|
+
async getCollection(id) {
|
|
244
|
+
return this.request(`/collections/${id}`);
|
|
245
|
+
}
|
|
246
|
+
async createCollection(input) {
|
|
247
|
+
return this.request("/collections", {
|
|
248
|
+
method: "POST",
|
|
249
|
+
body: JSON.stringify(input)
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async updateCollection(id, input) {
|
|
253
|
+
return this.request(`/collections/${id}`, {
|
|
254
|
+
method: "PATCH",
|
|
255
|
+
body: JSON.stringify(input)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async deleteCollection(id) {
|
|
259
|
+
return this.request(`/collections/${id}`, {
|
|
260
|
+
method: "DELETE"
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async addToolsToCollection(id, toolIds) {
|
|
264
|
+
for (const toolId of toolIds) {
|
|
265
|
+
await this.request(`/collections/${id}/tools/${toolId}`, {
|
|
266
|
+
method: "POST"
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return { success: true };
|
|
270
|
+
}
|
|
271
|
+
async removeToolFromCollection(id, toolId) {
|
|
272
|
+
return this.request(`/collections/${id}/tools/${toolId}`, {
|
|
273
|
+
method: "DELETE"
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// User
|
|
277
|
+
async whoami() {
|
|
278
|
+
return this.request("/user/profile");
|
|
279
|
+
}
|
|
280
|
+
async listApiKeys() {
|
|
281
|
+
return this.request("/user/tpmjs-api-keys");
|
|
282
|
+
}
|
|
283
|
+
// Scenarios
|
|
284
|
+
async listScenarios(options = {}) {
|
|
285
|
+
const params = new URLSearchParams();
|
|
286
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
287
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
288
|
+
if (options.collectionId) params.set("collectionId", options.collectionId);
|
|
289
|
+
if (options.tags) params.set("tags", options.tags);
|
|
290
|
+
if (options.sortBy) params.set("sortBy", options.sortBy);
|
|
291
|
+
const queryString = params.toString();
|
|
292
|
+
const endpoint = queryString ? `/scenarios?${queryString}` : "/scenarios";
|
|
293
|
+
return this.request(endpoint);
|
|
294
|
+
}
|
|
295
|
+
async listCollectionScenarios(collectionId, options = {}) {
|
|
296
|
+
const params = new URLSearchParams();
|
|
297
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
298
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
299
|
+
const queryString = params.toString();
|
|
300
|
+
const endpoint = queryString ? `/collections/${collectionId}/scenarios?${queryString}` : `/collections/${collectionId}/scenarios`;
|
|
301
|
+
return this.request(endpoint);
|
|
302
|
+
}
|
|
303
|
+
async getScenario(id) {
|
|
304
|
+
return this.request(`/scenarios/${id}`);
|
|
305
|
+
}
|
|
306
|
+
async createScenario(input) {
|
|
307
|
+
return this.request("/scenarios", {
|
|
308
|
+
method: "POST",
|
|
309
|
+
body: JSON.stringify(input)
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
async generateScenarios(collectionId, input = {}) {
|
|
313
|
+
return this.request(`/collections/${collectionId}/scenarios/generate`, {
|
|
314
|
+
method: "POST",
|
|
315
|
+
body: JSON.stringify(input)
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
async runScenario(scenarioId) {
|
|
319
|
+
return this.request(`/scenarios/${scenarioId}/run`, {
|
|
320
|
+
method: "POST"
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async getScenarioRuns(scenarioId, options = {}) {
|
|
324
|
+
const params = new URLSearchParams();
|
|
325
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
326
|
+
if (options.offset) params.set("offset", String(options.offset));
|
|
327
|
+
const queryString = params.toString();
|
|
328
|
+
const endpoint = queryString ? `/scenarios/${scenarioId}/runs?${queryString}` : `/scenarios/${scenarioId}/runs`;
|
|
329
|
+
return this.request(endpoint);
|
|
330
|
+
}
|
|
331
|
+
// Check if authenticated
|
|
332
|
+
isAuthenticated() {
|
|
333
|
+
return !!this.apiKey;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
var ApiError = class extends Error {
|
|
337
|
+
constructor(message, statusCode, data) {
|
|
338
|
+
super(message);
|
|
339
|
+
this.statusCode = statusCode;
|
|
340
|
+
this.data = data;
|
|
341
|
+
this.name = "ApiError";
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
var clientInstance = null;
|
|
345
|
+
function getClient(options) {
|
|
346
|
+
if (!clientInstance || options) {
|
|
347
|
+
clientInstance = new TpmClient(options);
|
|
348
|
+
}
|
|
349
|
+
return clientInstance;
|
|
350
|
+
}
|
|
351
|
+
var OutputFormatter = class {
|
|
352
|
+
options;
|
|
353
|
+
constructor(options = {}) {
|
|
354
|
+
this.options = options;
|
|
355
|
+
}
|
|
356
|
+
// Output as JSON
|
|
357
|
+
json(data) {
|
|
358
|
+
console.log(JSON.stringify(data, null, 2));
|
|
359
|
+
}
|
|
360
|
+
// Output a table
|
|
361
|
+
table(data, columns) {
|
|
362
|
+
if (this.options.json) {
|
|
363
|
+
this.json(data);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const table = new Table({
|
|
367
|
+
head: columns.map((col) => pc.bold(col.header)),
|
|
368
|
+
colWidths: columns.map((col) => col.width ?? null),
|
|
369
|
+
style: {
|
|
370
|
+
head: [],
|
|
371
|
+
border: []
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
for (const row of data) {
|
|
375
|
+
table.push(columns.map((col) => String(row[col.key] ?? "")));
|
|
376
|
+
}
|
|
377
|
+
console.log(table.toString());
|
|
378
|
+
}
|
|
379
|
+
// Success message
|
|
380
|
+
success(message) {
|
|
381
|
+
if (this.options.json) return;
|
|
382
|
+
console.log(pc.green("\u2713"), message);
|
|
383
|
+
}
|
|
384
|
+
// Error message
|
|
385
|
+
error(message, details) {
|
|
386
|
+
if (this.options.json) {
|
|
387
|
+
this.json({ error: message, details });
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
console.error(pc.red("\u2717"), message);
|
|
391
|
+
if (details && this.options.verbose) {
|
|
392
|
+
console.error(pc.dim(details));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Warning message
|
|
396
|
+
warning(message) {
|
|
397
|
+
if (this.options.json) return;
|
|
398
|
+
console.log(pc.yellow("\u26A0"), message);
|
|
399
|
+
}
|
|
400
|
+
// Info message
|
|
401
|
+
info(message) {
|
|
402
|
+
if (this.options.json) return;
|
|
403
|
+
console.log(pc.blue("\u2139"), message);
|
|
404
|
+
}
|
|
405
|
+
// Debug message (only in verbose mode)
|
|
406
|
+
debug(message) {
|
|
407
|
+
if (this.options.json) return;
|
|
408
|
+
if (this.options.verbose) {
|
|
409
|
+
console.log(pc.dim(`[debug] ${message}`));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// Plain text output
|
|
413
|
+
text(message) {
|
|
414
|
+
if (this.options.json) return;
|
|
415
|
+
console.log(message);
|
|
416
|
+
}
|
|
417
|
+
// Heading
|
|
418
|
+
heading(text) {
|
|
419
|
+
if (this.options.json) return;
|
|
420
|
+
console.log();
|
|
421
|
+
console.log(pc.bold(pc.underline(text)));
|
|
422
|
+
console.log();
|
|
423
|
+
}
|
|
424
|
+
// Subheading
|
|
425
|
+
subheading(text) {
|
|
426
|
+
if (this.options.json) return;
|
|
427
|
+
console.log(pc.bold(text));
|
|
428
|
+
}
|
|
429
|
+
// Key-value pair
|
|
430
|
+
keyValue(key, value) {
|
|
431
|
+
if (this.options.json) return;
|
|
432
|
+
console.log(`${pc.dim(key + ":")} ${value ?? pc.dim("(not set)")}`);
|
|
433
|
+
}
|
|
434
|
+
// List item
|
|
435
|
+
listItem(text, indent = 0) {
|
|
436
|
+
if (this.options.json) return;
|
|
437
|
+
const prefix = " ".repeat(indent) + "\u2022";
|
|
438
|
+
console.log(`${prefix} ${text}`);
|
|
439
|
+
}
|
|
440
|
+
// Spinner
|
|
441
|
+
spinner(message) {
|
|
442
|
+
return ora({
|
|
443
|
+
text: message,
|
|
444
|
+
isSilent: this.options.json
|
|
445
|
+
}).start();
|
|
446
|
+
}
|
|
447
|
+
// Blank line
|
|
448
|
+
newLine() {
|
|
449
|
+
if (this.options.json) return;
|
|
450
|
+
console.log();
|
|
451
|
+
}
|
|
452
|
+
// Horizontal rule
|
|
453
|
+
hr() {
|
|
454
|
+
if (this.options.json) return;
|
|
455
|
+
console.log(pc.dim("\u2500".repeat(50)));
|
|
456
|
+
}
|
|
457
|
+
// Alias for hr
|
|
458
|
+
divider() {
|
|
459
|
+
this.hr();
|
|
460
|
+
}
|
|
461
|
+
// Code block
|
|
462
|
+
code(text, language) {
|
|
463
|
+
if (this.options.json) {
|
|
464
|
+
this.json({ code: text, language });
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
console.log(pc.dim("```" + (language ?? "")));
|
|
468
|
+
console.log(text);
|
|
469
|
+
console.log(pc.dim("```"));
|
|
470
|
+
}
|
|
471
|
+
// Highlight text
|
|
472
|
+
highlight(text) {
|
|
473
|
+
return pc.cyan(text);
|
|
474
|
+
}
|
|
475
|
+
// Dim text
|
|
476
|
+
dim(text) {
|
|
477
|
+
return pc.dim(text);
|
|
478
|
+
}
|
|
479
|
+
// Bold text
|
|
480
|
+
bold(text) {
|
|
481
|
+
return pc.bold(text);
|
|
482
|
+
}
|
|
483
|
+
// Link (just returns text in terminal)
|
|
484
|
+
link(text, url) {
|
|
485
|
+
return `\x1B]8;;${url}\x07${pc.underline(pc.blue(text))}\x1B]8;;\x07`;
|
|
486
|
+
}
|
|
487
|
+
// Color helpers
|
|
488
|
+
green(text) {
|
|
489
|
+
return pc.green(text);
|
|
490
|
+
}
|
|
491
|
+
red(text) {
|
|
492
|
+
return pc.red(text);
|
|
493
|
+
}
|
|
494
|
+
yellow(text) {
|
|
495
|
+
return pc.yellow(text);
|
|
496
|
+
}
|
|
497
|
+
blue(text) {
|
|
498
|
+
return pc.blue(text);
|
|
499
|
+
}
|
|
500
|
+
cyan(text) {
|
|
501
|
+
return pc.cyan(text);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
function createOutput(flags) {
|
|
505
|
+
return new OutputFormatter({
|
|
506
|
+
json: flags.json,
|
|
507
|
+
verbose: flags.verbose
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/commands/scenario/run.ts
|
|
512
|
+
var ScenarioRun = class _ScenarioRun extends Command {
|
|
513
|
+
static description = "Run all scenarios for a collection";
|
|
514
|
+
static examples = [
|
|
515
|
+
"<%= config.bin %> <%= command.id %> my-collection",
|
|
516
|
+
"<%= config.bin %> <%= command.id %> my-collection --json",
|
|
517
|
+
"<%= config.bin %> <%= command.id %> my-collection --verbose"
|
|
518
|
+
];
|
|
519
|
+
static args = {
|
|
520
|
+
collection: Args.string({
|
|
521
|
+
description: "Collection ID or slug",
|
|
522
|
+
required: true
|
|
523
|
+
})
|
|
524
|
+
};
|
|
525
|
+
static flags = {
|
|
526
|
+
json: Flags.boolean({
|
|
527
|
+
description: "Output in JSON format",
|
|
528
|
+
default: false
|
|
529
|
+
}),
|
|
530
|
+
verbose: Flags.boolean({
|
|
531
|
+
char: "v",
|
|
532
|
+
description: "Show verbose output",
|
|
533
|
+
default: false
|
|
534
|
+
}),
|
|
535
|
+
limit: Flags.integer({
|
|
536
|
+
char: "l",
|
|
537
|
+
description: "Maximum number of scenarios to run",
|
|
538
|
+
default: 50
|
|
539
|
+
})
|
|
540
|
+
};
|
|
541
|
+
async run() {
|
|
542
|
+
const { args, flags } = await this.parse(_ScenarioRun);
|
|
543
|
+
const output = createOutput(flags);
|
|
544
|
+
const client = getClient();
|
|
545
|
+
if (!client.isAuthenticated()) {
|
|
546
|
+
output.error("Not authenticated. Run `tpm auth login` first.");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const collectionsSpinner = output.spinner("Finding collection...");
|
|
550
|
+
let collectionId;
|
|
551
|
+
try {
|
|
552
|
+
const collections = await client.listCollections({ limit: 100 });
|
|
553
|
+
const collection = collections.data.find(
|
|
554
|
+
(c) => c.id === args.collection || c.slug === args.collection
|
|
555
|
+
);
|
|
556
|
+
if (!collection) {
|
|
557
|
+
collectionsSpinner.fail("Collection not found");
|
|
558
|
+
output.error(`No collection found with ID or slug: ${args.collection}`);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
collectionId = collection.id;
|
|
562
|
+
collectionsSpinner.stop();
|
|
563
|
+
output.text(output.bold(`Running scenarios for: ${collection.name}
|
|
564
|
+
`));
|
|
565
|
+
} catch (error) {
|
|
566
|
+
collectionsSpinner.fail("Failed to find collection");
|
|
567
|
+
output.error(error instanceof Error ? error.message : "Unknown error");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const scenariosSpinner = output.spinner("Fetching scenarios...");
|
|
571
|
+
let scenarios;
|
|
572
|
+
try {
|
|
573
|
+
const response = await client.listCollectionScenarios(collectionId, { limit: flags.limit });
|
|
574
|
+
scenarios = response.data?.scenarios || [];
|
|
575
|
+
if (scenarios.length === 0) {
|
|
576
|
+
scenariosSpinner.fail("No scenarios found");
|
|
577
|
+
output.info("This collection has no scenarios. Generate some with:");
|
|
578
|
+
output.text(` tpm scenario generate ${args.collection}`);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
scenariosSpinner.stop();
|
|
582
|
+
output.info(`Found ${scenarios.length} scenario(s) to run
|
|
583
|
+
`);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
scenariosSpinner.fail("Failed to fetch scenarios");
|
|
586
|
+
output.error(error instanceof Error ? error.message : "Unknown error");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
const results = [];
|
|
590
|
+
let passed = 0;
|
|
591
|
+
let failed = 0;
|
|
592
|
+
let errors = 0;
|
|
593
|
+
for (const scenario of scenarios) {
|
|
594
|
+
const name = scenario.name || scenario.prompt.slice(0, 40) + "...";
|
|
595
|
+
const runSpinner = output.spinner(`Running: ${name}`);
|
|
596
|
+
try {
|
|
597
|
+
const result = await client.runScenario(scenario.id);
|
|
598
|
+
const runData = result.data;
|
|
599
|
+
if (runData.success) {
|
|
600
|
+
passed++;
|
|
601
|
+
runSpinner.succeed(`${output.green("\u2713")} ${name}`);
|
|
602
|
+
} else if (runData.status === "error") {
|
|
603
|
+
errors++;
|
|
604
|
+
runSpinner.fail(`${output.red("\u2717")} ${name} (error)`);
|
|
605
|
+
} else {
|
|
606
|
+
failed++;
|
|
607
|
+
runSpinner.fail(`${output.red("\u2717")} ${name}`);
|
|
608
|
+
}
|
|
609
|
+
if (flags.verbose && runData.evaluator?.reason) {
|
|
610
|
+
output.text(output.dim(` \u2192 ${runData.evaluator.reason}`));
|
|
611
|
+
}
|
|
612
|
+
results.push({
|
|
613
|
+
name,
|
|
614
|
+
status: runData.status,
|
|
615
|
+
verdict: runData.evaluator?.verdict ?? null,
|
|
616
|
+
reason: runData.evaluator?.reason ?? null,
|
|
617
|
+
timeMs: runData.usage?.executionTimeMs ?? null
|
|
618
|
+
});
|
|
619
|
+
} catch (error) {
|
|
620
|
+
errors++;
|
|
621
|
+
runSpinner.fail(`${output.red("\u2717")} ${name} (error)`);
|
|
622
|
+
results.push({
|
|
623
|
+
name,
|
|
624
|
+
status: "error",
|
|
625
|
+
verdict: null,
|
|
626
|
+
reason: error instanceof Error ? error.message : "Unknown error",
|
|
627
|
+
timeMs: null
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
output.newLine();
|
|
632
|
+
output.text(output.bold("\u2500".repeat(50)));
|
|
633
|
+
output.text(output.bold("Summary"));
|
|
634
|
+
output.text(` ${output.green("Passed:")} ${passed}`);
|
|
635
|
+
output.text(` ${output.red("Failed:")} ${failed}`);
|
|
636
|
+
if (errors > 0) {
|
|
637
|
+
output.text(` ${output.yellow("Errors:")} ${errors}`);
|
|
638
|
+
}
|
|
639
|
+
output.text(` ${output.dim("Total:")} ${scenarios.length}`);
|
|
640
|
+
const passRate = scenarios.length > 0 ? passed / scenarios.length * 100 : 0;
|
|
641
|
+
output.newLine();
|
|
642
|
+
output.text(`Pass rate: ${passRate.toFixed(1)}%`);
|
|
643
|
+
if (flags.json) {
|
|
644
|
+
output.newLine();
|
|
645
|
+
output.json({
|
|
646
|
+
collection: args.collection,
|
|
647
|
+
total: scenarios.length,
|
|
648
|
+
passed,
|
|
649
|
+
failed,
|
|
650
|
+
errors,
|
|
651
|
+
passRate: passRate.toFixed(1),
|
|
652
|
+
results
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
if (failed > 0 || errors > 0) {
|
|
656
|
+
this.exit(1);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
export { ScenarioRun as default };
|
|
662
|
+
//# sourceMappingURL=run.js.map
|
|
663
|
+
//# sourceMappingURL=run.js.map
|