@reminix/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/README.md +1 -1
- package/dist/commands/agents.d.ts +6 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +195 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +65 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +6 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +15 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/projects.d.ts +6 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +104 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/version.d.ts +1 -1
- package/dist/commands/version.js +1 -1
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +21 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +25 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +52 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/api-types.d.ts +518 -0
- package/dist/lib/api-types.d.ts.map +1 -0
- package/dist/lib/api-types.js +6 -0
- package/dist/lib/api-types.js.map +1 -0
- package/dist/lib/auth-server.d.ts +21 -0
- package/dist/lib/auth-server.d.ts.map +1 -0
- package/dist/lib/auth-server.js +234 -0
- package/dist/lib/auth-server.js.map +1 -0
- package/dist/lib/credentials.d.ts +32 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +86 -0
- package/dist/lib/credentials.js.map +1 -0
- package/package.json +8 -4
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* HTML page shown after successful authentication
|
|
4
|
+
*/
|
|
5
|
+
const SUCCESS_HTML = `
|
|
6
|
+
<!DOCTYPE html>
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="UTF-8">
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
11
|
+
<title>Reminix CLI - Authenticated</title>
|
|
12
|
+
<style>
|
|
13
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
16
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
color: #fff;
|
|
22
|
+
}
|
|
23
|
+
.container {
|
|
24
|
+
text-align: center;
|
|
25
|
+
padding: 2rem;
|
|
26
|
+
}
|
|
27
|
+
.checkmark {
|
|
28
|
+
width: 80px;
|
|
29
|
+
height: 80px;
|
|
30
|
+
margin: 0 auto 1.5rem;
|
|
31
|
+
background: #10b981;
|
|
32
|
+
border-radius: 50%;
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
animation: pop 0.3s ease-out;
|
|
37
|
+
}
|
|
38
|
+
.checkmark svg {
|
|
39
|
+
width: 40px;
|
|
40
|
+
height: 40px;
|
|
41
|
+
stroke: white;
|
|
42
|
+
stroke-width: 3;
|
|
43
|
+
fill: none;
|
|
44
|
+
}
|
|
45
|
+
h1 {
|
|
46
|
+
font-size: 1.5rem;
|
|
47
|
+
font-weight: 600;
|
|
48
|
+
margin-bottom: 0.5rem;
|
|
49
|
+
}
|
|
50
|
+
p {
|
|
51
|
+
color: #94a3b8;
|
|
52
|
+
font-size: 1rem;
|
|
53
|
+
}
|
|
54
|
+
@keyframes pop {
|
|
55
|
+
0% { transform: scale(0); }
|
|
56
|
+
80% { transform: scale(1.1); }
|
|
57
|
+
100% { transform: scale(1); }
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<div class="container">
|
|
63
|
+
<div class="checkmark">
|
|
64
|
+
<svg viewBox="0 0 24 24">
|
|
65
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
66
|
+
</svg>
|
|
67
|
+
</div>
|
|
68
|
+
<h1>Authentication successful!</h1>
|
|
69
|
+
<p>You can close this tab and return to your terminal.</p>
|
|
70
|
+
</div>
|
|
71
|
+
</body>
|
|
72
|
+
</html>
|
|
73
|
+
`;
|
|
74
|
+
/**
|
|
75
|
+
* HTML page shown when there's an error
|
|
76
|
+
*/
|
|
77
|
+
const ERROR_HTML = `
|
|
78
|
+
<!DOCTYPE html>
|
|
79
|
+
<html lang="en">
|
|
80
|
+
<head>
|
|
81
|
+
<meta charset="UTF-8">
|
|
82
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
83
|
+
<title>Reminix CLI - Error</title>
|
|
84
|
+
<style>
|
|
85
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
86
|
+
body {
|
|
87
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
88
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
89
|
+
min-height: 100vh;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
color: #fff;
|
|
94
|
+
}
|
|
95
|
+
.container {
|
|
96
|
+
text-align: center;
|
|
97
|
+
padding: 2rem;
|
|
98
|
+
}
|
|
99
|
+
.error-icon {
|
|
100
|
+
width: 80px;
|
|
101
|
+
height: 80px;
|
|
102
|
+
margin: 0 auto 1.5rem;
|
|
103
|
+
background: #ef4444;
|
|
104
|
+
border-radius: 50%;
|
|
105
|
+
display: flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
}
|
|
109
|
+
.error-icon svg {
|
|
110
|
+
width: 40px;
|
|
111
|
+
height: 40px;
|
|
112
|
+
stroke: white;
|
|
113
|
+
stroke-width: 3;
|
|
114
|
+
fill: none;
|
|
115
|
+
}
|
|
116
|
+
h1 {
|
|
117
|
+
font-size: 1.5rem;
|
|
118
|
+
font-weight: 600;
|
|
119
|
+
margin-bottom: 0.5rem;
|
|
120
|
+
}
|
|
121
|
+
p {
|
|
122
|
+
color: #94a3b8;
|
|
123
|
+
font-size: 1rem;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
126
|
+
</head>
|
|
127
|
+
<body>
|
|
128
|
+
<div class="container">
|
|
129
|
+
<div class="error-icon">
|
|
130
|
+
<svg viewBox="0 0 24 24">
|
|
131
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
132
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
133
|
+
</svg>
|
|
134
|
+
</div>
|
|
135
|
+
<h1>Authentication failed</h1>
|
|
136
|
+
<p>Please try again from your terminal.</p>
|
|
137
|
+
</div>
|
|
138
|
+
</body>
|
|
139
|
+
</html>
|
|
140
|
+
`;
|
|
141
|
+
/**
|
|
142
|
+
* Start the auth server and return both the port and the result promise
|
|
143
|
+
* This allows the caller to get the port immediately and wait for the result separately
|
|
144
|
+
*/
|
|
145
|
+
export function createAuthServer(expectedState, timeoutMs = 5 * 60 * 1000) {
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
let callbackResolve;
|
|
148
|
+
let callbackReject;
|
|
149
|
+
const callbackPromise = new Promise((res, rej) => {
|
|
150
|
+
callbackResolve = res;
|
|
151
|
+
callbackReject = rej;
|
|
152
|
+
});
|
|
153
|
+
const server = http.createServer((req, res) => {
|
|
154
|
+
// Only handle GET requests to /callback
|
|
155
|
+
if (!req.url?.startsWith('/callback')) {
|
|
156
|
+
res.writeHead(404);
|
|
157
|
+
res.end('Not found');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Parse query parameters using WHATWG URL API
|
|
161
|
+
const parsedUrl = new URL(req.url, `http://localhost`);
|
|
162
|
+
const token = parsedUrl.searchParams.get('token') ?? undefined;
|
|
163
|
+
const state = parsedUrl.searchParams.get('state') ?? undefined;
|
|
164
|
+
const email = parsedUrl.searchParams.get('email') ?? undefined;
|
|
165
|
+
const error = parsedUrl.searchParams.get('error') ?? undefined;
|
|
166
|
+
// Handle error from web app
|
|
167
|
+
if (error) {
|
|
168
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
169
|
+
res.end(ERROR_HTML);
|
|
170
|
+
cleanup();
|
|
171
|
+
callbackReject(new Error(`Authentication failed: ${error}`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Validate required parameters
|
|
175
|
+
if (!token || !state || !email) {
|
|
176
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
177
|
+
res.end(ERROR_HTML);
|
|
178
|
+
cleanup();
|
|
179
|
+
callbackReject(new Error('Missing token, state, or email in callback'));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Verify state matches (CSRF protection)
|
|
183
|
+
if (state !== expectedState) {
|
|
184
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
185
|
+
res.end(ERROR_HTML);
|
|
186
|
+
cleanup();
|
|
187
|
+
callbackReject(new Error('State mismatch - possible CSRF attack'));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Success!
|
|
191
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
192
|
+
res.end(SUCCESS_HTML);
|
|
193
|
+
cleanup();
|
|
194
|
+
callbackResolve({ token, state, email });
|
|
195
|
+
});
|
|
196
|
+
// Handle server errors
|
|
197
|
+
server.on('error', (err) => {
|
|
198
|
+
cleanup();
|
|
199
|
+
reject(err);
|
|
200
|
+
});
|
|
201
|
+
// Set up timeout
|
|
202
|
+
const timeoutId = setTimeout(() => {
|
|
203
|
+
cleanup();
|
|
204
|
+
callbackReject(new Error('Authentication timed out. Please try again.'));
|
|
205
|
+
}, timeoutMs);
|
|
206
|
+
// Cleanup function
|
|
207
|
+
function cleanup() {
|
|
208
|
+
clearTimeout(timeoutId);
|
|
209
|
+
server.close();
|
|
210
|
+
}
|
|
211
|
+
// Start server on random available port (port 0)
|
|
212
|
+
server.listen(0, '127.0.0.1', () => {
|
|
213
|
+
const address = server.address();
|
|
214
|
+
if (typeof address === 'object' && address) {
|
|
215
|
+
resolve({
|
|
216
|
+
port: address.port,
|
|
217
|
+
waitForCallback: () => callbackPromise,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
reject(new Error('Failed to start auth server'));
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Generate a random state string for CSRF protection
|
|
228
|
+
*/
|
|
229
|
+
export function generateState() {
|
|
230
|
+
const array = new Uint8Array(32);
|
|
231
|
+
crypto.getRandomValues(array);
|
|
232
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=auth-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-server.js","sourceRoot":"","sources":["../../src/lib/auth-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials stored locally for CLI authentication
|
|
3
|
+
*/
|
|
4
|
+
export interface Credentials {
|
|
5
|
+
token: string;
|
|
6
|
+
email: string;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Save credentials to the local filesystem
|
|
11
|
+
* Creates the config directory if it doesn't exist
|
|
12
|
+
*/
|
|
13
|
+
export declare function saveCredentials(token: string, email: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Load credentials from the local filesystem
|
|
16
|
+
* Returns null if no credentials exist or file is invalid
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadCredentials(): Credentials | null;
|
|
19
|
+
/**
|
|
20
|
+
* Clear stored credentials
|
|
21
|
+
* Removes the credentials file if it exists
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearCredentials(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Check if user is logged in (has valid credentials)
|
|
26
|
+
*/
|
|
27
|
+
export declare function isLoggedIn(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Get the credentials directory path (for display purposes)
|
|
30
|
+
*/
|
|
31
|
+
export declare function getCredentialsDirPath(): string;
|
|
32
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAqBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAmBlE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAoBpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
/**
|
|
5
|
+
* Get the path to the credentials directory
|
|
6
|
+
* Uses XDG_CONFIG_HOME if set, otherwise ~/.config/reminix
|
|
7
|
+
*/
|
|
8
|
+
function getCredentialsDir() {
|
|
9
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
10
|
+
if (xdgConfigHome) {
|
|
11
|
+
return path.join(xdgConfigHome, 'reminix');
|
|
12
|
+
}
|
|
13
|
+
return path.join(os.homedir(), '.config', 'reminix');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get the path to the credentials file
|
|
17
|
+
*/
|
|
18
|
+
function getCredentialsPath() {
|
|
19
|
+
return path.join(getCredentialsDir(), 'credentials.json');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Save credentials to the local filesystem
|
|
23
|
+
* Creates the config directory if it doesn't exist
|
|
24
|
+
*/
|
|
25
|
+
export function saveCredentials(token, email) {
|
|
26
|
+
const dir = getCredentialsDir();
|
|
27
|
+
const filePath = getCredentialsPath();
|
|
28
|
+
// Create directory if it doesn't exist
|
|
29
|
+
if (!fs.existsSync(dir)) {
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); // Only owner can access
|
|
31
|
+
}
|
|
32
|
+
const credentials = {
|
|
33
|
+
token,
|
|
34
|
+
email,
|
|
35
|
+
createdAt: new Date().toISOString(),
|
|
36
|
+
};
|
|
37
|
+
// Write credentials file with restricted permissions
|
|
38
|
+
fs.writeFileSync(filePath, JSON.stringify(credentials, null, 2), {
|
|
39
|
+
mode: 0o600, // Only owner can read/write
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load credentials from the local filesystem
|
|
44
|
+
* Returns null if no credentials exist or file is invalid
|
|
45
|
+
*/
|
|
46
|
+
export function loadCredentials() {
|
|
47
|
+
const filePath = getCredentialsPath();
|
|
48
|
+
if (!fs.existsSync(filePath)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
53
|
+
const credentials = JSON.parse(content);
|
|
54
|
+
// Basic validation
|
|
55
|
+
if (!credentials.token || !credentials.email) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return credentials;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Clear stored credentials
|
|
66
|
+
* Removes the credentials file if it exists
|
|
67
|
+
*/
|
|
68
|
+
export function clearCredentials() {
|
|
69
|
+
const filePath = getCredentialsPath();
|
|
70
|
+
if (fs.existsSync(filePath)) {
|
|
71
|
+
fs.unlinkSync(filePath);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if user is logged in (has valid credentials)
|
|
76
|
+
*/
|
|
77
|
+
export function isLoggedIn() {
|
|
78
|
+
return loadCredentials() !== null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the credentials directory path (for display purposes)
|
|
82
|
+
*/
|
|
83
|
+
export function getCredentialsDirPath() {
|
|
84
|
+
return getCredentialsDir();
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAW9B;;;GAGG;AACH,SAAS,iBAAiB;IACxB,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAClD,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB;IACzB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,KAAa;IAC1D,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IAEtC,uCAAuC;IACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,wBAAwB;IAC/E,CAAC;IAED,MAAM,WAAW,GAAgB;QAC/B,KAAK;QACL,KAAK;QACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IAEF,qDAAqD;IACrD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC/D,IAAI,EAAE,KAAK,EAAE,4BAA4B;KAC1C,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IAEtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC;QAEvD,mBAAmB;QACnB,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IAEtC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,eAAe,EAAE,KAAK,IAAI,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,iBAAiB,EAAE,CAAC;AAC7B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reminix/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Reminix CLI - Command-line interface for Reminix",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": {
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"homepage": "https://reminix.com",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/reminix-ai/
|
|
21
|
+
"url": "git+https://github.com/reminix-ai/cli.git",
|
|
22
22
|
"directory": "packages/cli"
|
|
23
23
|
},
|
|
24
24
|
"bugs": {
|
|
25
|
-
"url": "https://github.com/reminix-ai/
|
|
25
|
+
"url": "https://github.com/reminix-ai/cli/issues"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=20"
|
|
@@ -48,10 +48,13 @@
|
|
|
48
48
|
"LICENSE"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"commander": "^14.0.2"
|
|
51
|
+
"commander": "^14.0.2",
|
|
52
|
+
"open": "^11.0.0",
|
|
53
|
+
"openapi-fetch": "^0.15.0"
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
56
|
"@types/node": "^25.0.9",
|
|
57
|
+
"openapi-typescript": "^7.10.1",
|
|
55
58
|
"tsx": "^4.21.0",
|
|
56
59
|
"typescript": "^5.9.3",
|
|
57
60
|
"vitest": "^4.0.17"
|
|
@@ -60,6 +63,7 @@
|
|
|
60
63
|
"build": "tsc",
|
|
61
64
|
"clean": "rm -rf dist",
|
|
62
65
|
"dev": "tsx src/index.ts",
|
|
66
|
+
"generate:api": "openapi-typescript ${REMINIX_API_URL:-https://api.reminix.com}/v1/openapi.json -o src/lib/api-types.ts",
|
|
63
67
|
"test": "vitest run",
|
|
64
68
|
"test:watch": "vitest"
|
|
65
69
|
}
|