refineo-cli 0.0.3

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.
@@ -0,0 +1,475 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
5
+
6
+ // src/config.ts
7
+ import { homedir } from "os";
8
+ import { join } from "path";
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
10
+ var CONFIG_DIR = join(homedir(), ".refineo");
11
+ var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
12
+ var API_BASE_URL = process.env.REFINEO_API_URL || "https://refineo.app";
13
+ function ensureConfigDir() {
14
+ if (!existsSync(CONFIG_DIR)) {
15
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
16
+ }
17
+ }
18
+ function loadCredentials() {
19
+ try {
20
+ if (!existsSync(CREDENTIALS_FILE)) {
21
+ return null;
22
+ }
23
+ const data = readFileSync(CREDENTIALS_FILE, "utf-8");
24
+ return JSON.parse(data);
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ function saveCredentials(credentials) {
30
+ ensureConfigDir();
31
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), {
32
+ mode: 384
33
+ // Read/write for owner only
34
+ });
35
+ }
36
+ function clearCredentials() {
37
+ try {
38
+ if (existsSync(CREDENTIALS_FILE)) {
39
+ unlinkSync(CREDENTIALS_FILE);
40
+ }
41
+ } catch {
42
+ }
43
+ }
44
+ function isTokenExpired(credentials) {
45
+ const now = Math.floor(Date.now() / 1e3);
46
+ return credentials.expiresAt <= now + 60;
47
+ }
48
+ function getPlatformInfo() {
49
+ const platform = process.platform;
50
+ const arch = process.arch;
51
+ const nodeVersion = process.version;
52
+ let os = "Unknown";
53
+ if (platform === "darwin") os = "macOS";
54
+ else if (platform === "win32") os = "Windows";
55
+ else if (platform === "linux") os = "Linux";
56
+ return `refineo-cli/0.1.0 (${os}; ${arch}) Node/${nodeVersion}`;
57
+ }
58
+
59
+ // src/api.ts
60
+ var USER_AGENT = getPlatformInfo();
61
+ async function apiRequest(path, options = {}) {
62
+ const credentials = loadCredentials();
63
+ if (!credentials) {
64
+ throw new Error("Not logged in. Run: refineo login");
65
+ }
66
+ let token = credentials.accessToken;
67
+ if (isTokenExpired(credentials)) {
68
+ const refreshed = await refreshToken(credentials.refreshToken);
69
+ if (refreshed) {
70
+ token = refreshed.accessToken;
71
+ } else {
72
+ throw new Error("Session expired. Run: refineo login");
73
+ }
74
+ }
75
+ const response = await fetch(`${API_BASE_URL}${path}`, {
76
+ ...options,
77
+ headers: {
78
+ "Content-Type": "application/json",
79
+ "Authorization": `Bearer ${token}`,
80
+ "User-Agent": USER_AGENT,
81
+ ...options.headers
82
+ }
83
+ });
84
+ if (!response.ok) {
85
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
86
+ throw new Error(error.message || error.error_description || error.error || `HTTP ${response.status}`);
87
+ }
88
+ return response.json();
89
+ }
90
+ async function refreshToken(refreshTokenValue) {
91
+ try {
92
+ const response = await fetch(`${API_BASE_URL}/api/auth/device/refresh`, {
93
+ method: "POST",
94
+ headers: {
95
+ "Content-Type": "application/json",
96
+ "User-Agent": USER_AGENT
97
+ },
98
+ body: JSON.stringify({
99
+ refresh_token: refreshTokenValue,
100
+ grant_type: "refresh_token"
101
+ })
102
+ });
103
+ if (!response.ok) {
104
+ return null;
105
+ }
106
+ const data = await response.json();
107
+ const oldCredentials = loadCredentials();
108
+ const credentials = {
109
+ accessToken: data.access_token,
110
+ refreshToken: data.refresh_token,
111
+ expiresAt: data.expires_at,
112
+ user: oldCredentials?.user || { email: "", tier: "" }
113
+ };
114
+ saveCredentials(credentials);
115
+ return credentials;
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
120
+ async function startDeviceCodeFlow() {
121
+ const response = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
122
+ method: "POST",
123
+ headers: {
124
+ "Content-Type": "application/json",
125
+ "User-Agent": USER_AGENT
126
+ }
127
+ });
128
+ if (!response.ok) {
129
+ throw new Error("Failed to start login flow");
130
+ }
131
+ return response.json();
132
+ }
133
+ async function pollForToken(deviceCode, interval, expiresIn, onPoll) {
134
+ const startTime = Date.now();
135
+ const timeout = expiresIn * 1e3;
136
+ while (Date.now() - startTime < timeout) {
137
+ onPoll?.();
138
+ const response = await fetch(`${API_BASE_URL}/api/auth/device/token`, {
139
+ method: "POST",
140
+ headers: {
141
+ "Content-Type": "application/json",
142
+ "User-Agent": USER_AGENT
143
+ },
144
+ body: JSON.stringify({
145
+ device_code: deviceCode,
146
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
147
+ })
148
+ });
149
+ const data = await response.json();
150
+ if (response.ok) {
151
+ const tokenData = data;
152
+ const credentials = {
153
+ accessToken: tokenData.access_token,
154
+ refreshToken: tokenData.refresh_token,
155
+ expiresAt: tokenData.expires_at,
156
+ user: tokenData.user || { email: "", tier: "" }
157
+ };
158
+ saveCredentials(credentials);
159
+ return credentials;
160
+ }
161
+ const error = data;
162
+ if (error.error === "authorization_pending") {
163
+ await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
164
+ continue;
165
+ }
166
+ if (error.error === "slow_down") {
167
+ await new Promise((resolve) => setTimeout(resolve, (interval + 5) * 1e3));
168
+ continue;
169
+ }
170
+ if (error.error === "access_denied") {
171
+ throw new Error(
172
+ error.error_description || "Access denied. CLI requires Pro or Ultra subscription."
173
+ );
174
+ }
175
+ if (error.error === "expired_token") {
176
+ throw new Error("Login timed out. Please try again.");
177
+ }
178
+ throw new Error(error.error_description || error.error || "Login failed");
179
+ }
180
+ throw new Error("Login timed out. Please try again.");
181
+ }
182
+ async function humanize(text, model = "enhanced") {
183
+ const result = await apiRequest("/api/humanize", {
184
+ method: "POST",
185
+ body: JSON.stringify({
186
+ text,
187
+ model: model === "standard" ? "BALANCE" : "ENHANCED"
188
+ })
189
+ });
190
+ return {
191
+ humanizedText: result.data.humanizedText,
192
+ wordCount: result.data.wordCount,
193
+ model: result.data.model
194
+ };
195
+ }
196
+ async function getUsage() {
197
+ const result = await apiRequest("/api/usage");
198
+ return {
199
+ tier: result.tier,
200
+ used: result.used,
201
+ limit: result.limit,
202
+ remaining: result.remaining,
203
+ resetDate: result.resetDate,
204
+ wordLimit: result.wordLimit,
205
+ rateLimit: result.rateLimit
206
+ };
207
+ }
208
+
209
+ // src/cli.ts
210
+ var VERSION = "0.1.0";
211
+ var colors = {
212
+ reset: "\x1B[0m",
213
+ bold: "\x1B[1m",
214
+ dim: "\x1B[2m",
215
+ green: "\x1B[32m",
216
+ yellow: "\x1B[33m",
217
+ blue: "\x1B[34m",
218
+ cyan: "\x1B[36m",
219
+ red: "\x1B[31m"
220
+ };
221
+ function print(message) {
222
+ console.log(message);
223
+ }
224
+ function printError(message) {
225
+ console.error(`${colors.red}Error:${colors.reset} ${message}`);
226
+ }
227
+ function printSuccess(message) {
228
+ print(`${colors.green}\u2713${colors.reset} ${message}`);
229
+ }
230
+ async function openBrowser(url) {
231
+ const { exec } = await import("child_process");
232
+ const { promisify } = await import("util");
233
+ const execAsync = promisify(exec);
234
+ const platform = process.platform;
235
+ let command;
236
+ if (platform === "darwin") {
237
+ command = `open "${url}"`;
238
+ } else if (platform === "win32") {
239
+ command = `start "" "${url}"`;
240
+ } else {
241
+ command = `xdg-open "${url}"`;
242
+ }
243
+ try {
244
+ await execAsync(command);
245
+ } catch {
246
+ print(`Please open this URL in your browser: ${url}`);
247
+ }
248
+ }
249
+ async function loginCommand() {
250
+ const existing = loadCredentials();
251
+ if (existing) {
252
+ print(`Already logged in as ${colors.cyan}${existing.user.email}${colors.reset}`);
253
+ print(`Tier: ${colors.bold}${existing.user.tier}${colors.reset}`);
254
+ print(`
255
+ Run ${colors.dim}refineo logout${colors.reset} to switch accounts.`);
256
+ return;
257
+ }
258
+ print(`${colors.bold}Refineo CLI Login${colors.reset}
259
+ `);
260
+ try {
261
+ const deviceCode = await startDeviceCodeFlow();
262
+ print(`Your code: ${colors.bold}${colors.cyan}${deviceCode.user_code}${colors.reset}
263
+ `);
264
+ print(`Opening browser to authorize...`);
265
+ print(`${colors.dim}${deviceCode.verification_uri_complete}${colors.reset}
266
+ `);
267
+ await openBrowser(deviceCode.verification_uri_complete);
268
+ print(`Waiting for authorization...`);
269
+ let dots = 0;
270
+ const credentials = await pollForToken(
271
+ deviceCode.device_code,
272
+ deviceCode.interval,
273
+ deviceCode.expires_in,
274
+ () => {
275
+ process.stdout.write(`\rWaiting for authorization${".".repeat(dots++ % 3 + 1)} `);
276
+ }
277
+ );
278
+ process.stdout.write("\r \r");
279
+ printSuccess(`Logged in as ${colors.cyan}${credentials.user.email}${colors.reset}`);
280
+ print(`Tier: ${colors.bold}${credentials.user.tier}${colors.reset}`);
281
+ } catch (error) {
282
+ printError(error instanceof Error ? error.message : "Login failed");
283
+ process.exit(1);
284
+ }
285
+ }
286
+ function logoutCommand() {
287
+ const credentials = loadCredentials();
288
+ if (!credentials) {
289
+ print("Not logged in.");
290
+ return;
291
+ }
292
+ clearCredentials();
293
+ printSuccess(`Logged out from ${credentials.user.email}`);
294
+ }
295
+ async function statsCommand() {
296
+ const credentials = loadCredentials();
297
+ if (!credentials) {
298
+ printError("Not logged in. Run: refineo login");
299
+ process.exit(1);
300
+ }
301
+ try {
302
+ const usage = await getUsage();
303
+ print(`${colors.bold}Refineo Usage${colors.reset}
304
+ `);
305
+ print(`Account: ${colors.cyan}${credentials.user.email}${colors.reset}`);
306
+ print(`Plan: ${colors.bold}${usage.tier}${colors.reset}`);
307
+ print("");
308
+ if (usage.limit === -1) {
309
+ print(`Requests: ${colors.green}Unlimited${colors.reset}`);
310
+ if (usage.rateLimit) {
311
+ print(`Rate limit: ${usage.rateLimit} requests/hour`);
312
+ }
313
+ } else {
314
+ const percentage = Math.round(usage.used / usage.limit * 100);
315
+ const color = percentage >= 90 ? colors.red : percentage >= 70 ? colors.yellow : colors.green;
316
+ print(`Requests: ${color}${usage.used}${colors.reset} / ${usage.limit} (${percentage}%)`);
317
+ print(`Remaining: ${usage.remaining}`);
318
+ }
319
+ print(`Word limit: ${usage.wordLimit} words/request`);
320
+ if (usage.resetDate) {
321
+ print(`Resets: ${usage.resetDate}`);
322
+ }
323
+ } catch (error) {
324
+ printError(error instanceof Error ? error.message : "Failed to get usage");
325
+ process.exit(1);
326
+ }
327
+ }
328
+ async function humanizeCommand(args) {
329
+ const credentials = loadCredentials();
330
+ if (!credentials) {
331
+ printError("Not logged in. Run: refineo login");
332
+ process.exit(1);
333
+ }
334
+ let text = "";
335
+ let model = "enhanced";
336
+ let inputFile = "";
337
+ let outputFile = "";
338
+ for (let i = 0; i < args.length; i++) {
339
+ const arg = args[i];
340
+ if (arg === "--model" || arg === "-m") {
341
+ const value = args[++i];
342
+ if (value === "standard" || value === "enhanced") {
343
+ model = value;
344
+ } else {
345
+ printError('Model must be "standard" or "enhanced"');
346
+ process.exit(1);
347
+ }
348
+ } else if (arg === "--file" || arg === "-f") {
349
+ inputFile = args[++i];
350
+ } else if (arg === "--output" || arg === "-o") {
351
+ outputFile = args[++i];
352
+ } else if (!arg.startsWith("-")) {
353
+ text = arg;
354
+ }
355
+ }
356
+ if (inputFile) {
357
+ if (!existsSync2(inputFile)) {
358
+ printError(`File not found: ${inputFile}`);
359
+ process.exit(1);
360
+ }
361
+ text = readFileSync2(inputFile, "utf-8");
362
+ }
363
+ if (!text) {
364
+ if (process.stdin.isTTY) {
365
+ printError('No text provided. Usage: refineo humanize "your text" or echo "text" | refineo humanize');
366
+ process.exit(1);
367
+ }
368
+ const chunks = [];
369
+ for await (const chunk of process.stdin) {
370
+ chunks.push(chunk);
371
+ }
372
+ text = Buffer.concat(chunks).toString("utf-8").trim();
373
+ }
374
+ if (!text) {
375
+ printError("No text provided");
376
+ process.exit(1);
377
+ }
378
+ try {
379
+ const result = await humanize(text, model);
380
+ if (outputFile) {
381
+ const { writeFileSync: writeFileSync2 } = await import("fs");
382
+ writeFileSync2(outputFile, result.humanizedText);
383
+ printSuccess(`Output written to ${outputFile}`);
384
+ } else {
385
+ print(result.humanizedText);
386
+ }
387
+ } catch (error) {
388
+ printError(error instanceof Error ? error.message : "Humanization failed");
389
+ process.exit(1);
390
+ }
391
+ }
392
+ function helpCommand() {
393
+ print(`
394
+ ${colors.bold}Refineo CLI${colors.reset} - AI Text Humanizer
395
+ Version ${VERSION}
396
+
397
+ ${colors.bold}Usage:${colors.reset}
398
+ refineo <command> [options]
399
+
400
+ ${colors.bold}Commands:${colors.reset}
401
+ login Authenticate with your Refineo account
402
+ logout Clear stored credentials
403
+ stats Show usage statistics
404
+ humanize <text> Humanize AI-generated text
405
+
406
+ ${colors.bold}Humanize Options:${colors.reset}
407
+ -m, --model <model> Model: "standard" or "enhanced" (default: enhanced)
408
+ -f, --file <path> Read input from file
409
+ -o, --output <path> Write output to file
410
+
411
+ ${colors.bold}Examples:${colors.reset}
412
+ ${colors.dim}# Login to your account${colors.reset}
413
+ refineo login
414
+
415
+ ${colors.dim}# Humanize text directly${colors.reset}
416
+ refineo humanize "The results indicate a significant correlation."
417
+
418
+ ${colors.dim}# Use standard model${colors.reset}
419
+ refineo humanize "Text here" --model standard
420
+
421
+ ${colors.dim}# Read from file${colors.reset}
422
+ refineo humanize --file input.txt --output output.txt
423
+
424
+ ${colors.dim}# Pipe from stdin${colors.reset}
425
+ echo "AI-generated text" | refineo humanize
426
+
427
+ ${colors.dim}# Check usage${colors.reset}
428
+ refineo stats
429
+
430
+ ${colors.bold}More Info:${colors.reset}
431
+ https://refineo.app/docs/cli
432
+ `);
433
+ }
434
+ function versionCommand() {
435
+ print(`refineo ${VERSION}`);
436
+ }
437
+ async function main() {
438
+ const args = process.argv.slice(2);
439
+ const command = args[0];
440
+ switch (command) {
441
+ case "login":
442
+ await loginCommand();
443
+ break;
444
+ case "logout":
445
+ logoutCommand();
446
+ break;
447
+ case "stats":
448
+ await statsCommand();
449
+ break;
450
+ case "humanize":
451
+ await humanizeCommand(args.slice(1));
452
+ break;
453
+ case "help":
454
+ case "--help":
455
+ case "-h":
456
+ helpCommand();
457
+ break;
458
+ case "version":
459
+ case "--version":
460
+ case "-v":
461
+ versionCommand();
462
+ break;
463
+ case void 0:
464
+ helpCommand();
465
+ break;
466
+ default:
467
+ printError(`Unknown command: ${command}`);
468
+ print(`Run ${colors.dim}refineo help${colors.reset} for usage.`);
469
+ process.exit(1);
470
+ }
471
+ }
472
+ main().catch((error) => {
473
+ printError(error instanceof Error ? error.message : "Unknown error");
474
+ process.exit(1);
475
+ });
@@ -0,0 +1,23 @@
1
+ import type { Credentials } from './types.js';
2
+ export declare const API_BASE_URL: string;
3
+ /**
4
+ * Load credentials from disk
5
+ */
6
+ export declare function loadCredentials(): Credentials | null;
7
+ /**
8
+ * Save credentials to disk
9
+ */
10
+ export declare function saveCredentials(credentials: Credentials): void;
11
+ /**
12
+ * Clear credentials from disk
13
+ */
14
+ export declare function clearCredentials(): void;
15
+ /**
16
+ * Check if credentials are expired (with 1 minute buffer)
17
+ */
18
+ export declare function isTokenExpired(credentials: Credentials): boolean;
19
+ /**
20
+ * Get current platform info for User-Agent
21
+ */
22
+ export declare function getPlatformInfo(): string;
23
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,eAAO,MAAM,YAAY,QAAuD,CAAC;AAWjF;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAUpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAK9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAQvC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAGhE;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC"}
@@ -0,0 +1,60 @@
1
+ // src/config.ts
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
5
+ var CONFIG_DIR = join(homedir(), ".refineo");
6
+ var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
7
+ var API_BASE_URL = process.env.REFINEO_API_URL || "https://refineo.app";
8
+ function ensureConfigDir() {
9
+ if (!existsSync(CONFIG_DIR)) {
10
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
11
+ }
12
+ }
13
+ function loadCredentials() {
14
+ try {
15
+ if (!existsSync(CREDENTIALS_FILE)) {
16
+ return null;
17
+ }
18
+ const data = readFileSync(CREDENTIALS_FILE, "utf-8");
19
+ return JSON.parse(data);
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+ function saveCredentials(credentials) {
25
+ ensureConfigDir();
26
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), {
27
+ mode: 384
28
+ // Read/write for owner only
29
+ });
30
+ }
31
+ function clearCredentials() {
32
+ try {
33
+ if (existsSync(CREDENTIALS_FILE)) {
34
+ unlinkSync(CREDENTIALS_FILE);
35
+ }
36
+ } catch {
37
+ }
38
+ }
39
+ function isTokenExpired(credentials) {
40
+ const now = Math.floor(Date.now() / 1e3);
41
+ return credentials.expiresAt <= now + 60;
42
+ }
43
+ function getPlatformInfo() {
44
+ const platform = process.platform;
45
+ const arch = process.arch;
46
+ const nodeVersion = process.version;
47
+ let os = "Unknown";
48
+ if (platform === "darwin") os = "macOS";
49
+ else if (platform === "win32") os = "Windows";
50
+ else if (platform === "linux") os = "Linux";
51
+ return `refineo-cli/0.1.0 (${os}; ${arch}) Node/${nodeVersion}`;
52
+ }
53
+ export {
54
+ API_BASE_URL,
55
+ clearCredentials,
56
+ getPlatformInfo,
57
+ isTokenExpired,
58
+ loadCredentials,
59
+ saveCredentials
60
+ };
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './config.js';
3
+ export * from './api.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}