@runtypelabs/cli 0.1.0 → 0.1.1
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/index.d.ts +2 -0
- package/dist/index.js +1628 -0
- package/dist/index.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,1628 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var import_commander8 = require("commander");
|
|
28
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
29
|
+
var import_dotenv = require("dotenv");
|
|
30
|
+
var import_shared = require("@travrse/shared");
|
|
31
|
+
|
|
32
|
+
// src/commands/auth.ts
|
|
33
|
+
var import_commander = require("commander");
|
|
34
|
+
var import_chalk = __toESM(require("chalk"));
|
|
35
|
+
var import_ora = __toESM(require("ora"));
|
|
36
|
+
|
|
37
|
+
// src/auth/oauth-manager.ts
|
|
38
|
+
var import_open = __toESM(require("open"));
|
|
39
|
+
var import_crypto = __toESM(require("crypto"));
|
|
40
|
+
|
|
41
|
+
// src/auth/callback-server.ts
|
|
42
|
+
var import_express = __toESM(require("express"));
|
|
43
|
+
var CallbackServer = class {
|
|
44
|
+
server = null;
|
|
45
|
+
app;
|
|
46
|
+
constructor() {
|
|
47
|
+
this.app = (0, import_express.default)();
|
|
48
|
+
}
|
|
49
|
+
async start(port = 8765) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
this.app.get("/callback", (req, res) => {
|
|
52
|
+
const { code, error } = req.query;
|
|
53
|
+
if (error) {
|
|
54
|
+
res.send(this.errorHTML(error));
|
|
55
|
+
reject(new Error(error));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!code) {
|
|
59
|
+
res.send(this.errorHTML("No authorization code received"));
|
|
60
|
+
reject(new Error("No authorization code received"));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
res.send(this.successHTML());
|
|
64
|
+
resolve(code);
|
|
65
|
+
setTimeout(() => this.stop(), 1e3);
|
|
66
|
+
});
|
|
67
|
+
this.app.get("/health", (_req, res) => {
|
|
68
|
+
res.json({ status: "ok" });
|
|
69
|
+
});
|
|
70
|
+
this.server = this.app.listen(port, () => {
|
|
71
|
+
console.log(`Callback server listening on port ${port}`);
|
|
72
|
+
});
|
|
73
|
+
this.server.on("error", (error) => {
|
|
74
|
+
if (error.code === "EADDRINUSE") {
|
|
75
|
+
console.log(`Port ${port} is in use, trying ${port + 1}...`);
|
|
76
|
+
this.server = null;
|
|
77
|
+
this.start(port + 1).then(resolve).catch(reject);
|
|
78
|
+
} else {
|
|
79
|
+
reject(error);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
stop() {
|
|
85
|
+
if (this.server) {
|
|
86
|
+
this.server.close();
|
|
87
|
+
this.server = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
successHTML() {
|
|
91
|
+
return `
|
|
92
|
+
<!DOCTYPE html>
|
|
93
|
+
<html>
|
|
94
|
+
<head>
|
|
95
|
+
<title>Authentication Successful</title>
|
|
96
|
+
<style>
|
|
97
|
+
* {
|
|
98
|
+
margin: 0;
|
|
99
|
+
padding: 0;
|
|
100
|
+
box-sizing: border-box;
|
|
101
|
+
}
|
|
102
|
+
body {
|
|
103
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
height: 100vh;
|
|
108
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
109
|
+
}
|
|
110
|
+
.container {
|
|
111
|
+
background: white;
|
|
112
|
+
padding: 3rem;
|
|
113
|
+
border-radius: 12px;
|
|
114
|
+
text-align: center;
|
|
115
|
+
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
116
|
+
max-width: 400px;
|
|
117
|
+
animation: slideUp 0.3s ease-out;
|
|
118
|
+
}
|
|
119
|
+
@keyframes slideUp {
|
|
120
|
+
from {
|
|
121
|
+
opacity: 0;
|
|
122
|
+
transform: translateY(20px);
|
|
123
|
+
}
|
|
124
|
+
to {
|
|
125
|
+
opacity: 1;
|
|
126
|
+
transform: translateY(0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
.checkmark {
|
|
130
|
+
color: #10b981;
|
|
131
|
+
font-size: 64px;
|
|
132
|
+
margin-bottom: 1rem;
|
|
133
|
+
animation: scaleIn 0.3s ease-out 0.1s both;
|
|
134
|
+
}
|
|
135
|
+
@keyframes scaleIn {
|
|
136
|
+
from {
|
|
137
|
+
transform: scale(0);
|
|
138
|
+
}
|
|
139
|
+
to {
|
|
140
|
+
transform: scale(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
h1 {
|
|
144
|
+
color: #1f2937;
|
|
145
|
+
margin-bottom: 0.5rem;
|
|
146
|
+
font-size: 1.5rem;
|
|
147
|
+
}
|
|
148
|
+
p {
|
|
149
|
+
color: #6b7280;
|
|
150
|
+
font-size: 1rem;
|
|
151
|
+
}
|
|
152
|
+
.progress {
|
|
153
|
+
margin-top: 1.5rem;
|
|
154
|
+
color: #9ca3af;
|
|
155
|
+
font-size: 0.875rem;
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
158
|
+
</head>
|
|
159
|
+
<body>
|
|
160
|
+
<div class="container">
|
|
161
|
+
<div class="checkmark">\u2713</div>
|
|
162
|
+
<h1>Authentication Successful!</h1>
|
|
163
|
+
<p>You can close this window and return to your terminal.</p>
|
|
164
|
+
<p class="progress">Closing automatically in 3 seconds...</p>
|
|
165
|
+
<script>
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
window.close();
|
|
168
|
+
// Fallback if window.close() doesn't work
|
|
169
|
+
document.body.innerHTML = '<div class="container"><h1>You can now close this window</h1></div>';
|
|
170
|
+
}, 3000);
|
|
171
|
+
</script>
|
|
172
|
+
</div>
|
|
173
|
+
</body>
|
|
174
|
+
</html>
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
errorHTML(error) {
|
|
178
|
+
return `
|
|
179
|
+
<!DOCTYPE html>
|
|
180
|
+
<html>
|
|
181
|
+
<head>
|
|
182
|
+
<title>Authentication Failed</title>
|
|
183
|
+
<style>
|
|
184
|
+
* {
|
|
185
|
+
margin: 0;
|
|
186
|
+
padding: 0;
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
}
|
|
189
|
+
body {
|
|
190
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
191
|
+
display: flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
justify-content: center;
|
|
194
|
+
height: 100vh;
|
|
195
|
+
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
196
|
+
}
|
|
197
|
+
.container {
|
|
198
|
+
background: white;
|
|
199
|
+
padding: 3rem;
|
|
200
|
+
border-radius: 12px;
|
|
201
|
+
text-align: center;
|
|
202
|
+
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
203
|
+
max-width: 400px;
|
|
204
|
+
}
|
|
205
|
+
.error-icon {
|
|
206
|
+
color: #ef4444;
|
|
207
|
+
font-size: 64px;
|
|
208
|
+
margin-bottom: 1rem;
|
|
209
|
+
}
|
|
210
|
+
h1 {
|
|
211
|
+
color: #1f2937;
|
|
212
|
+
margin-bottom: 0.5rem;
|
|
213
|
+
font-size: 1.5rem;
|
|
214
|
+
}
|
|
215
|
+
p {
|
|
216
|
+
color: #6b7280;
|
|
217
|
+
font-size: 1rem;
|
|
218
|
+
margin-bottom: 1rem;
|
|
219
|
+
}
|
|
220
|
+
.error-details {
|
|
221
|
+
background: #fef2f2;
|
|
222
|
+
color: #991b1b;
|
|
223
|
+
padding: 0.75rem;
|
|
224
|
+
border-radius: 6px;
|
|
225
|
+
font-size: 0.875rem;
|
|
226
|
+
font-family: monospace;
|
|
227
|
+
}
|
|
228
|
+
</style>
|
|
229
|
+
</head>
|
|
230
|
+
<body>
|
|
231
|
+
<div class="container">
|
|
232
|
+
<div class="error-icon">\u2715</div>
|
|
233
|
+
<h1>Authentication Failed</h1>
|
|
234
|
+
<p>There was an error during authentication.</p>
|
|
235
|
+
<div class="error-details">${error}</div>
|
|
236
|
+
</div>
|
|
237
|
+
</body>
|
|
238
|
+
</html>
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/auth/oauth-manager.ts
|
|
244
|
+
var OAuthManager = class {
|
|
245
|
+
clerkPublishableKey;
|
|
246
|
+
dashboardUrl;
|
|
247
|
+
constructor(config3) {
|
|
248
|
+
this.clerkPublishableKey = config3.clerkPublishableKey;
|
|
249
|
+
this.dashboardUrl = config3.dashboardUrl;
|
|
250
|
+
}
|
|
251
|
+
async authenticate(mode) {
|
|
252
|
+
const state = import_crypto.default.randomBytes(32).toString("base64url");
|
|
253
|
+
const codeVerifier = import_crypto.default.randomBytes(32).toString("base64url");
|
|
254
|
+
const codeChallenge = import_crypto.default.createHash("sha256").update(codeVerifier).digest("base64url");
|
|
255
|
+
const callbackServer = new CallbackServer();
|
|
256
|
+
const port = await this.findAvailablePort();
|
|
257
|
+
const codePromise = callbackServer.start(port);
|
|
258
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
259
|
+
const authUrl = new URL(`${this.dashboardUrl.replace(/\/$/, "")}/${mode}`);
|
|
260
|
+
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
261
|
+
authUrl.searchParams.set("cli", "true");
|
|
262
|
+
authUrl.searchParams.set("state", state);
|
|
263
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
264
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
265
|
+
authUrl.searchParams.set("publishable_key", this.clerkPublishableKey);
|
|
266
|
+
console.log("\u{1F310} Opening browser for authentication...");
|
|
267
|
+
console.log(`If the browser doesn't open, visit: ${authUrl}`);
|
|
268
|
+
try {
|
|
269
|
+
await (0, import_open.default)(authUrl.toString());
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.log("Failed to open browser automatically. Please visit the URL above.");
|
|
272
|
+
}
|
|
273
|
+
console.log("\u23F3 Waiting for authentication...");
|
|
274
|
+
const code = await codePromise;
|
|
275
|
+
return this.exchangeCodeForSession(code, codeVerifier, redirectUri);
|
|
276
|
+
}
|
|
277
|
+
async findAvailablePort() {
|
|
278
|
+
const defaultPort = 8765;
|
|
279
|
+
return defaultPort;
|
|
280
|
+
}
|
|
281
|
+
async exchangeCodeForSession(code, codeVerifier, redirectUri) {
|
|
282
|
+
void codeVerifier;
|
|
283
|
+
void redirectUri;
|
|
284
|
+
return code;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/config/env.ts
|
|
289
|
+
function getApiUrl() {
|
|
290
|
+
return process.env.RUNTYPE_API_URL || "https://api.runtype.com";
|
|
291
|
+
}
|
|
292
|
+
function getApiVersion() {
|
|
293
|
+
return process.env.RUNTYPE_API_VERSION || "v1";
|
|
294
|
+
}
|
|
295
|
+
function getDashboardUrl() {
|
|
296
|
+
return process.env.RUNTYPE_DASHBOARD_URL || "https://dashboard.runtype.com";
|
|
297
|
+
}
|
|
298
|
+
function getClerkPublishableKey() {
|
|
299
|
+
return process.env.CLERK_PUBLISHABLE_KEY || "pk_live_default_key";
|
|
300
|
+
}
|
|
301
|
+
function getDefaultModel() {
|
|
302
|
+
return process.env.RUNTYPE_DEFAULT_MODEL || "meta/llama3.1-8b-instruct-free";
|
|
303
|
+
}
|
|
304
|
+
function getDefaultTemperature() {
|
|
305
|
+
const temp = process.env.RUNTYPE_DEFAULT_TEMPERATURE;
|
|
306
|
+
return temp ? parseFloat(temp) : 0.7;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/auth/api-key-manager.ts
|
|
310
|
+
var isDashboardAuthResponse = (value) => {
|
|
311
|
+
if (!value || typeof value !== "object") {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
const record = value;
|
|
315
|
+
return typeof record.apiKey === "string" && typeof record.userId === "string";
|
|
316
|
+
};
|
|
317
|
+
var isAuthMeResponse = (value) => {
|
|
318
|
+
if (!value || typeof value !== "object") {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
const record = value;
|
|
322
|
+
return typeof record.user_id === "string";
|
|
323
|
+
};
|
|
324
|
+
var ApiKeyManager = class {
|
|
325
|
+
async exchangeSessionForApiKey(authCode, apiUrl) {
|
|
326
|
+
void apiUrl;
|
|
327
|
+
const dashboardUrl = process.env.RUNTYPE_DASHBOARD_URL || "http://localhost:3001";
|
|
328
|
+
const authResponse = await fetch(`${dashboardUrl}/api/cli/auth?code=${authCode}`, {
|
|
329
|
+
method: "GET",
|
|
330
|
+
headers: {
|
|
331
|
+
"Content-Type": "application/json"
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
if (!authResponse.ok) {
|
|
335
|
+
const error = await authResponse.text();
|
|
336
|
+
throw new Error(`Authentication failed: ${error}`);
|
|
337
|
+
}
|
|
338
|
+
const authData = await authResponse.json();
|
|
339
|
+
if (!isDashboardAuthResponse(authData)) {
|
|
340
|
+
throw new Error("Invalid authentication response format");
|
|
341
|
+
}
|
|
342
|
+
const result = {
|
|
343
|
+
key: authData.apiKey,
|
|
344
|
+
userId: authData.userId
|
|
345
|
+
};
|
|
346
|
+
if (authData.orgId) {
|
|
347
|
+
result.orgId = authData.orgId;
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
async validateApiKey(apiKey, apiUrl) {
|
|
352
|
+
const baseUrl = apiUrl || getApiUrl();
|
|
353
|
+
try {
|
|
354
|
+
const response = await fetch(`${baseUrl}/${getApiVersion()}/auth/me`, {
|
|
355
|
+
headers: {
|
|
356
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
357
|
+
"Content-Type": "application/json"
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
return response.ok;
|
|
361
|
+
} catch {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async getCurrentUser(apiKey, apiUrl) {
|
|
366
|
+
const baseUrl = apiUrl || getApiUrl();
|
|
367
|
+
const response = await fetch(`${baseUrl}/auth/me`, {
|
|
368
|
+
headers: {
|
|
369
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
370
|
+
"Content-Type": "application/json"
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
if (!response.ok) {
|
|
374
|
+
throw new Error("Failed to get user information");
|
|
375
|
+
}
|
|
376
|
+
const data = await response.json();
|
|
377
|
+
if (!isAuthMeResponse(data)) {
|
|
378
|
+
throw new Error("Invalid user response format");
|
|
379
|
+
}
|
|
380
|
+
return data;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/auth/credential-store.ts
|
|
385
|
+
var import_conf = __toESM(require("conf"));
|
|
386
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
387
|
+
var import_os = __toESM(require("os"));
|
|
388
|
+
var import_path = __toESM(require("path"));
|
|
389
|
+
var CredentialStore = class {
|
|
390
|
+
config;
|
|
391
|
+
encryptionKey;
|
|
392
|
+
constructor() {
|
|
393
|
+
this.encryptionKey = this.getMachineKey();
|
|
394
|
+
this.config = new import_conf.default({
|
|
395
|
+
projectName: "runtype-cli",
|
|
396
|
+
projectSuffix: "",
|
|
397
|
+
configName: "credentials",
|
|
398
|
+
encryptionKey: this.encryptionKey,
|
|
399
|
+
cwd: import_path.default.join(import_os.default.homedir(), ".runtype")
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
getMachineKey() {
|
|
403
|
+
const hostname = import_os.default.hostname();
|
|
404
|
+
const username = import_os.default.userInfo().username;
|
|
405
|
+
return import_crypto2.default.createHash("sha256").update(`runtype-cli-${hostname}-${username}`).digest("hex").substring(0, 32);
|
|
406
|
+
}
|
|
407
|
+
async saveCredentials(credentials) {
|
|
408
|
+
const configData = {
|
|
409
|
+
apiKey: this.encrypt(credentials.apiKey),
|
|
410
|
+
userId: credentials.userId,
|
|
411
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
412
|
+
lastUsed: (/* @__PURE__ */ new Date()).toISOString()
|
|
413
|
+
};
|
|
414
|
+
if (credentials.orgId !== void 0) {
|
|
415
|
+
configData.orgId = credentials.orgId;
|
|
416
|
+
}
|
|
417
|
+
if (credentials.apiUrl !== void 0) {
|
|
418
|
+
configData.apiUrl = credentials.apiUrl;
|
|
419
|
+
}
|
|
420
|
+
this.config.set(configData);
|
|
421
|
+
}
|
|
422
|
+
async getApiKey() {
|
|
423
|
+
const encrypted = this.config.get("apiKey");
|
|
424
|
+
if (!encrypted) return null;
|
|
425
|
+
this.config.set("lastUsed", (/* @__PURE__ */ new Date()).toISOString());
|
|
426
|
+
return this.decrypt(encrypted);
|
|
427
|
+
}
|
|
428
|
+
async getCredentials() {
|
|
429
|
+
const stored = this.config.store;
|
|
430
|
+
if (!stored.apiKey) return null;
|
|
431
|
+
return {
|
|
432
|
+
...stored,
|
|
433
|
+
apiKey: this.decrypt(stored.apiKey)
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async clearCredentials() {
|
|
437
|
+
this.config.clear();
|
|
438
|
+
}
|
|
439
|
+
async hasCredentials() {
|
|
440
|
+
return !!this.config.get("apiKey");
|
|
441
|
+
}
|
|
442
|
+
encrypt(text) {
|
|
443
|
+
const algorithm = "aes-256-cbc";
|
|
444
|
+
const key = Buffer.from(this.encryptionKey);
|
|
445
|
+
const iv = import_crypto2.default.randomBytes(16);
|
|
446
|
+
const cipher = import_crypto2.default.createCipheriv(algorithm, key, iv);
|
|
447
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
448
|
+
encrypted += cipher.final("hex");
|
|
449
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
450
|
+
}
|
|
451
|
+
decrypt(text) {
|
|
452
|
+
const algorithm = "aes-256-cbc";
|
|
453
|
+
const key = Buffer.from(this.encryptionKey);
|
|
454
|
+
const [ivHex, encrypted] = text.split(":");
|
|
455
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
456
|
+
const decipher = import_crypto2.default.createDecipheriv(algorithm, key, iv);
|
|
457
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
458
|
+
decrypted += decipher.final("utf8");
|
|
459
|
+
return decrypted;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// src/commands/auth.ts
|
|
464
|
+
var authCommand = new import_commander.Command("auth").description("Manage authentication");
|
|
465
|
+
authCommand.command("signup").description("Create a new Runtype account").option("--api-url <url>", "Custom API URL").action(async (options) => {
|
|
466
|
+
const spinner = (0, import_ora.default)("Initializing authentication...").start();
|
|
467
|
+
try {
|
|
468
|
+
const oauth = new OAuthManager({
|
|
469
|
+
clerkPublishableKey: getClerkPublishableKey(),
|
|
470
|
+
dashboardUrl: options.dashboardUrl || getDashboardUrl()
|
|
471
|
+
});
|
|
472
|
+
spinner.text = "Waiting for browser authentication...";
|
|
473
|
+
const sessionToken = await oauth.authenticate("sign-up");
|
|
474
|
+
spinner.text = "Creating API key...";
|
|
475
|
+
const apiKeyManager = new ApiKeyManager();
|
|
476
|
+
const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
|
|
477
|
+
sessionToken,
|
|
478
|
+
options.apiUrl || getApiUrl()
|
|
479
|
+
);
|
|
480
|
+
spinner.text = "Storing credentials securely...";
|
|
481
|
+
const store = new CredentialStore();
|
|
482
|
+
await store.saveCredentials({
|
|
483
|
+
apiKey: key,
|
|
484
|
+
userId,
|
|
485
|
+
orgId,
|
|
486
|
+
apiUrl: options.apiUrl || getApiUrl()
|
|
487
|
+
});
|
|
488
|
+
spinner.succeed("Authentication successful!");
|
|
489
|
+
console.log(import_chalk.default.green("\n\u2728 Welcome to Runtype!"));
|
|
490
|
+
console.log(import_chalk.default.gray("Your API key has been saved securely."));
|
|
491
|
+
console.log(import_chalk.default.cyan("\nGet started with:"));
|
|
492
|
+
console.log(" runtype flows list");
|
|
493
|
+
console.log(" runtype records create --help");
|
|
494
|
+
console.log(" runtype talk");
|
|
495
|
+
} catch (error) {
|
|
496
|
+
spinner.fail("Authentication failed");
|
|
497
|
+
console.error(import_chalk.default.red(error.message));
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
authCommand.command("login").description("Login to existing account").option("--api-url <url>", "Custom API URL").option("--api-key <key>", "Use API key directly").action(async (options) => {
|
|
502
|
+
const store = new CredentialStore();
|
|
503
|
+
if (options.apiKey) {
|
|
504
|
+
const spinner2 = (0, import_ora.default)("Validating API key...").start();
|
|
505
|
+
try {
|
|
506
|
+
const apiKeyManager = new ApiKeyManager();
|
|
507
|
+
const isValid = await apiKeyManager.validateApiKey(
|
|
508
|
+
options.apiKey,
|
|
509
|
+
options.apiUrl
|
|
510
|
+
);
|
|
511
|
+
if (!isValid) {
|
|
512
|
+
spinner2.fail("Invalid API key");
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
const userData = await apiKeyManager.getCurrentUser(
|
|
516
|
+
options.apiKey,
|
|
517
|
+
options.apiUrl
|
|
518
|
+
);
|
|
519
|
+
await store.saveCredentials({
|
|
520
|
+
apiKey: options.apiKey,
|
|
521
|
+
userId: userData.user_id,
|
|
522
|
+
orgId: userData.org_id ?? void 0,
|
|
523
|
+
apiUrl: options.apiUrl || getApiUrl()
|
|
524
|
+
});
|
|
525
|
+
spinner2.succeed("Logged in successfully!");
|
|
526
|
+
console.log(import_chalk.default.green(`Welcome back! User ID: ${userData.user_id}`));
|
|
527
|
+
} catch (error) {
|
|
528
|
+
spinner2.fail("Login failed");
|
|
529
|
+
console.error(import_chalk.default.red(error.message));
|
|
530
|
+
process.exit(1);
|
|
531
|
+
}
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const spinner = (0, import_ora.default)("Initializing authentication...").start();
|
|
535
|
+
try {
|
|
536
|
+
const oauth = new OAuthManager({
|
|
537
|
+
clerkPublishableKey: getClerkPublishableKey(),
|
|
538
|
+
dashboardUrl: options.dashboardUrl || getDashboardUrl()
|
|
539
|
+
});
|
|
540
|
+
spinner.text = "Waiting for browser authentication...";
|
|
541
|
+
const sessionToken = await oauth.authenticate("sign-in");
|
|
542
|
+
spinner.text = "Getting API key...";
|
|
543
|
+
const apiKeyManager = new ApiKeyManager();
|
|
544
|
+
const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
|
|
545
|
+
sessionToken,
|
|
546
|
+
options.apiUrl || getApiUrl()
|
|
547
|
+
);
|
|
548
|
+
spinner.text = "Storing credentials...";
|
|
549
|
+
await store.saveCredentials({
|
|
550
|
+
apiKey: key,
|
|
551
|
+
userId,
|
|
552
|
+
orgId,
|
|
553
|
+
apiUrl: options.apiUrl || getApiUrl()
|
|
554
|
+
});
|
|
555
|
+
spinner.succeed("Logged in successfully!");
|
|
556
|
+
console.log(import_chalk.default.green(`Welcome back! User ID: ${userId}`));
|
|
557
|
+
} catch (error) {
|
|
558
|
+
spinner.fail("Login failed");
|
|
559
|
+
console.error(import_chalk.default.red(error.message));
|
|
560
|
+
process.exit(1);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
authCommand.command("status").description("Show authentication status").action(async () => {
|
|
564
|
+
const store = new CredentialStore();
|
|
565
|
+
try {
|
|
566
|
+
const hasCredentials = await store.hasCredentials();
|
|
567
|
+
if (!hasCredentials) {
|
|
568
|
+
console.log(import_chalk.default.yellow("Not authenticated"));
|
|
569
|
+
console.log("Run runtype auth signup or runtype auth login to get started");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const credentials = await store.getCredentials();
|
|
573
|
+
if (credentials) {
|
|
574
|
+
console.log(import_chalk.default.green("\u2713 Authenticated"));
|
|
575
|
+
console.log(`User ID: ${credentials.userId}`);
|
|
576
|
+
console.log(`API URL: ${credentials.apiUrl || "default"}`);
|
|
577
|
+
console.log(`API Key: ${credentials.apiKey ? credentials.apiKey.substring(0, 12) + "..." : "missing"}`);
|
|
578
|
+
console.log(`Org ID: ${credentials.orgId || "none"}`);
|
|
579
|
+
}
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.error(import_chalk.default.red("Error checking auth status:"), error.message);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
authCommand.command("logout").description("Remove stored credentials").action(async () => {
|
|
585
|
+
const store = new CredentialStore();
|
|
586
|
+
const hasCredentials = await store.hasCredentials();
|
|
587
|
+
if (!hasCredentials) {
|
|
588
|
+
console.log(import_chalk.default.yellow("No stored credentials found"));
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
await store.clearCredentials();
|
|
592
|
+
console.log(import_chalk.default.green("\u2713 Logged out successfully"));
|
|
593
|
+
});
|
|
594
|
+
authCommand.command("whoami").description("Display current user information").action(async () => {
|
|
595
|
+
const store = new CredentialStore();
|
|
596
|
+
const credentials = await store.getCredentials();
|
|
597
|
+
if (!credentials || !credentials.apiKey) {
|
|
598
|
+
console.log(import_chalk.default.yellow("Not logged in"));
|
|
599
|
+
console.log("Run: runtype auth login");
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const spinner = (0, import_ora.default)("Fetching user information...").start();
|
|
603
|
+
try {
|
|
604
|
+
const apiKeyManager = new ApiKeyManager();
|
|
605
|
+
const user = await apiKeyManager.getCurrentUser(
|
|
606
|
+
credentials.apiKey,
|
|
607
|
+
credentials.apiUrl
|
|
608
|
+
);
|
|
609
|
+
spinner.stop();
|
|
610
|
+
console.log(import_chalk.default.cyan("Current User:"));
|
|
611
|
+
console.log(` User ID: ${user.user_id}`);
|
|
612
|
+
console.log(` Organization: ${user.org_id || "Personal"}`);
|
|
613
|
+
console.log(` API Key: ${credentials.apiKey.substring(0, 10)}...`);
|
|
614
|
+
console.log(` API URL: ${credentials.apiUrl || getApiUrl()}`);
|
|
615
|
+
console.log(` Created: ${credentials.createdAt}`);
|
|
616
|
+
console.log(` Last Used: ${credentials.lastUsed}`);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
spinner.fail("Failed to get user information");
|
|
619
|
+
console.error(import_chalk.default.red(error.message));
|
|
620
|
+
console.log("\nYou may need to login again: runtype auth login");
|
|
621
|
+
process.exit(1);
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// src/commands/flows.ts
|
|
626
|
+
var import_commander2 = require("commander");
|
|
627
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
628
|
+
var import_ora2 = __toESM(require("ora"));
|
|
629
|
+
var isFlowSummary = (value) => {
|
|
630
|
+
if (!value || typeof value !== "object") {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
const record = value;
|
|
634
|
+
return typeof record.id === "string" && typeof record.name === "string";
|
|
635
|
+
};
|
|
636
|
+
var isFlowListResponse = (value) => {
|
|
637
|
+
if (!value || typeof value !== "object") {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
const record = value;
|
|
641
|
+
if (record.data !== void 0) {
|
|
642
|
+
if (!Array.isArray(record.data) || !record.data.every(isFlowSummary)) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (record.pagination !== void 0 && (typeof record.pagination !== "object" || record.pagination === null)) {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
return true;
|
|
650
|
+
};
|
|
651
|
+
var flowsCommand = new import_commander2.Command("flows").description("Manage flows");
|
|
652
|
+
flowsCommand.command("list").description("List all flows").option("--json", "Output as JSON").action(async (options) => {
|
|
653
|
+
const store = new CredentialStore();
|
|
654
|
+
const apiKey = await store.getApiKey();
|
|
655
|
+
if (!apiKey) {
|
|
656
|
+
console.log(import_chalk2.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
const spinner = (0, import_ora2.default)("Fetching flows...").start();
|
|
660
|
+
try {
|
|
661
|
+
const response = await fetch(`${getApiUrl()}/flows`, {
|
|
662
|
+
headers: {
|
|
663
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
664
|
+
"Content-Type": "application/json"
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
if (!response.ok) {
|
|
668
|
+
throw new Error(`Failed to fetch flows: ${response.statusText}`);
|
|
669
|
+
}
|
|
670
|
+
const data = await response.json();
|
|
671
|
+
spinner.stop();
|
|
672
|
+
if (options.json) {
|
|
673
|
+
console.log(JSON.stringify(data, null, 2));
|
|
674
|
+
} else {
|
|
675
|
+
if (!isFlowListResponse(data)) {
|
|
676
|
+
throw new Error("Unexpected flows response format");
|
|
677
|
+
}
|
|
678
|
+
console.log(import_chalk2.default.cyan("Your Flows:"));
|
|
679
|
+
const flowsArray = data.data ?? [];
|
|
680
|
+
if (flowsArray.length > 0) {
|
|
681
|
+
flowsArray.forEach((flow) => {
|
|
682
|
+
console.log(` ${import_chalk2.default.green(flow.id)} - ${flow.name}`);
|
|
683
|
+
if (flow.description) {
|
|
684
|
+
console.log(` ${import_chalk2.default.gray(flow.description)}`);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
const total = data.pagination?.total_count ?? data.pagination?.total;
|
|
688
|
+
if (typeof total === "number") {
|
|
689
|
+
console.log(import_chalk2.default.dim(`
|
|
690
|
+
Total: ${total} flows`));
|
|
691
|
+
}
|
|
692
|
+
} else {
|
|
693
|
+
console.log(import_chalk2.default.gray(" No flows found"));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
} catch (error) {
|
|
697
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
698
|
+
spinner.fail("Failed to fetch flows");
|
|
699
|
+
console.error(import_chalk2.default.red(message));
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
flowsCommand.command("run <id>").description("Execute a flow").option("-r, --record <id>", "Record ID to run with").option("--stream", "Stream the response", true).action(async (flowId, options) => {
|
|
704
|
+
void options;
|
|
705
|
+
const store = new CredentialStore();
|
|
706
|
+
const apiKey = await store.getApiKey();
|
|
707
|
+
if (!apiKey) {
|
|
708
|
+
console.log(import_chalk2.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
709
|
+
process.exit(1);
|
|
710
|
+
}
|
|
711
|
+
console.log(import_chalk2.default.cyan(`Running flow ${flowId}...`));
|
|
712
|
+
console.log(import_chalk2.default.gray("Implementation coming soon"));
|
|
713
|
+
});
|
|
714
|
+
flowsCommand.command("create").description("Create a new flow").action(async () => {
|
|
715
|
+
console.log(import_chalk2.default.cyan("Interactive flow creation coming soon"));
|
|
716
|
+
});
|
|
717
|
+
flowsCommand.command("delete <id>").description("Delete a flow").action(async (flowId) => {
|
|
718
|
+
console.log(import_chalk2.default.cyan(`Deleting flow ${flowId}...`));
|
|
719
|
+
console.log(import_chalk2.default.gray("Implementation coming soon"));
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
// src/commands/records.ts
|
|
723
|
+
var import_commander3 = require("commander");
|
|
724
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
725
|
+
var import_ora3 = __toESM(require("ora"));
|
|
726
|
+
var isRecordSummary = (value) => {
|
|
727
|
+
if (!value || typeof value !== "object") {
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
const record = value;
|
|
731
|
+
return typeof record.id === "string" && typeof record.name === "string" && typeof record.type === "string";
|
|
732
|
+
};
|
|
733
|
+
var isRecordListResponse = (value) => {
|
|
734
|
+
if (!value || typeof value !== "object") {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
const record = value;
|
|
738
|
+
if (record.data !== void 0) {
|
|
739
|
+
if (!Array.isArray(record.data) || !record.data.every(isRecordSummary)) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (record.pagination !== void 0 && (typeof record.pagination !== "object" || record.pagination === null)) {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
return true;
|
|
747
|
+
};
|
|
748
|
+
var isRecordCreateResponse = (value) => {
|
|
749
|
+
if (!value || typeof value !== "object") {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
const record = value;
|
|
753
|
+
return typeof record.id === "string" && typeof record.name === "string" && typeof record.type === "string";
|
|
754
|
+
};
|
|
755
|
+
var recordsCommand = new import_commander3.Command("records").description("Manage records");
|
|
756
|
+
recordsCommand.command("list").description("List all records").option("--type <type>", "Filter by record type").option("--limit <n>", "Limit number of results", "20").option("--json", "Output as JSON").action(async (options) => {
|
|
757
|
+
const store = new CredentialStore();
|
|
758
|
+
const apiKey = await store.getApiKey();
|
|
759
|
+
if (!apiKey) {
|
|
760
|
+
console.log(import_chalk3.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
761
|
+
process.exit(1);
|
|
762
|
+
}
|
|
763
|
+
const spinner = (0, import_ora3.default)("Fetching records...").start();
|
|
764
|
+
try {
|
|
765
|
+
const params = new URLSearchParams();
|
|
766
|
+
if (options.type) params.append("type", options.type);
|
|
767
|
+
params.append("limit", options.limit);
|
|
768
|
+
const response = await fetch(`${getApiUrl()}/${getApiVersion()}/records?${params}`, {
|
|
769
|
+
headers: {
|
|
770
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
771
|
+
"Content-Type": "application/json"
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
if (!response.ok) {
|
|
775
|
+
throw new Error(`Failed to fetch records: ${response.statusText}`);
|
|
776
|
+
}
|
|
777
|
+
const data = await response.json();
|
|
778
|
+
spinner.stop();
|
|
779
|
+
if (options.json) {
|
|
780
|
+
console.log(JSON.stringify(data, null, 2));
|
|
781
|
+
} else {
|
|
782
|
+
if (!isRecordListResponse(data)) {
|
|
783
|
+
throw new Error("Unexpected records response format");
|
|
784
|
+
}
|
|
785
|
+
console.log(import_chalk3.default.cyan("Your Records:"));
|
|
786
|
+
const recordsArray = data.data ?? [];
|
|
787
|
+
if (recordsArray.length > 0) {
|
|
788
|
+
recordsArray.forEach((record) => {
|
|
789
|
+
console.log(` ${import_chalk3.default.green(record.id)} - ${record.name} (${record.type})`);
|
|
790
|
+
if (record.metadata && Object.keys(record.metadata).length > 0) {
|
|
791
|
+
console.log(` ${import_chalk3.default.gray(JSON.stringify(record.metadata))}`);
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
const total = data.pagination?.total_count ?? data.pagination?.total;
|
|
795
|
+
if (typeof total === "number") {
|
|
796
|
+
console.log(import_chalk3.default.dim(`
|
|
797
|
+
Total: ${total} records`));
|
|
798
|
+
}
|
|
799
|
+
} else {
|
|
800
|
+
console.log(import_chalk3.default.gray(" No records found"));
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} catch (error) {
|
|
804
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
805
|
+
spinner.fail("Failed to fetch records");
|
|
806
|
+
console.error(import_chalk3.default.red(message));
|
|
807
|
+
process.exit(1);
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
recordsCommand.command("create").description("Create a new record").requiredOption("-n, --name <name>", "Record name").requiredOption("-t, --type <type>", "Record type").option("-m, --metadata <json>", "Metadata as JSON string").action(async (options) => {
|
|
811
|
+
const store = new CredentialStore();
|
|
812
|
+
const apiKey = await store.getApiKey();
|
|
813
|
+
if (!apiKey) {
|
|
814
|
+
console.log(import_chalk3.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
815
|
+
process.exit(1);
|
|
816
|
+
}
|
|
817
|
+
const spinner = (0, import_ora3.default)("Creating record...").start();
|
|
818
|
+
try {
|
|
819
|
+
let metadata = {};
|
|
820
|
+
if (options.metadata) {
|
|
821
|
+
try {
|
|
822
|
+
metadata = JSON.parse(options.metadata);
|
|
823
|
+
} catch {
|
|
824
|
+
throw new Error("Invalid JSON in metadata");
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const response = await fetch(`${getApiUrl()}/${getApiVersion()}/records`, {
|
|
828
|
+
method: "POST",
|
|
829
|
+
headers: {
|
|
830
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
831
|
+
"Content-Type": "application/json"
|
|
832
|
+
},
|
|
833
|
+
body: JSON.stringify({
|
|
834
|
+
name: options.name,
|
|
835
|
+
type: options.type,
|
|
836
|
+
metadata
|
|
837
|
+
})
|
|
838
|
+
});
|
|
839
|
+
if (!response.ok) {
|
|
840
|
+
const error = await response.text();
|
|
841
|
+
throw new Error(`Failed to create record: ${error}`);
|
|
842
|
+
}
|
|
843
|
+
const data = await response.json();
|
|
844
|
+
if (!isRecordCreateResponse(data)) {
|
|
845
|
+
throw new Error("Unexpected create record response format");
|
|
846
|
+
}
|
|
847
|
+
spinner.succeed("Record created successfully!");
|
|
848
|
+
console.log(import_chalk3.default.green(`Record ID: ${data.id}`));
|
|
849
|
+
console.log(import_chalk3.default.gray(`Name: ${data.name}`));
|
|
850
|
+
console.log(import_chalk3.default.gray(`Type: ${data.type}`));
|
|
851
|
+
} catch (error) {
|
|
852
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
853
|
+
spinner.fail("Failed to create record");
|
|
854
|
+
console.error(import_chalk3.default.red(message));
|
|
855
|
+
process.exit(1);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
recordsCommand.command("bulk").description("Bulk operations on records").action(async () => {
|
|
859
|
+
console.log(import_chalk3.default.cyan("Bulk operations coming soon"));
|
|
860
|
+
});
|
|
861
|
+
recordsCommand.command("export").description("Export records").option("--format <format>", "Export format (json, csv)", "json").action(async (options) => {
|
|
862
|
+
console.log(import_chalk3.default.cyan(`Exporting records as ${options.format}...`));
|
|
863
|
+
console.log(import_chalk3.default.gray("Implementation coming soon"));
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// src/commands/prompts.ts
|
|
867
|
+
var import_commander4 = require("commander");
|
|
868
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
869
|
+
var import_ora4 = __toESM(require("ora"));
|
|
870
|
+
var isPromptSummary = (value) => {
|
|
871
|
+
if (!value || typeof value !== "object") {
|
|
872
|
+
return false;
|
|
873
|
+
}
|
|
874
|
+
const record = value;
|
|
875
|
+
return typeof record.id === "string" && typeof record.name === "string";
|
|
876
|
+
};
|
|
877
|
+
var isPromptListResponse = (value) => {
|
|
878
|
+
if (!value || typeof value !== "object") {
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
const record = value;
|
|
882
|
+
if (record.data !== void 0) {
|
|
883
|
+
if (!Array.isArray(record.data) || !record.data.every(isPromptSummary)) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (record.pagination !== void 0 && (typeof record.pagination !== "object" || record.pagination === null)) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
return true;
|
|
891
|
+
};
|
|
892
|
+
var promptsCommand = new import_commander4.Command("prompts").description("Manage prompts");
|
|
893
|
+
promptsCommand.command("list").description("List all prompts").option("--json", "Output as JSON").action(async (options) => {
|
|
894
|
+
const store = new CredentialStore();
|
|
895
|
+
const apiKey = await store.getApiKey();
|
|
896
|
+
if (!apiKey) {
|
|
897
|
+
console.log(import_chalk4.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
898
|
+
process.exit(1);
|
|
899
|
+
}
|
|
900
|
+
const spinner = (0, import_ora4.default)("Fetching prompts...").start();
|
|
901
|
+
try {
|
|
902
|
+
const response = await fetch(`${getApiUrl()}/prompts`, {
|
|
903
|
+
headers: {
|
|
904
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
905
|
+
"Content-Type": "application/json"
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
if (!response.ok) {
|
|
909
|
+
throw new Error(`Failed to fetch prompts: ${response.statusText}`);
|
|
910
|
+
}
|
|
911
|
+
const data = await response.json();
|
|
912
|
+
spinner.stop();
|
|
913
|
+
if (options.json) {
|
|
914
|
+
console.log(JSON.stringify(data, null, 2));
|
|
915
|
+
} else {
|
|
916
|
+
if (!isPromptListResponse(data)) {
|
|
917
|
+
throw new Error("Unexpected prompts response format");
|
|
918
|
+
}
|
|
919
|
+
console.log(import_chalk4.default.cyan("Your Prompts:"));
|
|
920
|
+
const promptsArray = data.data ?? [];
|
|
921
|
+
if (promptsArray.length > 0) {
|
|
922
|
+
promptsArray.forEach((prompt) => {
|
|
923
|
+
console.log(` ${import_chalk4.default.green(prompt.id)} - ${prompt.name}`);
|
|
924
|
+
if (prompt.description) {
|
|
925
|
+
console.log(` ${import_chalk4.default.gray(prompt.description)}`);
|
|
926
|
+
}
|
|
927
|
+
if (prompt.model) {
|
|
928
|
+
console.log(` Model: ${import_chalk4.default.blue(prompt.model)}`);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
const total = data.pagination?.total_count;
|
|
932
|
+
if (typeof total === "number") {
|
|
933
|
+
console.log(import_chalk4.default.dim(`
|
|
934
|
+
Total: ${total} prompts`));
|
|
935
|
+
}
|
|
936
|
+
} else {
|
|
937
|
+
console.log(import_chalk4.default.gray(" No prompts found"));
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
} catch (error) {
|
|
941
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
942
|
+
spinner.fail("Failed to fetch prompts");
|
|
943
|
+
console.error(import_chalk4.default.red(message));
|
|
944
|
+
process.exit(1);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
promptsCommand.command("test <id>").description("Test a prompt").option("-i, --input <text>", "Input text for the prompt").action(async (promptId, options) => {
|
|
948
|
+
void options;
|
|
949
|
+
const store = new CredentialStore();
|
|
950
|
+
const apiKey = await store.getApiKey();
|
|
951
|
+
if (!apiKey) {
|
|
952
|
+
console.log(import_chalk4.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
953
|
+
process.exit(1);
|
|
954
|
+
}
|
|
955
|
+
console.log(import_chalk4.default.cyan(`Testing prompt ${promptId}...`));
|
|
956
|
+
console.log(import_chalk4.default.gray("Implementation coming soon"));
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
// src/commands/batch.ts
|
|
960
|
+
var import_commander5 = require("commander");
|
|
961
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
962
|
+
var import_ora5 = __toESM(require("ora"));
|
|
963
|
+
var isBatchStatusResponse = (value) => {
|
|
964
|
+
if (!value || typeof value !== "object") {
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
const record = value;
|
|
968
|
+
return typeof record.status === "string" && typeof record.processed_count === "number" && typeof record.total_count === "number" && typeof record.failed_count === "number";
|
|
969
|
+
};
|
|
970
|
+
var batchCommand = new import_commander5.Command("batch").description("Manage batch operations");
|
|
971
|
+
batchCommand.command("submit").description("Submit a batch job").requiredOption("-f, --flow <id>", "Flow ID to execute").requiredOption("-r, --records <file>", "JSON file with record IDs").action(async (options) => {
|
|
972
|
+
const store = new CredentialStore();
|
|
973
|
+
const apiKey = await store.getApiKey();
|
|
974
|
+
if (!apiKey) {
|
|
975
|
+
console.log(import_chalk5.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
976
|
+
process.exit(1);
|
|
977
|
+
}
|
|
978
|
+
console.log(import_chalk5.default.cyan("Submitting batch job..."));
|
|
979
|
+
console.log(import_chalk5.default.gray(`Flow: ${options.flow}`));
|
|
980
|
+
console.log(import_chalk5.default.gray(`Records file: ${options.records}`));
|
|
981
|
+
console.log(import_chalk5.default.gray("Implementation coming soon"));
|
|
982
|
+
});
|
|
983
|
+
batchCommand.command("status <id>").description("Check batch job status").option("--watch", "Watch for updates").action(async (batchId, options) => {
|
|
984
|
+
const store = new CredentialStore();
|
|
985
|
+
const apiKey = await store.getApiKey();
|
|
986
|
+
if (!apiKey) {
|
|
987
|
+
console.log(import_chalk5.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
988
|
+
process.exit(1);
|
|
989
|
+
}
|
|
990
|
+
const spinner = (0, import_ora5.default)("Fetching batch status...").start();
|
|
991
|
+
try {
|
|
992
|
+
const response = await fetch(`${getApiUrl()}/${getApiVersion()}/batch/status/${batchId}`, {
|
|
993
|
+
headers: {
|
|
994
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
995
|
+
"Content-Type": "application/json"
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
if (!response.ok) {
|
|
999
|
+
throw new Error(`Failed to fetch batch status: ${response.statusText}`);
|
|
1000
|
+
}
|
|
1001
|
+
const data = await response.json();
|
|
1002
|
+
if (!isBatchStatusResponse(data)) {
|
|
1003
|
+
throw new Error("Invalid batch status response received from the API");
|
|
1004
|
+
}
|
|
1005
|
+
spinner.stop();
|
|
1006
|
+
console.log(import_chalk5.default.cyan(`Batch Job: ${batchId}`));
|
|
1007
|
+
console.log(` Status: ${import_chalk5.default.green(data.status)}`);
|
|
1008
|
+
console.log(` Progress: ${data.processed_count}/${data.total_count}`);
|
|
1009
|
+
if (data.failed_count > 0) {
|
|
1010
|
+
console.log(` Failed: ${import_chalk5.default.red(data.failed_count)}`);
|
|
1011
|
+
}
|
|
1012
|
+
if (options.watch && data.status === "processing") {
|
|
1013
|
+
console.log(import_chalk5.default.gray("\nWatching for updates... (Ctrl+C to stop)"));
|
|
1014
|
+
}
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1017
|
+
spinner.fail("Failed to fetch batch status");
|
|
1018
|
+
console.error(import_chalk5.default.red(message));
|
|
1019
|
+
process.exit(1);
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
batchCommand.command("cancel <id>").description("Cancel a batch job").action(async (batchId) => {
|
|
1023
|
+
const store = new CredentialStore();
|
|
1024
|
+
const apiKey = await store.getApiKey();
|
|
1025
|
+
if (!apiKey) {
|
|
1026
|
+
console.log(import_chalk5.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
1027
|
+
process.exit(1);
|
|
1028
|
+
}
|
|
1029
|
+
const spinner = (0, import_ora5.default)("Cancelling batch job...").start();
|
|
1030
|
+
try {
|
|
1031
|
+
const response = await fetch(`${getApiUrl()}/${getApiVersion()}/batch/cancel/${batchId}`, {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers: {
|
|
1034
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
1035
|
+
"Content-Type": "application/json"
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
if (!response.ok) {
|
|
1039
|
+
throw new Error(`Failed to cancel batch: ${response.statusText}`);
|
|
1040
|
+
}
|
|
1041
|
+
spinner.succeed("Batch job cancelled successfully");
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1044
|
+
spinner.fail("Failed to cancel batch job");
|
|
1045
|
+
console.error(import_chalk5.default.red(message));
|
|
1046
|
+
process.exit(1);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
// src/commands/talk.ts
|
|
1051
|
+
var import_commander6 = require("commander");
|
|
1052
|
+
var import_readline = __toESM(require("readline"));
|
|
1053
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
1054
|
+
|
|
1055
|
+
// src/chat/session-manager.ts
|
|
1056
|
+
var import_uuid = require("uuid");
|
|
1057
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
1058
|
+
var import_path2 = __toESM(require("path"));
|
|
1059
|
+
var import_os2 = __toESM(require("os"));
|
|
1060
|
+
var ChatSession = class {
|
|
1061
|
+
sessionId;
|
|
1062
|
+
messages = [];
|
|
1063
|
+
modelConfig;
|
|
1064
|
+
sessionDir;
|
|
1065
|
+
constructor(modelConfig) {
|
|
1066
|
+
this.sessionId = (0, import_uuid.v4)();
|
|
1067
|
+
this.modelConfig = {
|
|
1068
|
+
model: modelConfig?.model || "meta/llama3.1-8b-instruct-free",
|
|
1069
|
+
temperature: modelConfig?.temperature ?? 0.7,
|
|
1070
|
+
maxOutputTokens: modelConfig?.maxOutputTokens || 2e3,
|
|
1071
|
+
systemPrompt: modelConfig?.systemPrompt
|
|
1072
|
+
};
|
|
1073
|
+
this.sessionDir = import_path2.default.join(import_os2.default.homedir(), ".runtype", "chat-sessions");
|
|
1074
|
+
}
|
|
1075
|
+
addMessage(role, content) {
|
|
1076
|
+
this.messages.push({
|
|
1077
|
+
role,
|
|
1078
|
+
content,
|
|
1079
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
getMessages() {
|
|
1083
|
+
return this.messages;
|
|
1084
|
+
}
|
|
1085
|
+
clearHistory() {
|
|
1086
|
+
this.messages = [];
|
|
1087
|
+
console.log("Chat history cleared");
|
|
1088
|
+
}
|
|
1089
|
+
setModel(model) {
|
|
1090
|
+
this.modelConfig.model = model;
|
|
1091
|
+
console.log(`Model changed to: ${model}`);
|
|
1092
|
+
}
|
|
1093
|
+
setTemperature(temperature) {
|
|
1094
|
+
this.modelConfig.temperature = Math.max(0, Math.min(1, temperature));
|
|
1095
|
+
console.log(`Temperature set to: ${this.modelConfig.temperature}`);
|
|
1096
|
+
}
|
|
1097
|
+
getConversationContext() {
|
|
1098
|
+
return this.messages.map((m) => {
|
|
1099
|
+
const role = m.role === "user" ? "User" : m.role === "assistant" ? "Assistant" : "System";
|
|
1100
|
+
return `${role}: ${m.content}`;
|
|
1101
|
+
}).join("\n\n");
|
|
1102
|
+
}
|
|
1103
|
+
buildDispatchPayload(userInput) {
|
|
1104
|
+
this.addMessage("user", userInput);
|
|
1105
|
+
const metadata = {};
|
|
1106
|
+
return {
|
|
1107
|
+
record: {
|
|
1108
|
+
name: "Chat session",
|
|
1109
|
+
type: "standalone",
|
|
1110
|
+
metadata
|
|
1111
|
+
},
|
|
1112
|
+
flow: {
|
|
1113
|
+
name: "Chat Flow",
|
|
1114
|
+
description: "",
|
|
1115
|
+
mode: "standalone",
|
|
1116
|
+
steps: [
|
|
1117
|
+
{
|
|
1118
|
+
id: "1",
|
|
1119
|
+
type: "prompt",
|
|
1120
|
+
name: "Chat Response",
|
|
1121
|
+
order: 1,
|
|
1122
|
+
enabled: true,
|
|
1123
|
+
config: {
|
|
1124
|
+
text: this.buildPrompt(userInput),
|
|
1125
|
+
model: this.modelConfig.model,
|
|
1126
|
+
output_format: "text",
|
|
1127
|
+
output_variable: "chat_result",
|
|
1128
|
+
response_format: "text"
|
|
1129
|
+
},
|
|
1130
|
+
prompt_id: null,
|
|
1131
|
+
context_template_id: null,
|
|
1132
|
+
item_type: "prompt",
|
|
1133
|
+
prompt: null,
|
|
1134
|
+
context_template: null
|
|
1135
|
+
}
|
|
1136
|
+
]
|
|
1137
|
+
},
|
|
1138
|
+
options: {
|
|
1139
|
+
stream_response: true,
|
|
1140
|
+
record_mode: "virtual",
|
|
1141
|
+
flow_mode: "virtual"
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
buildPrompt(userInput) {
|
|
1146
|
+
const systemContext = this.modelConfig.systemPrompt || `You are a helpful AI assistant. Provide clear, concise, and helpful responses to user questions. Use markdown formatting when appropriate.`;
|
|
1147
|
+
if (this.messages.length > 1) {
|
|
1148
|
+
const history = this.messages.slice(0, -1).slice(-10).map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
1149
|
+
return `${systemContext}
|
|
1150
|
+
|
|
1151
|
+
Conversation history:
|
|
1152
|
+
${history}
|
|
1153
|
+
|
|
1154
|
+
${userInput}`;
|
|
1155
|
+
} else {
|
|
1156
|
+
return `${systemContext}
|
|
1157
|
+
|
|
1158
|
+
${userInput}`;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
async saveToFile(filename) {
|
|
1162
|
+
await import_promises.default.mkdir(this.sessionDir, { recursive: true });
|
|
1163
|
+
const file = filename || `session-${Date.now()}.json`;
|
|
1164
|
+
const filepath = import_path2.default.join(this.sessionDir, file);
|
|
1165
|
+
const sessionData = {
|
|
1166
|
+
sessionId: this.sessionId,
|
|
1167
|
+
modelConfig: this.modelConfig,
|
|
1168
|
+
messages: this.messages,
|
|
1169
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1170
|
+
};
|
|
1171
|
+
await import_promises.default.writeFile(filepath, JSON.stringify(sessionData, null, 2));
|
|
1172
|
+
return filepath;
|
|
1173
|
+
}
|
|
1174
|
+
async loadFromFile(filename) {
|
|
1175
|
+
const filepath = import_path2.default.join(this.sessionDir, filename);
|
|
1176
|
+
const data = await import_promises.default.readFile(filepath, "utf-8");
|
|
1177
|
+
const sessionData = JSON.parse(data);
|
|
1178
|
+
this.sessionId = sessionData.sessionId;
|
|
1179
|
+
this.modelConfig = sessionData.modelConfig;
|
|
1180
|
+
this.messages = sessionData.messages;
|
|
1181
|
+
}
|
|
1182
|
+
async exportAsMarkdown() {
|
|
1183
|
+
await import_promises.default.mkdir(this.sessionDir, { recursive: true });
|
|
1184
|
+
const filename = `export-${Date.now()}.md`;
|
|
1185
|
+
const filepath = import_path2.default.join(this.sessionDir, filename);
|
|
1186
|
+
let content = `# Runtype Chat Session
|
|
1187
|
+
|
|
1188
|
+
`;
|
|
1189
|
+
content += `**Session ID:** ${this.sessionId}
|
|
1190
|
+
`;
|
|
1191
|
+
content += `**Model:** ${this.modelConfig.model}
|
|
1192
|
+
`;
|
|
1193
|
+
content += `**Date:** ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
1194
|
+
|
|
1195
|
+
`;
|
|
1196
|
+
content += `---
|
|
1197
|
+
|
|
1198
|
+
`;
|
|
1199
|
+
for (const message of this.messages) {
|
|
1200
|
+
const role = message.role === "user" ? "**User**" : message.role === "assistant" ? "**Assistant**" : "**System**";
|
|
1201
|
+
content += `${role} *(${new Date(message.timestamp).toLocaleString()})*
|
|
1202
|
+
|
|
1203
|
+
`;
|
|
1204
|
+
content += `${message.content}
|
|
1205
|
+
|
|
1206
|
+
`;
|
|
1207
|
+
content += `---
|
|
1208
|
+
|
|
1209
|
+
`;
|
|
1210
|
+
}
|
|
1211
|
+
await import_promises.default.writeFile(filepath, content);
|
|
1212
|
+
return filepath;
|
|
1213
|
+
}
|
|
1214
|
+
getSessionInfo() {
|
|
1215
|
+
const firstMessage = this.messages[0];
|
|
1216
|
+
const lastMessage = this.messages[this.messages.length - 1];
|
|
1217
|
+
let duration = "N/A";
|
|
1218
|
+
if (firstMessage && lastMessage) {
|
|
1219
|
+
const start = new Date(firstMessage.timestamp);
|
|
1220
|
+
const end = new Date(lastMessage.timestamp);
|
|
1221
|
+
const diff = end.getTime() - start.getTime();
|
|
1222
|
+
const minutes = Math.floor(diff / 6e4);
|
|
1223
|
+
const seconds = Math.floor(diff % 6e4 / 1e3);
|
|
1224
|
+
duration = `${minutes}m ${seconds}s`;
|
|
1225
|
+
}
|
|
1226
|
+
return {
|
|
1227
|
+
id: this.sessionId,
|
|
1228
|
+
messageCount: this.messages.length,
|
|
1229
|
+
model: this.modelConfig.model,
|
|
1230
|
+
temperature: this.modelConfig.temperature || 0.7,
|
|
1231
|
+
duration
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
// src/chat/stream-handler.ts
|
|
1237
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
1238
|
+
var import_sdk = require("@runtypelabs/sdk");
|
|
1239
|
+
var StreamHandler = class {
|
|
1240
|
+
buffer = "";
|
|
1241
|
+
isStreaming = false;
|
|
1242
|
+
enableMarkdown;
|
|
1243
|
+
constructor(options = {}) {
|
|
1244
|
+
this.enableMarkdown = options.enableMarkdown ?? true;
|
|
1245
|
+
}
|
|
1246
|
+
async handleStream(response) {
|
|
1247
|
+
this.isStreaming = true;
|
|
1248
|
+
this.buffer = "";
|
|
1249
|
+
const callbacks = {
|
|
1250
|
+
onStepChunk: (chunk) => {
|
|
1251
|
+
this.appendOutput(chunk);
|
|
1252
|
+
},
|
|
1253
|
+
onError: (error) => {
|
|
1254
|
+
console.error(import_chalk6.default.red(`
|
|
1255
|
+
Error: ${error.message}`));
|
|
1256
|
+
}
|
|
1257
|
+
// All other events (step_start, step_complete, flow_start, etc.) are
|
|
1258
|
+
// silently ignored - processStream handles them but we don't need to display them
|
|
1259
|
+
};
|
|
1260
|
+
try {
|
|
1261
|
+
await (0, import_sdk.processStream)(response, callbacks);
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1264
|
+
console.error(import_chalk6.default.red(`
|
|
1265
|
+
Stream error: ${message}`));
|
|
1266
|
+
throw error;
|
|
1267
|
+
} finally {
|
|
1268
|
+
this.isStreaming = false;
|
|
1269
|
+
this.flush();
|
|
1270
|
+
}
|
|
1271
|
+
return this.buffer;
|
|
1272
|
+
}
|
|
1273
|
+
flush() {
|
|
1274
|
+
if (this.buffer && !this.buffer.endsWith("\n")) {
|
|
1275
|
+
process.stdout.write("\n");
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
appendOutput(text) {
|
|
1279
|
+
const output = this.enableMarkdown ? text : this.stripMarkdown(text);
|
|
1280
|
+
this.buffer += output;
|
|
1281
|
+
process.stdout.write(output);
|
|
1282
|
+
}
|
|
1283
|
+
stripMarkdown(text) {
|
|
1284
|
+
return text.replace(/([*_`~]|\r)/g, "");
|
|
1285
|
+
}
|
|
1286
|
+
isCurrentlyStreaming() {
|
|
1287
|
+
return this.isStreaming;
|
|
1288
|
+
}
|
|
1289
|
+
getBuffer() {
|
|
1290
|
+
return this.buffer;
|
|
1291
|
+
}
|
|
1292
|
+
clearBuffer() {
|
|
1293
|
+
this.buffer = "";
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
// src/commands/talk.ts
|
|
1298
|
+
var CHAT_COMMANDS = {
|
|
1299
|
+
"/help": "Show available commands",
|
|
1300
|
+
"/clear": "Clear conversation history",
|
|
1301
|
+
"/save [filename]": "Save session to file",
|
|
1302
|
+
"/load <filename>": "Load session from file",
|
|
1303
|
+
"/export": "Export conversation as markdown",
|
|
1304
|
+
"/model <model>": "Change AI model",
|
|
1305
|
+
"/temp <value>": "Set temperature (0-1)",
|
|
1306
|
+
"/info": "Show session information",
|
|
1307
|
+
"/exit": "Exit chat session"
|
|
1308
|
+
};
|
|
1309
|
+
var talkCommand = new import_commander6.Command("talk").description("Start an interactive chat session").option("-m, --model <model>", "AI model to use", getDefaultModel()).option("-t, --temperature <temp>", "Temperature (0-1)", getDefaultTemperature().toString()).option("--max-tokens <tokens>", "Max response tokens", "2000").option("--system <prompt>", "System prompt to prepend").option("--no-markdown", "Disable markdown rendering").option("--continue <file>", "Continue from saved session").action(async (options) => {
|
|
1310
|
+
const store = new CredentialStore();
|
|
1311
|
+
const apiKey = await store.getApiKey();
|
|
1312
|
+
if (!apiKey) {
|
|
1313
|
+
console.log(import_chalk7.default.yellow("Not authenticated. Please run: runtype auth login"));
|
|
1314
|
+
process.exit(1);
|
|
1315
|
+
}
|
|
1316
|
+
const session = new ChatSession({
|
|
1317
|
+
model: options.model,
|
|
1318
|
+
temperature: parseFloat(options.temperature),
|
|
1319
|
+
maxOutputTokens: parseInt(options.maxOutputTokens),
|
|
1320
|
+
systemPrompt: options.system
|
|
1321
|
+
});
|
|
1322
|
+
if (options.continue) {
|
|
1323
|
+
try {
|
|
1324
|
+
await session.loadFromFile(options.continue);
|
|
1325
|
+
console.log(import_chalk7.default.green(`Loaded session from: ${options.continue}`));
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
console.error(import_chalk7.default.red(`Failed to load session: ${error.message}`));
|
|
1328
|
+
process.exit(1);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
const apiUrl = getApiUrl();
|
|
1332
|
+
const streamHandler = new StreamHandler({
|
|
1333
|
+
enableMarkdown: options.markdown !== false
|
|
1334
|
+
});
|
|
1335
|
+
const rl = import_readline.default.createInterface({
|
|
1336
|
+
input: process.stdin,
|
|
1337
|
+
output: process.stdout,
|
|
1338
|
+
prompt: import_chalk7.default.cyan("You> "),
|
|
1339
|
+
terminal: true,
|
|
1340
|
+
historySize: 100
|
|
1341
|
+
});
|
|
1342
|
+
console.clear();
|
|
1343
|
+
console.log(import_chalk7.default.green("\u{1F916} Runtype Chat Session Started"));
|
|
1344
|
+
console.log(import_chalk7.default.gray(`Model: ${options.model} | Temperature: ${options.temperature}`));
|
|
1345
|
+
console.log(import_chalk7.default.gray('Type "/help" for commands or "exit" to quit\n'));
|
|
1346
|
+
if (options.system) {
|
|
1347
|
+
session.addMessage("system", options.system);
|
|
1348
|
+
}
|
|
1349
|
+
const handleCommand = async (input) => {
|
|
1350
|
+
const parts = input.split(" ");
|
|
1351
|
+
const command = parts[0];
|
|
1352
|
+
const args = parts.slice(1).join(" ");
|
|
1353
|
+
switch (command) {
|
|
1354
|
+
case "/help":
|
|
1355
|
+
console.log(import_chalk7.default.cyan("\nAvailable commands:"));
|
|
1356
|
+
for (const [cmd, desc] of Object.entries(CHAT_COMMANDS)) {
|
|
1357
|
+
console.log(` ${import_chalk7.default.green(cmd)} - ${desc}`);
|
|
1358
|
+
}
|
|
1359
|
+
console.log();
|
|
1360
|
+
return true;
|
|
1361
|
+
case "/clear":
|
|
1362
|
+
session.clearHistory();
|
|
1363
|
+
console.clear();
|
|
1364
|
+
console.log(import_chalk7.default.green("\u{1F916} Chat history cleared"));
|
|
1365
|
+
return true;
|
|
1366
|
+
case "/save":
|
|
1367
|
+
try {
|
|
1368
|
+
const filepath = await session.saveToFile(args || void 0);
|
|
1369
|
+
console.log(import_chalk7.default.green(`Session saved to: ${filepath}`));
|
|
1370
|
+
} catch (error) {
|
|
1371
|
+
console.error(import_chalk7.default.red(`Failed to save: ${error.message}`));
|
|
1372
|
+
}
|
|
1373
|
+
return true;
|
|
1374
|
+
case "/load":
|
|
1375
|
+
if (!args) {
|
|
1376
|
+
console.log(import_chalk7.default.red("Please specify a filename"));
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
try {
|
|
1380
|
+
await session.loadFromFile(args);
|
|
1381
|
+
console.log(import_chalk7.default.green(`Session loaded from: ${args}`));
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
console.error(import_chalk7.default.red(`Failed to load: ${error.message}`));
|
|
1384
|
+
}
|
|
1385
|
+
return true;
|
|
1386
|
+
case "/export":
|
|
1387
|
+
try {
|
|
1388
|
+
const filepath = await session.exportAsMarkdown();
|
|
1389
|
+
console.log(import_chalk7.default.green(`Conversation exported to: ${filepath}`));
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
console.error(import_chalk7.default.red(`Failed to export: ${error.message}`));
|
|
1392
|
+
}
|
|
1393
|
+
return true;
|
|
1394
|
+
case "/model":
|
|
1395
|
+
if (!args) {
|
|
1396
|
+
console.log(import_chalk7.default.yellow("Please specify a model"));
|
|
1397
|
+
return true;
|
|
1398
|
+
}
|
|
1399
|
+
session.setModel(args);
|
|
1400
|
+
return true;
|
|
1401
|
+
case "/temp":
|
|
1402
|
+
if (!args) {
|
|
1403
|
+
console.log(import_chalk7.default.yellow("Please specify a temperature (0-1)"));
|
|
1404
|
+
return true;
|
|
1405
|
+
}
|
|
1406
|
+
const temp = parseFloat(args);
|
|
1407
|
+
if (isNaN(temp) || temp < 0 || temp > 1) {
|
|
1408
|
+
console.log(import_chalk7.default.red("Temperature must be between 0 and 1"));
|
|
1409
|
+
return true;
|
|
1410
|
+
}
|
|
1411
|
+
session.setTemperature(temp);
|
|
1412
|
+
return true;
|
|
1413
|
+
case "/info":
|
|
1414
|
+
const info = session.getSessionInfo();
|
|
1415
|
+
console.log(import_chalk7.default.cyan("\nSession Information:"));
|
|
1416
|
+
console.log(` Session ID: ${info.id}`);
|
|
1417
|
+
console.log(` Messages: ${info.messageCount}`);
|
|
1418
|
+
console.log(` Model: ${info.model}`);
|
|
1419
|
+
console.log(` Temperature: ${info.temperature}`);
|
|
1420
|
+
console.log(` Duration: ${info.duration}`);
|
|
1421
|
+
console.log();
|
|
1422
|
+
return true;
|
|
1423
|
+
case "/exit":
|
|
1424
|
+
return false;
|
|
1425
|
+
default:
|
|
1426
|
+
if (input.startsWith("/")) {
|
|
1427
|
+
console.log(import_chalk7.default.red(`Unknown command: ${command}`));
|
|
1428
|
+
console.log(import_chalk7.default.gray('Type "/help" for available commands'));
|
|
1429
|
+
return true;
|
|
1430
|
+
}
|
|
1431
|
+
return false;
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
rl.prompt();
|
|
1435
|
+
rl.on("line", async (input) => {
|
|
1436
|
+
const trimmed = input.trim();
|
|
1437
|
+
if (trimmed.toLowerCase() === "exit" || trimmed.toLowerCase() === "quit") {
|
|
1438
|
+
console.log(import_chalk7.default.green("\n\u{1F44B} Goodbye!"));
|
|
1439
|
+
rl.close();
|
|
1440
|
+
process.exit(0);
|
|
1441
|
+
}
|
|
1442
|
+
if (!trimmed) {
|
|
1443
|
+
rl.prompt();
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
if (trimmed.startsWith("/")) {
|
|
1447
|
+
const shouldContinue = await handleCommand(trimmed);
|
|
1448
|
+
if (!shouldContinue) {
|
|
1449
|
+
console.log(import_chalk7.default.green("\n\u{1F44B} Goodbye!"));
|
|
1450
|
+
rl.close();
|
|
1451
|
+
process.exit(0);
|
|
1452
|
+
}
|
|
1453
|
+
rl.prompt();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
console.log(import_chalk7.default.gray("\nAssistant> ") + import_chalk7.default.gray("Thinking..."));
|
|
1457
|
+
try {
|
|
1458
|
+
const payload = session.buildDispatchPayload(trimmed);
|
|
1459
|
+
const response = await fetch(`${apiUrl}/dispatch`, {
|
|
1460
|
+
method: "POST",
|
|
1461
|
+
headers: {
|
|
1462
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
1463
|
+
"Content-Type": "application/json",
|
|
1464
|
+
"Accept": "text/event-stream"
|
|
1465
|
+
},
|
|
1466
|
+
body: JSON.stringify(payload)
|
|
1467
|
+
});
|
|
1468
|
+
if (!response.ok) {
|
|
1469
|
+
const error = await response.text();
|
|
1470
|
+
throw new Error(`API error: ${response.status} - ${error}`);
|
|
1471
|
+
}
|
|
1472
|
+
process.stdout.write("\x1B[1A\x1B[2K");
|
|
1473
|
+
process.stdout.write(import_chalk7.default.gray("Assistant> "));
|
|
1474
|
+
const fullResponse = await streamHandler.handleStream(response);
|
|
1475
|
+
session.addMessage("assistant", fullResponse);
|
|
1476
|
+
console.log();
|
|
1477
|
+
rl.prompt();
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
process.stdout.write("\x1B[1A\x1B[2K");
|
|
1480
|
+
console.error(import_chalk7.default.red(`Error: ${error.message}`));
|
|
1481
|
+
rl.prompt();
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
rl.on("SIGINT", () => {
|
|
1485
|
+
console.log(import_chalk7.default.green("\n\n\u{1F44B} Goodbye!"));
|
|
1486
|
+
const info = session.getSessionInfo();
|
|
1487
|
+
if (info.messageCount > 0) {
|
|
1488
|
+
console.log(import_chalk7.default.gray('Tip: Use "/save" to save your conversation before exiting'));
|
|
1489
|
+
}
|
|
1490
|
+
process.exit(0);
|
|
1491
|
+
});
|
|
1492
|
+
rl.on("error", (error) => {
|
|
1493
|
+
console.error(import_chalk7.default.red(`Readline error: ${error.message}`));
|
|
1494
|
+
process.exit(1);
|
|
1495
|
+
});
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
// src/commands/config.ts
|
|
1499
|
+
var import_commander7 = require("commander");
|
|
1500
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
1501
|
+
var import_conf2 = __toESM(require("conf"));
|
|
1502
|
+
var import_path3 = __toESM(require("path"));
|
|
1503
|
+
var import_os3 = __toESM(require("os"));
|
|
1504
|
+
var CLI_CONFIG_KEYS = [
|
|
1505
|
+
"apiUrl",
|
|
1506
|
+
"defaultModel",
|
|
1507
|
+
"defaultTemperature",
|
|
1508
|
+
"outputFormat",
|
|
1509
|
+
"streamResponses"
|
|
1510
|
+
];
|
|
1511
|
+
var isCliConfigKey = (value) => {
|
|
1512
|
+
return CLI_CONFIG_KEYS.includes(value);
|
|
1513
|
+
};
|
|
1514
|
+
var configCommand = new import_commander7.Command("config").description("Manage CLI configuration");
|
|
1515
|
+
var config = new import_conf2.default({
|
|
1516
|
+
projectName: "runtype-cli",
|
|
1517
|
+
projectSuffix: "",
|
|
1518
|
+
configName: "config",
|
|
1519
|
+
cwd: import_path3.default.join(import_os3.default.homedir(), ".runtype")
|
|
1520
|
+
});
|
|
1521
|
+
configCommand.command("get [key]").description("Get configuration value").action((key) => {
|
|
1522
|
+
if (key) {
|
|
1523
|
+
if (!isCliConfigKey(key)) {
|
|
1524
|
+
console.log(import_chalk8.default.yellow(`Configuration key '${key}' not found`));
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
const value = config.get(key);
|
|
1528
|
+
if (value !== void 0) {
|
|
1529
|
+
console.log(value);
|
|
1530
|
+
} else {
|
|
1531
|
+
console.log(import_chalk8.default.yellow(`Configuration key '${key}' not found`));
|
|
1532
|
+
}
|
|
1533
|
+
} else {
|
|
1534
|
+
const allConfig = config.store;
|
|
1535
|
+
if (Object.keys(allConfig).length > 0) {
|
|
1536
|
+
console.log(import_chalk8.default.cyan("Current Configuration:"));
|
|
1537
|
+
for (const [k, v] of Object.entries(allConfig)) {
|
|
1538
|
+
console.log(` ${import_chalk8.default.green(k)}: ${v}`);
|
|
1539
|
+
}
|
|
1540
|
+
} else {
|
|
1541
|
+
console.log(import_chalk8.default.gray("No configuration set"));
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
configCommand.command("set <key> <value>").description("Set configuration value").action((key, value) => {
|
|
1546
|
+
if (!isCliConfigKey(key)) {
|
|
1547
|
+
console.log(import_chalk8.default.red(`Invalid configuration key: ${key}`));
|
|
1548
|
+
console.log(import_chalk8.default.gray(`Valid keys: ${CLI_CONFIG_KEYS.join(", ")}`));
|
|
1549
|
+
process.exit(1);
|
|
1550
|
+
}
|
|
1551
|
+
let parsedValue;
|
|
1552
|
+
switch (key) {
|
|
1553
|
+
case "defaultTemperature":
|
|
1554
|
+
{
|
|
1555
|
+
const temperature = parseFloat(value);
|
|
1556
|
+
if (isNaN(temperature) || temperature < 0 || temperature > 1) {
|
|
1557
|
+
console.log(import_chalk8.default.red("Temperature must be a number between 0 and 1"));
|
|
1558
|
+
process.exit(1);
|
|
1559
|
+
}
|
|
1560
|
+
parsedValue = temperature;
|
|
1561
|
+
}
|
|
1562
|
+
break;
|
|
1563
|
+
case "streamResponses":
|
|
1564
|
+
parsedValue = value === "true";
|
|
1565
|
+
break;
|
|
1566
|
+
case "outputFormat":
|
|
1567
|
+
if (!["table", "json", "plain"].includes(value)) {
|
|
1568
|
+
console.log(import_chalk8.default.red("Output format must be: table, json, or plain"));
|
|
1569
|
+
process.exit(1);
|
|
1570
|
+
}
|
|
1571
|
+
parsedValue = value;
|
|
1572
|
+
break;
|
|
1573
|
+
default:
|
|
1574
|
+
parsedValue = value;
|
|
1575
|
+
break;
|
|
1576
|
+
}
|
|
1577
|
+
config.set(key, parsedValue);
|
|
1578
|
+
console.log(import_chalk8.default.green(`Configuration updated: ${key} = ${parsedValue}`));
|
|
1579
|
+
});
|
|
1580
|
+
configCommand.command("reset").description("Reset all configuration to defaults").action(() => {
|
|
1581
|
+
config.clear();
|
|
1582
|
+
console.log(import_chalk8.default.green("Configuration reset to defaults"));
|
|
1583
|
+
});
|
|
1584
|
+
configCommand.command("path").description("Show configuration file path").action(() => {
|
|
1585
|
+
console.log(config.path);
|
|
1586
|
+
});
|
|
1587
|
+
|
|
1588
|
+
// src/index.ts
|
|
1589
|
+
(0, import_dotenv.config)();
|
|
1590
|
+
var program = new import_commander8.Command();
|
|
1591
|
+
program.name(import_shared.BRAND.cliCommand).description(`CLI for ${import_shared.BRAND.name} AI Platform`).version("0.1.0").option("-v, --verbose", "Enable verbose output").option("--api-url <url>", "Override API URL").option("--json", "Output in JSON format");
|
|
1592
|
+
program.addCommand(authCommand);
|
|
1593
|
+
program.addCommand(flowsCommand);
|
|
1594
|
+
program.addCommand(recordsCommand);
|
|
1595
|
+
program.addCommand(promptsCommand);
|
|
1596
|
+
program.addCommand(batchCommand);
|
|
1597
|
+
program.addCommand(talkCommand);
|
|
1598
|
+
program.addCommand(configCommand);
|
|
1599
|
+
program.exitOverride();
|
|
1600
|
+
try {
|
|
1601
|
+
program.parse(process.argv);
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
if (error.code === "commander.missingArgument") {
|
|
1604
|
+
console.error(import_chalk9.default.red(`Error: ${error.message}`));
|
|
1605
|
+
process.exit(1);
|
|
1606
|
+
} else if (error.code === "commander.unknownOption") {
|
|
1607
|
+
console.error(import_chalk9.default.red(`Error: ${error.message}`));
|
|
1608
|
+
process.exit(1);
|
|
1609
|
+
} else if (error.code === "commander.help") {
|
|
1610
|
+
process.exit(0);
|
|
1611
|
+
} else {
|
|
1612
|
+
console.error(import_chalk9.default.red("An unexpected error occurred:"));
|
|
1613
|
+
console.error(error);
|
|
1614
|
+
process.exit(1);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
if (!process.argv.slice(2).length) {
|
|
1618
|
+
try {
|
|
1619
|
+
program.outputHelp();
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
if (error.code === "commander.help") {
|
|
1622
|
+
process.exit(0);
|
|
1623
|
+
} else {
|
|
1624
|
+
throw error;
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
//# sourceMappingURL=index.js.map
|