@vreko/cli 3.0.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/LICENSE +201 -0
- package/README.md +45 -0
- package/dist/CeremonyView-LQS7FTMK.js +134 -0
- package/dist/CeremonyView-LQS7FTMK.js.map +1 -0
- package/dist/InitApp-7K5DTYSW.js +1479 -0
- package/dist/InitApp-7K5DTYSW.js.map +1 -0
- package/dist/SkippedTestDetector-PJSKSOZR.js +7 -0
- package/dist/SkippedTestDetector-PJSKSOZR.js.map +1 -0
- package/dist/TuiApp-FX23XQBK.js +8 -0
- package/dist/TuiApp-FX23XQBK.js.map +1 -0
- package/dist/analysis-ABEO6RTN.js +8 -0
- package/dist/analysis-ABEO6RTN.js.map +1 -0
- package/dist/auth-XNBEBNPY.js +7669 -0
- package/dist/auth-XNBEBNPY.js.map +1 -0
- package/dist/ceremony-M7CXVBVA.js +45 -0
- package/dist/ceremony-M7CXVBVA.js.map +1 -0
- package/dist/chunk-A3QSZJPD.js +3147 -0
- package/dist/chunk-A3QSZJPD.js.map +1 -0
- package/dist/chunk-ASGZ5B6C.js +3969 -0
- package/dist/chunk-ASGZ5B6C.js.map +1 -0
- package/dist/chunk-DMXC2JTC.js +58 -0
- package/dist/chunk-DMXC2JTC.js.map +1 -0
- package/dist/chunk-EEBSK2IH.js +161 -0
- package/dist/chunk-EEBSK2IH.js.map +1 -0
- package/dist/chunk-EWOJGXRX.js +22 -0
- package/dist/chunk-EWOJGXRX.js.map +1 -0
- package/dist/chunk-F7GEJLP7.js +2389 -0
- package/dist/chunk-F7GEJLP7.js.map +1 -0
- package/dist/chunk-GOYL3F4T.js +605 -0
- package/dist/chunk-GOYL3F4T.js.map +1 -0
- package/dist/chunk-GRMRYWYS.js +17 -0
- package/dist/chunk-GRMRYWYS.js.map +1 -0
- package/dist/chunk-GSUGROXB.js +1951 -0
- package/dist/chunk-GSUGROXB.js.map +1 -0
- package/dist/chunk-H7773ONB.js +50 -0
- package/dist/chunk-H7773ONB.js.map +1 -0
- package/dist/chunk-HFQHU5LC.js +445 -0
- package/dist/chunk-HFQHU5LC.js.map +1 -0
- package/dist/chunk-IVHUBLJD.js +318 -0
- package/dist/chunk-IVHUBLJD.js.map +1 -0
- package/dist/chunk-KJWKY4L4.js +14 -0
- package/dist/chunk-KJWKY4L4.js.map +1 -0
- package/dist/chunk-MJVY2XUN.js +1793 -0
- package/dist/chunk-MJVY2XUN.js.map +1 -0
- package/dist/chunk-QWZVCJII.js +1797 -0
- package/dist/chunk-QWZVCJII.js.map +1 -0
- package/dist/chunk-VTSNRV3V.js +3237 -0
- package/dist/chunk-VTSNRV3V.js.map +1 -0
- package/dist/chunk-W5B4GTXR.js +1466 -0
- package/dist/chunk-W5B4GTXR.js.map +1 -0
- package/dist/chunk-WZEZLVOW.js +4995 -0
- package/dist/chunk-WZEZLVOW.js.map +1 -0
- package/dist/chunk-YPTTIXKC.js +199 -0
- package/dist/chunk-YPTTIXKC.js.map +1 -0
- package/dist/chunk-Z55UGM6X.js +6360 -0
- package/dist/chunk-Z55UGM6X.js.map +1 -0
- package/dist/chunk-ZIIRQODJ.js +110 -0
- package/dist/chunk-ZIIRQODJ.js.map +1 -0
- package/dist/chunk-ZSUQ4FMB.js +77 -0
- package/dist/chunk-ZSUQ4FMB.js.map +1 -0
- package/dist/client-JMTSZS3V.js +10 -0
- package/dist/client-JMTSZS3V.js.map +1 -0
- package/dist/deprecated-snap.js +19 -0
- package/dist/deprecated-snap.js.map +1 -0
- package/dist/dist-2KWBZFLA.js +14 -0
- package/dist/dist-2KWBZFLA.js.map +1 -0
- package/dist/dist-5ZYKNNU3.js +7 -0
- package/dist/dist-5ZYKNNU3.js.map +1 -0
- package/dist/dist-CP3RFHPI.js +11 -0
- package/dist/dist-CP3RFHPI.js.map +1 -0
- package/dist/gecko-53ITAGG6.js +56 -0
- package/dist/gecko-53ITAGG6.js.map +1 -0
- package/dist/guards-QAFC64NO.js +7 -0
- package/dist/guards-QAFC64NO.js.map +1 -0
- package/dist/index.js +57785 -0
- package/dist/index.js.map +1 -0
- package/dist/init-command-246JIVXM.js +7 -0
- package/dist/init-command-246JIVXM.js.map +1 -0
- package/dist/init-core-KAI7LCXZ.js +12 -0
- package/dist/init-core-KAI7LCXZ.js.map +1 -0
- package/dist/init-scan-RZNYDTUV.js +1919 -0
- package/dist/init-scan-RZNYDTUV.js.map +1 -0
- package/dist/local-service-adapter-6KNN6WQL.js +8 -0
- package/dist/local-service-adapter-6KNN6WQL.js.map +1 -0
- package/dist/secure-credentials-JXWAQLS2.js +306 -0
- package/dist/secure-credentials-JXWAQLS2.js.map +1 -0
- package/dist/tui-TPJPUS2R.js +111 -0
- package/dist/tui-TPJPUS2R.js.map +1 -0
- package/dist/vreko-dir-O3RLG7PI.js +8 -0
- package/dist/vreko-dir-O3RLG7PI.js.map +1 -0
- package/package.json +132 -0
- package/scripts/check-banned-words.ts +152 -0
- package/scripts/hooks/posttooluse-file-notify.sh +108 -0
- package/scripts/hooks/pretooluse-fragile-guard.sh +82 -0
- package/scripts/post-install-notice.js +24 -0
- package/scripts/postinstall.mjs +84 -0
- package/scripts/preuninstall.mjs +34 -0
- package/scripts/verify-jsx-transform.mjs +55 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { __name } from './chunk-EWOJGXRX.js';
|
|
3
|
+
import { randomBytes, scryptSync, createCipheriv, createDecipheriv } from 'crypto';
|
|
4
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
5
|
+
import { unlink, readFile, mkdir, writeFile } from 'fs/promises';
|
|
6
|
+
import { homedir, hostname, platform, userInfo } from 'os';
|
|
7
|
+
import { join, dirname } from 'path';
|
|
8
|
+
|
|
9
|
+
process.env.VREKO_CLI='true';process.env.NODE_NO_WARNINGS='1';
|
|
10
|
+
var SERVICE_NAME = "vreko-cli";
|
|
11
|
+
var ACCOUNT_NAME = "default";
|
|
12
|
+
var ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
13
|
+
var KEY_LENGTH = 32;
|
|
14
|
+
var IV_LENGTH = 12;
|
|
15
|
+
var AUTH_TAG_LENGTH = 16;
|
|
16
|
+
var SALT_LENGTH = 32;
|
|
17
|
+
var GLOBAL_DIR = join(homedir(), ".vreko");
|
|
18
|
+
var CREDENTIALS_FILE = join(GLOBAL_DIR, "credentials.json");
|
|
19
|
+
var ENCRYPTED_CREDENTIALS_FILE = join(GLOBAL_DIR, "credentials.enc");
|
|
20
|
+
var PER_INSTALL_SECRET_FILE = join(GLOBAL_DIR, ".cred-key");
|
|
21
|
+
var PER_INSTALL_SECRET_LENGTH = 32;
|
|
22
|
+
async function createKeytarProvider() {
|
|
23
|
+
try {
|
|
24
|
+
const keytar = await import('keytar');
|
|
25
|
+
return {
|
|
26
|
+
name: "keytar",
|
|
27
|
+
async isAvailable() {
|
|
28
|
+
try {
|
|
29
|
+
await keytar.getPassword("__vreko_test__", "__test__");
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async getPassword(service, account) {
|
|
36
|
+
return keytar.getPassword(service, account);
|
|
37
|
+
},
|
|
38
|
+
async setPassword(service, account, password) {
|
|
39
|
+
await keytar.setPassword(service, account, password);
|
|
40
|
+
},
|
|
41
|
+
async deletePassword(service, account) {
|
|
42
|
+
return keytar.deletePassword(service, account);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
__name(createKeytarProvider, "createKeytarProvider");
|
|
50
|
+
function getOrCreatePerInstallSecret() {
|
|
51
|
+
try {
|
|
52
|
+
if (existsSync(PER_INSTALL_SECRET_FILE)) {
|
|
53
|
+
const existing = readFileSync(PER_INSTALL_SECRET_FILE);
|
|
54
|
+
if (existing.length >= PER_INSTALL_SECRET_LENGTH) {
|
|
55
|
+
return existing;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
const secret = randomBytes(PER_INSTALL_SECRET_LENGTH);
|
|
61
|
+
mkdirSync(dirname(PER_INSTALL_SECRET_FILE), {
|
|
62
|
+
recursive: true
|
|
63
|
+
});
|
|
64
|
+
writeFileSync(PER_INSTALL_SECRET_FILE, secret, {
|
|
65
|
+
mode: 384
|
|
66
|
+
});
|
|
67
|
+
try {
|
|
68
|
+
chmodSync(PER_INSTALL_SECRET_FILE, 384);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
return secret;
|
|
72
|
+
}
|
|
73
|
+
__name(getOrCreatePerInstallSecret, "getOrCreatePerInstallSecret");
|
|
74
|
+
function machineDataString() {
|
|
75
|
+
return [
|
|
76
|
+
hostname(),
|
|
77
|
+
platform(),
|
|
78
|
+
userInfo().username,
|
|
79
|
+
homedir(),
|
|
80
|
+
// Add some entropy from process info
|
|
81
|
+
process.arch,
|
|
82
|
+
process.platform
|
|
83
|
+
].join("|");
|
|
84
|
+
}
|
|
85
|
+
__name(machineDataString, "machineDataString");
|
|
86
|
+
function deriveKeyFromSecret(salt, perInstallSecret) {
|
|
87
|
+
const keyMaterial = Buffer.concat([
|
|
88
|
+
Buffer.from(machineDataString(), "utf8"),
|
|
89
|
+
Buffer.from("|", "utf8"),
|
|
90
|
+
perInstallSecret
|
|
91
|
+
]);
|
|
92
|
+
return scryptSync(keyMaterial, salt, KEY_LENGTH);
|
|
93
|
+
}
|
|
94
|
+
__name(deriveKeyFromSecret, "deriveKeyFromSecret");
|
|
95
|
+
function deriveMachineKey(salt) {
|
|
96
|
+
return deriveKeyFromSecret(salt, getOrCreatePerInstallSecret());
|
|
97
|
+
}
|
|
98
|
+
__name(deriveMachineKey, "deriveMachineKey");
|
|
99
|
+
function encryptCredentials(credentials, salt) {
|
|
100
|
+
const key = deriveMachineKey(salt);
|
|
101
|
+
const iv = randomBytes(IV_LENGTH);
|
|
102
|
+
const cipher = createCipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
103
|
+
const plaintext = JSON.stringify(credentials);
|
|
104
|
+
const encrypted = Buffer.concat([
|
|
105
|
+
cipher.update(plaintext, "utf8"),
|
|
106
|
+
cipher.final()
|
|
107
|
+
]);
|
|
108
|
+
const authTag = cipher.getAuthTag();
|
|
109
|
+
return Buffer.concat([
|
|
110
|
+
salt,
|
|
111
|
+
iv,
|
|
112
|
+
authTag,
|
|
113
|
+
encrypted
|
|
114
|
+
]);
|
|
115
|
+
}
|
|
116
|
+
__name(encryptCredentials, "encryptCredentials");
|
|
117
|
+
function decryptCredentials(data) {
|
|
118
|
+
const salt = data.subarray(0, SALT_LENGTH);
|
|
119
|
+
const iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
|
|
120
|
+
const authTag = data.subarray(SALT_LENGTH + IV_LENGTH, SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
121
|
+
const encrypted = data.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
122
|
+
const key = deriveMachineKey(salt);
|
|
123
|
+
const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
|
|
124
|
+
decipher.setAuthTag(authTag);
|
|
125
|
+
const decrypted = Buffer.concat([
|
|
126
|
+
decipher.update(encrypted),
|
|
127
|
+
decipher.final()
|
|
128
|
+
]);
|
|
129
|
+
return JSON.parse(decrypted.toString("utf8"));
|
|
130
|
+
}
|
|
131
|
+
__name(decryptCredentials, "decryptCredentials");
|
|
132
|
+
function createEncryptedFileProvider() {
|
|
133
|
+
return {
|
|
134
|
+
name: "encrypted-file",
|
|
135
|
+
async isAvailable() {
|
|
136
|
+
return true;
|
|
137
|
+
},
|
|
138
|
+
async getPassword(_service, _account) {
|
|
139
|
+
try {
|
|
140
|
+
const data = await readFile(ENCRYPTED_CREDENTIALS_FILE);
|
|
141
|
+
const credentials = decryptCredentials(data);
|
|
142
|
+
return JSON.stringify(credentials);
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
async setPassword(_service, _account, password) {
|
|
148
|
+
const credentials = JSON.parse(password);
|
|
149
|
+
const salt = randomBytes(SALT_LENGTH);
|
|
150
|
+
const encrypted = encryptCredentials(credentials, salt);
|
|
151
|
+
await mkdir(dirname(ENCRYPTED_CREDENTIALS_FILE), {
|
|
152
|
+
recursive: true
|
|
153
|
+
});
|
|
154
|
+
await writeFile(ENCRYPTED_CREDENTIALS_FILE, encrypted);
|
|
155
|
+
},
|
|
156
|
+
async deletePassword(_service, _account) {
|
|
157
|
+
try {
|
|
158
|
+
await unlink(ENCRYPTED_CREDENTIALS_FILE);
|
|
159
|
+
return true;
|
|
160
|
+
} catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
__name(createEncryptedFileProvider, "createEncryptedFileProvider");
|
|
167
|
+
var SecureCredentialsManager = class SecureCredentialsManager2 {
|
|
168
|
+
static {
|
|
169
|
+
__name(this, "SecureCredentialsManager");
|
|
170
|
+
}
|
|
171
|
+
provider = null;
|
|
172
|
+
initialized = false;
|
|
173
|
+
/**
|
|
174
|
+
* Initialize the credentials manager
|
|
175
|
+
* Selects the best available provider
|
|
176
|
+
*/
|
|
177
|
+
async initialize() {
|
|
178
|
+
if (this.initialized) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const keytarProvider = await createKeytarProvider();
|
|
182
|
+
if (keytarProvider && await keytarProvider.isAvailable()) {
|
|
183
|
+
this.provider = keytarProvider;
|
|
184
|
+
this.initialized = true;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.provider = createEncryptedFileProvider();
|
|
188
|
+
this.initialized = true;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get the name of the active provider
|
|
192
|
+
*/
|
|
193
|
+
getProviderName() {
|
|
194
|
+
return this.provider?.name ?? "none";
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get stored credentials
|
|
198
|
+
*/
|
|
199
|
+
async getCredentials() {
|
|
200
|
+
await this.initialize();
|
|
201
|
+
if (!this.provider) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const stored = await this.provider.getPassword(SERVICE_NAME, ACCOUNT_NAME);
|
|
205
|
+
if (!stored) {
|
|
206
|
+
const legacy = await this.getLegacyCredentials();
|
|
207
|
+
if (legacy) {
|
|
208
|
+
await this.setCredentials(legacy);
|
|
209
|
+
try {
|
|
210
|
+
await unlink(CREDENTIALS_FILE);
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
return legacy;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
return JSON.parse(stored);
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get legacy plain-text credentials for migration
|
|
225
|
+
*/
|
|
226
|
+
async getLegacyCredentials() {
|
|
227
|
+
try {
|
|
228
|
+
const content = await readFile(CREDENTIALS_FILE, "utf8");
|
|
229
|
+
return JSON.parse(content);
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Save credentials securely
|
|
236
|
+
*/
|
|
237
|
+
async setCredentials(credentials) {
|
|
238
|
+
await this.initialize();
|
|
239
|
+
if (!this.provider) {
|
|
240
|
+
throw new Error("No credentials provider available");
|
|
241
|
+
}
|
|
242
|
+
await this.provider.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(credentials));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Clear stored credentials
|
|
246
|
+
*/
|
|
247
|
+
async clearCredentials() {
|
|
248
|
+
await this.initialize();
|
|
249
|
+
if (!this.provider) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
await this.provider.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
|
|
253
|
+
try {
|
|
254
|
+
await unlink(CREDENTIALS_FILE);
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
await unlink(ENCRYPTED_CREDENTIALS_FILE);
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Check if user is logged in
|
|
264
|
+
*/
|
|
265
|
+
async isLoggedIn() {
|
|
266
|
+
const credentials = await this.getCredentials();
|
|
267
|
+
if (!credentials?.accessToken) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
if (credentials.expiresAt) {
|
|
271
|
+
const expiresAt = new Date(credentials.expiresAt);
|
|
272
|
+
if (expiresAt < /* @__PURE__ */ new Date()) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
var secureCredentialsManager = null;
|
|
280
|
+
function getSecureCredentials() {
|
|
281
|
+
if (!secureCredentialsManager) {
|
|
282
|
+
secureCredentialsManager = new SecureCredentialsManager();
|
|
283
|
+
}
|
|
284
|
+
return secureCredentialsManager;
|
|
285
|
+
}
|
|
286
|
+
__name(getSecureCredentials, "getSecureCredentials");
|
|
287
|
+
async function getCredentialsSecure() {
|
|
288
|
+
return getSecureCredentials().getCredentials();
|
|
289
|
+
}
|
|
290
|
+
__name(getCredentialsSecure, "getCredentialsSecure");
|
|
291
|
+
async function saveCredentialsSecure(credentials) {
|
|
292
|
+
return getSecureCredentials().setCredentials(credentials);
|
|
293
|
+
}
|
|
294
|
+
__name(saveCredentialsSecure, "saveCredentialsSecure");
|
|
295
|
+
async function clearCredentialsSecure() {
|
|
296
|
+
return getSecureCredentials().clearCredentials();
|
|
297
|
+
}
|
|
298
|
+
__name(clearCredentialsSecure, "clearCredentialsSecure");
|
|
299
|
+
async function isLoggedInSecure() {
|
|
300
|
+
return getSecureCredentials().isLoggedIn();
|
|
301
|
+
}
|
|
302
|
+
__name(isLoggedInSecure, "isLoggedInSecure");
|
|
303
|
+
|
|
304
|
+
export { SecureCredentialsManager, clearCredentialsSecure, deriveKeyFromSecret, getCredentialsSecure, getSecureCredentials, isLoggedInSecure, saveCredentialsSecure };
|
|
305
|
+
//# sourceMappingURL=secure-credentials-JXWAQLS2.js.map
|
|
306
|
+
//# sourceMappingURL=secure-credentials-JXWAQLS2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/secure-credentials.ts"],"names":["SERVICE_NAME","ACCOUNT_NAME","ENCRYPTION_ALGORITHM","KEY_LENGTH","IV_LENGTH","AUTH_TAG_LENGTH","SALT_LENGTH","GLOBAL_DIR","join","homedir","CREDENTIALS_FILE","ENCRYPTED_CREDENTIALS_FILE","PER_INSTALL_SECRET_FILE","PER_INSTALL_SECRET_LENGTH","createKeytarProvider","keytar","name","isAvailable","getPassword","service","account","setPassword","password","deletePassword","getOrCreatePerInstallSecret","existsSync","existing","readFileSync","length","secret","randomBytes","mkdirSync","dirname","recursive","writeFileSync","mode","chmodSync","machineDataString","hostname","platform","userInfo","username","process","arch","deriveKeyFromSecret","salt","perInstallSecret","keyMaterial","Buffer","concat","from","scryptSync","deriveMachineKey","encryptCredentials","credentials","key","iv","cipher","createCipheriv","plaintext","JSON","stringify","encrypted","update","final","authTag","getAuthTag","decryptCredentials","data","subarray","decipher","createDecipheriv","setAuthTag","decrypted","parse","toString","createEncryptedFileProvider","_service","_account","readFile","mkdir","writeFile","unlink","SecureCredentialsManager","provider","initialized","initialize","keytarProvider","getProviderName","getCredentials","stored","legacy","getLegacyCredentials","setCredentials","content","Error","clearCredentials","isLoggedIn","accessToken","expiresAt","Date","secureCredentialsManager","getSecureCredentials","getCredentialsSecure","saveCredentialsSecure","clearCredentialsSecure","isLoggedInSecure"],"mappings":"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","file":"secure-credentials-JXWAQLS2.js","sourcesContent":["/**\n * Secure Credentials Storage for Vreko CLI\n *\n * FIX 4: Implements OS keychain storage with file fallback\n *\n * Security Hierarchy:\n * 1. OS Keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service)\n * 2. Encrypted file fallback (AES-256-GCM with machine-derived key)\n * 3. Plain text fallback (development only, with warning)\n *\n * @module services/secure-credentials\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, scryptSync } from \"node:crypto\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir, hostname, platform, userInfo } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { GlobalCredentials } from \"./vreko-dir\";\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst SERVICE_NAME = \"vreko-cli\";\nconst ACCOUNT_NAME = \"default\";\nconst ENCRYPTION_ALGORITHM = \"aes-256-gcm\";\nconst KEY_LENGTH = 32;\nconst IV_LENGTH = 12;\nconst AUTH_TAG_LENGTH = 16;\nconst SALT_LENGTH = 32;\n\n// File paths\nconst GLOBAL_DIR = join(homedir(), \".vreko\");\nconst CREDENTIALS_FILE = join(GLOBAL_DIR, \"credentials.json\");\nconst ENCRYPTED_CREDENTIALS_FILE = join(GLOBAL_DIR, \"credentials.enc\");\n/**\n * AUTH-04: per-install secret. Random entropy generated once, persisted at 0o600,\n * and NEVER written into the credential file header. The encrypted file stores\n * only the KDF salt; without this secret a same-UID reader of credentials.enc\n * cannot recompute the key from machine values alone.\n */\nconst PER_INSTALL_SECRET_FILE = join(GLOBAL_DIR, \".cred-key\");\nconst PER_INSTALL_SECRET_LENGTH = 32;\n\n// =============================================================================\n// KEYCHAIN INTERFACE\n// =============================================================================\n\n/**\n * Keychain abstraction interface\n * Allows for different implementations based on availability\n */\ninterface KeychainProvider {\n\tname: string;\n\tisAvailable(): Promise<boolean>;\n\tgetPassword(service: string, account: string): Promise<string | null>;\n\tsetPassword(service: string, account: string, password: string): Promise<void>;\n\tdeletePassword(service: string, account: string): Promise<boolean>;\n}\n\n// =============================================================================\n// KEYTAR PROVIDER (OS KEYCHAIN)\n// =============================================================================\n\n/**\n * Keytar-based keychain provider\n * Uses OS-level secure credential storage\n */\nasync function createKeytarProvider(): Promise<KeychainProvider | null> {\n\ttry {\n\t\t// Dynamic import to handle missing keytar gracefully\n\t\tconst keytar = await import(\"keytar\");\n\n\t\treturn {\n\t\t\tname: \"keytar\",\n\t\t\tasync isAvailable(): Promise<boolean> {\n\t\t\t\ttry {\n\t\t\t\t\t// Test availability by trying a no-op operation\n\t\t\t\t\tawait keytar.getPassword(\"__vreko_test__\", \"__test__\");\n\t\t\t\t\treturn true;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync getPassword(service: string, account: string): Promise<string | null> {\n\t\t\t\treturn keytar.getPassword(service, account);\n\t\t\t},\n\t\t\tasync setPassword(service: string, account: string, password: string): Promise<void> {\n\t\t\t\tawait keytar.setPassword(service, account, password);\n\t\t\t},\n\t\t\tasync deletePassword(service: string, account: string): Promise<boolean> {\n\t\t\t\treturn keytar.deletePassword(service, account);\n\t\t\t},\n\t\t};\n\t} catch {\n\t\t// keytar not available (not installed or native module issues)\n\t\treturn null;\n\t}\n}\n\n// =============================================================================\n// ENCRYPTED FILE PROVIDER\n// =============================================================================\n\n/**\n * Read or create the per-install secret (AUTH-04).\n *\n * Generated once with crypto.randomBytes, stored at PER_INSTALL_SECRET_FILE with\n * mode 0o600, and read on subsequent calls. This secret is the real key entropy:\n * it is never embedded in the credential file, so a reader of credentials.enc\n * (who sees only the salt) cannot derive the key from recomputable machine values.\n * Two installs on the same machine generate different secrets → different keys.\n */\nfunction getOrCreatePerInstallSecret(): Buffer {\n\ttry {\n\t\tif (existsSync(PER_INSTALL_SECRET_FILE)) {\n\t\t\tconst existing = readFileSync(PER_INSTALL_SECRET_FILE);\n\t\t\tif (existing.length >= PER_INSTALL_SECRET_LENGTH) {\n\t\t\t\treturn existing;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// fall through to (re)generate\n\t}\n\n\tconst secret = randomBytes(PER_INSTALL_SECRET_LENGTH);\n\tmkdirSync(dirname(PER_INSTALL_SECRET_FILE), { recursive: true });\n\t// Write with restrictive permissions, then chmod to guarantee 0o600 even if\n\t// the umask widened the create mode.\n\twriteFileSync(PER_INSTALL_SECRET_FILE, secret, { mode: 0o600 });\n\ttry {\n\t\tchmodSync(PER_INSTALL_SECRET_FILE, 0o600);\n\t} catch {\n\t\t// best-effort on platforms without POSIX modes\n\t}\n\treturn secret;\n}\n\n/**\n * Derive an encryption key from machine-specific data PLUS a per-install secret.\n *\n * The machine values alone are recomputable by any same-UID process, and the salt\n * lives in the credential file header - so without the per-install secret the key\n * is offline-derivable (AUTH-04 / F-5). Mixing in the secret closes that gap.\n */\nfunction machineDataString(): string {\n\treturn [\n\t\thostname(),\n\t\tplatform(),\n\t\tuserInfo().username,\n\t\thomedir(),\n\t\t// Add some entropy from process info\n\t\tprocess.arch,\n\t\tprocess.platform,\n\t].join(\"|\");\n}\n\n/**\n * Pure key derivation from machine data + a per-install secret + salt.\n * Exported for testing (R-4.1): with identical machine data and salt, two\n * different per-install secrets MUST yield different keys.\n */\nexport function deriveKeyFromSecret(salt: Buffer, perInstallSecret: Buffer): Buffer {\n\tconst keyMaterial = Buffer.concat([\n\t\tBuffer.from(machineDataString(), \"utf8\"),\n\t\tBuffer.from(\"|\", \"utf8\"),\n\t\tperInstallSecret,\n\t]);\n\treturn scryptSync(keyMaterial, salt, KEY_LENGTH);\n}\n\nfunction deriveMachineKey(salt: Buffer): Buffer {\n\t// AUTH-04: bind the key to the per-install secret. The secret is not stored\n\t// anywhere recomputable from the credential file (only the salt is).\n\treturn deriveKeyFromSecret(salt, getOrCreatePerInstallSecret());\n}\n\n/**\n * Encrypt credentials with machine-derived key\n */\nfunction encryptCredentials(credentials: GlobalCredentials, salt: Buffer): Buffer {\n\tconst key = deriveMachineKey(salt);\n\tconst iv = randomBytes(IV_LENGTH);\n\tconst cipher = createCipheriv(ENCRYPTION_ALGORITHM, key, iv);\n\n\tconst plaintext = JSON.stringify(credentials);\n\tconst encrypted = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n\tconst authTag = cipher.getAuthTag();\n\n\t// Format: salt (32) + iv (12) + authTag (16) + encrypted data\n\treturn Buffer.concat([salt, iv, authTag, encrypted]);\n}\n\n/**\n * Decrypt credentials with machine-derived key\n */\nfunction decryptCredentials(data: Buffer): GlobalCredentials {\n\t// Extract components\n\tconst salt = data.subarray(0, SALT_LENGTH);\n\tconst iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n\tconst authTag = data.subarray(SALT_LENGTH + IV_LENGTH, SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n\tconst encrypted = data.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);\n\n\tconst key = deriveMachineKey(salt);\n\tconst decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);\n\tdecipher.setAuthTag(authTag);\n\n\tconst decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);\n\treturn JSON.parse(decrypted.toString(\"utf8\")) as GlobalCredentials;\n}\n\n/**\n * Encrypted file provider\n * Uses AES-256-GCM with machine-derived key\n */\nfunction createEncryptedFileProvider(): KeychainProvider {\n\treturn {\n\t\tname: \"encrypted-file\",\n\t\tasync isAvailable(): Promise<boolean> {\n\t\t\treturn true; // Always available as fallback\n\t\t},\n\t\tasync getPassword(_service: string, _account: string): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst data = await readFile(ENCRYPTED_CREDENTIALS_FILE);\n\t\t\t\tconst credentials = decryptCredentials(data);\n\t\t\t\treturn JSON.stringify(credentials);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync setPassword(_service: string, _account: string, password: string): Promise<void> {\n\t\t\tconst credentials = JSON.parse(password) as GlobalCredentials;\n\t\t\tconst salt = randomBytes(SALT_LENGTH);\n\t\t\tconst encrypted = encryptCredentials(credentials, salt);\n\n\t\t\tawait mkdir(dirname(ENCRYPTED_CREDENTIALS_FILE), { recursive: true });\n\t\t\tawait writeFile(ENCRYPTED_CREDENTIALS_FILE, encrypted);\n\t\t},\n\t\tasync deletePassword(_service: string, _account: string): Promise<boolean> {\n\t\t\ttry {\n\t\t\t\tawait unlink(ENCRYPTED_CREDENTIALS_FILE);\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t};\n}\n\n// =============================================================================\n// PLAIN FILE PROVIDER (DEVELOPMENT FALLBACK)\n// =============================================================================\n\n/**\n * Plain file provider (legacy, development only)\n * Shows warning when used in production\n * @deprecated Use encrypted file provider instead\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction _createPlainFileProvider(): KeychainProvider {\n\tlet warningShown = false;\n\n\treturn {\n\t\tname: \"plain-file\",\n\t\tasync isAvailable(): Promise<boolean> {\n\t\t\treturn true;\n\t\t},\n\t\tasync getPassword(_service: string, _account: string): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(CREDENTIALS_FILE, \"utf8\");\n\t\t\t\treturn content;\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync setPassword(_service: string, _account: string, password: string): Promise<void> {\n\t\t\tif (process.env.NODE_ENV === \"production\" && !warningShown) {\n\t\t\t\twarningShown = true;\n\t\t\t}\n\n\t\t\tawait mkdir(dirname(CREDENTIALS_FILE), { recursive: true });\n\t\t\tawait writeFile(CREDENTIALS_FILE, password, { mode: 0o600 }); // Restrict file permissions\n\t\t},\n\t\tasync deletePassword(_service: string, _account: string): Promise<boolean> {\n\t\t\ttry {\n\t\t\t\tawait unlink(CREDENTIALS_FILE);\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t};\n}\n\n// =============================================================================\n// SECURE CREDENTIALS MANAGER\n// =============================================================================\n\n/**\n * Secure Credentials Manager\n *\n * Automatically selects the most secure available storage:\n * 1. OS Keychain (via keytar)\n * 2. Encrypted file\n * 3. Plain file (with warning)\n */\nclass SecureCredentialsManager {\n\tprivate provider: KeychainProvider | null = null;\n\tprivate initialized = false;\n\n\t/**\n\t * Initialize the credentials manager\n\t * Selects the best available provider\n\t */\n\tasync initialize(): Promise<void> {\n\t\tif (this.initialized) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Try keytar first (OS keychain)\n\t\tconst keytarProvider = await createKeytarProvider();\n\t\tif (keytarProvider && (await keytarProvider.isAvailable())) {\n\t\t\tthis.provider = keytarProvider;\n\t\t\tthis.initialized = true;\n\t\t\treturn;\n\t\t}\n\n\t\t// Fall back to encrypted file\n\t\tthis.provider = createEncryptedFileProvider();\n\t\tthis.initialized = true;\n\t}\n\n\t/**\n\t * Get the name of the active provider\n\t */\n\tgetProviderName(): string {\n\t\treturn this.provider?.name ?? \"none\";\n\t}\n\n\t/**\n\t * Get stored credentials\n\t */\n\tasync getCredentials(): Promise<GlobalCredentials | null> {\n\t\tawait this.initialize();\n\t\tif (!this.provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst stored = await this.provider.getPassword(SERVICE_NAME, ACCOUNT_NAME);\n\t\tif (!stored) {\n\t\t\t// Check legacy plain file as migration fallback\n\t\t\tconst legacy = await this.getLegacyCredentials();\n\t\t\tif (legacy) {\n\t\t\t\t// Migrate to secure storage\n\t\t\t\tawait this.setCredentials(legacy);\n\t\t\t\t// Delete legacy file after successful migration\n\t\t\t\ttry {\n\t\t\t\t\tawait unlink(CREDENTIALS_FILE);\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore if file doesn't exist\n\t\t\t\t}\n\t\t\t\treturn legacy;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(stored) as GlobalCredentials;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get legacy plain-text credentials for migration\n\t */\n\tprivate async getLegacyCredentials(): Promise<GlobalCredentials | null> {\n\t\ttry {\n\t\t\tconst content = await readFile(CREDENTIALS_FILE, \"utf8\");\n\t\t\treturn JSON.parse(content) as GlobalCredentials;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Save credentials securely\n\t */\n\tasync setCredentials(credentials: GlobalCredentials): Promise<void> {\n\t\tawait this.initialize();\n\t\tif (!this.provider) {\n\t\t\tthrow new Error(\"No credentials provider available\");\n\t\t}\n\n\t\tawait this.provider.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(credentials));\n\t}\n\n\t/**\n\t * Clear stored credentials\n\t */\n\tasync clearCredentials(): Promise<void> {\n\t\tawait this.initialize();\n\t\tif (!this.provider) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.provider.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n\n\t\t// Also clean up any legacy files\n\t\ttry {\n\t\t\tawait unlink(CREDENTIALS_FILE);\n\t\t} catch {\n\t\t\t// Ignore\n\t\t}\n\t\ttry {\n\t\t\tawait unlink(ENCRYPTED_CREDENTIALS_FILE);\n\t\t} catch {\n\t\t\t// Ignore\n\t\t}\n\t}\n\n\t/**\n\t * Check if user is logged in\n\t */\n\tasync isLoggedIn(): Promise<boolean> {\n\t\tconst credentials = await this.getCredentials();\n\t\tif (!credentials?.accessToken) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if token is expired\n\t\tif (credentials.expiresAt) {\n\t\t\tconst expiresAt = new Date(credentials.expiresAt);\n\t\t\tif (expiresAt < new Date()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\n// Singleton instance\nlet secureCredentialsManager: SecureCredentialsManager | null = null;\n\n/**\n * Get the secure credentials manager singleton\n */\nexport function getSecureCredentials(): SecureCredentialsManager {\n\tif (!secureCredentialsManager) {\n\t\tsecureCredentialsManager = new SecureCredentialsManager();\n\t}\n\treturn secureCredentialsManager;\n}\n\n/**\n * Secure versions of credential functions (drop-in replacements)\n */\nexport async function getCredentialsSecure(): Promise<GlobalCredentials | null> {\n\treturn getSecureCredentials().getCredentials();\n}\n\nexport async function saveCredentialsSecure(credentials: GlobalCredentials): Promise<void> {\n\treturn getSecureCredentials().setCredentials(credentials);\n}\n\nexport async function clearCredentialsSecure(): Promise<void> {\n\treturn getSecureCredentials().clearCredentials();\n}\n\nexport async function isLoggedInSecure(): Promise<boolean> {\n\treturn getSecureCredentials().isLoggedIn();\n}\n\nexport { SecureCredentialsManager };\n"]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export { TuiApp } from './chunk-W5B4GTXR.js';
|
|
3
|
+
import { Sentry } from './chunk-YPTTIXKC.js';
|
|
4
|
+
import { cliState } from './chunk-GRMRYWYS.js';
|
|
5
|
+
import { createServiceClient, connectServiceClient } from './chunk-GSUGROXB.js';
|
|
6
|
+
import './chunk-DMXC2JTC.js';
|
|
7
|
+
import './chunk-VTSNRV3V.js';
|
|
8
|
+
import { __name } from './chunk-EWOJGXRX.js';
|
|
9
|
+
import { readFile, writeFile, unlink } from 'fs/promises';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { render } from 'ink';
|
|
14
|
+
import React from 'react';
|
|
15
|
+
|
|
16
|
+
process.env.VREKO_CLI='true';process.env.NODE_NO_WARNINGS='1';
|
|
17
|
+
var PID_FILE = join(homedir(), ".vreko", "tui.pid");
|
|
18
|
+
var isShuttingDown = false;
|
|
19
|
+
var inkInstance = null;
|
|
20
|
+
async function acquirePidLock() {
|
|
21
|
+
try {
|
|
22
|
+
const existing = await readFile(PID_FILE, "utf-8");
|
|
23
|
+
const pid = Number.parseInt(existing.trim(), 10);
|
|
24
|
+
try {
|
|
25
|
+
process.kill(pid, 0);
|
|
26
|
+
console.error(`A Vreko TUI session is already running (PID ${pid}). Run \`vr stop\` first.`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
await writeFile(PID_FILE, String(process.pid), "utf-8");
|
|
33
|
+
}
|
|
34
|
+
__name(acquirePidLock, "acquirePidLock");
|
|
35
|
+
async function releasePidLock() {
|
|
36
|
+
try {
|
|
37
|
+
await unlink(PID_FILE);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
__name(releasePidLock, "releasePidLock");
|
|
42
|
+
function shutdown(code) {
|
|
43
|
+
if (isShuttingDown) return;
|
|
44
|
+
isShuttingDown = true;
|
|
45
|
+
if (inkInstance) {
|
|
46
|
+
inkInstance.unmount();
|
|
47
|
+
}
|
|
48
|
+
process.stdout.write("\x1B[?25h");
|
|
49
|
+
process.stdout.write("\x1B[0m\n");
|
|
50
|
+
releasePidLock().finally(() => process.exit(code));
|
|
51
|
+
}
|
|
52
|
+
__name(shutdown, "shutdown");
|
|
53
|
+
async function launchTui(panelOrOptions = "dashboard") {
|
|
54
|
+
if (cliState.renderMode !== "ink") {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await acquirePidLock();
|
|
58
|
+
for (const sig of [
|
|
59
|
+
"SIGINT",
|
|
60
|
+
"SIGTERM"
|
|
61
|
+
]) {
|
|
62
|
+
process.on(sig, () => shutdown(0));
|
|
63
|
+
}
|
|
64
|
+
process.on("uncaughtException", (err) => {
|
|
65
|
+
Sentry.captureException(err);
|
|
66
|
+
shutdown(1);
|
|
67
|
+
});
|
|
68
|
+
process.on("unhandledRejection", (reason) => {
|
|
69
|
+
Sentry.captureException(reason instanceof Error ? reason : new Error(String(reason)));
|
|
70
|
+
shutdown(1);
|
|
71
|
+
});
|
|
72
|
+
const opts = typeof panelOrOptions === "string" ? {
|
|
73
|
+
panel: panelOrOptions
|
|
74
|
+
} : panelOrOptions;
|
|
75
|
+
const panel = opts.panel ?? opts.initialPanel ?? "dashboard";
|
|
76
|
+
const statusFocus = opts.statusFocus ?? false;
|
|
77
|
+
const client = createServiceClient();
|
|
78
|
+
try {
|
|
79
|
+
await connectServiceClient(client);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
console.error(chalk.red("Failed to connect to Vreko service:"), err instanceof Error ? err.message : String(err));
|
|
82
|
+
console.log(chalk.gray("Start the service with: vr service start"));
|
|
83
|
+
await releasePidLock();
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
const { detectOverlayCapability, writeGeckoOverlay, renderGeckoArt } = await import('./gecko-53ITAGG6.js');
|
|
87
|
+
const capability = detectOverlayCapability();
|
|
88
|
+
if (capability !== "none") {
|
|
89
|
+
writeGeckoOverlay(() => renderGeckoArt(capability));
|
|
90
|
+
}
|
|
91
|
+
const { TuiApp: TuiApp2 } = await import('./TuiApp-FX23XQBK.js');
|
|
92
|
+
const instance = render(React.createElement(TuiApp2, {
|
|
93
|
+
client,
|
|
94
|
+
initialPanel: panel,
|
|
95
|
+
statusFocus
|
|
96
|
+
}), {
|
|
97
|
+
exitOnCtrlC: false
|
|
98
|
+
});
|
|
99
|
+
inkInstance = instance;
|
|
100
|
+
try {
|
|
101
|
+
await instance.waitUntilExit();
|
|
102
|
+
} finally {
|
|
103
|
+
inkInstance = null;
|
|
104
|
+
await releasePidLock();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
__name(launchTui, "launchTui");
|
|
108
|
+
|
|
109
|
+
export { launchTui };
|
|
110
|
+
//# sourceMappingURL=tui-TPJPUS2R.js.map
|
|
111
|
+
//# sourceMappingURL=tui-TPJPUS2R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/tui/index.ts"],"names":["PID_FILE","join","homedir","isShuttingDown","inkInstance","acquirePidLock","existing","readFile","pid","Number","parseInt","trim","process","kill","console","error","exit","writeFile","String","releasePidLock","unlink","shutdown","code","unmount","stdout","write","finally","launchTui","panelOrOptions","cliState","renderMode","sig","on","err","Sentry","captureException","reason","Error","opts","panel","initialPanel","statusFocus","client","createServiceClient","connectServiceClient","chalk","red","message","log","gray","detectOverlayCapability","writeGeckoOverlay","renderGeckoArt","capability","TuiApp","instance","render","React","createElement","exitOnCtrlC","waitUntilExit"],"mappings":"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","file":"tui-TPJPUS2R.js","sourcesContent":["/**\n * Vreko TUI - barrel export + launchTui() entry function.\n *\n * launchTui() is the single entry point called by all commands that open the TUI.\n * It handles: PID lockfile, signal/error registration, gecko overlay, Ink render.\n * daemon close() lives in TuiApp useEffect cleanup - NOT in this file.\n * Pattern: commands/init/init-command.ts TTY guard + service client lifecycle\n */\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Sentry } from \"@vreko/sentry-privacy\";\nimport chalk from \"chalk\";\nimport { render } from \"ink\";\nimport React from \"react\";\nimport { cliState } from \"../../cli-state.js\";\nimport { connectServiceClient, createServiceClient } from \"../../service-adapter/local-service-adapter.js\";\n\nexport type { PanelId, TuiAppProps } from \"./TuiApp.js\";\nexport { TuiApp } from \"./TuiApp.js\";\n\nexport interface LaunchTuiOptions {\n\tpanel?: import(\"./TuiApp.js\").PanelId;\n\t/** Alias for `panel` - preferred spelling in command call sites (TUI-01). */\n\tinitialPanel?: import(\"./TuiApp.js\").PanelId;\n\t/** When true, shows StatusPanel on the dashboard tab (used by `vr status`) */\n\tstatusFocus?: boolean;\n}\n\n// =============================================================================\n// MODULE-LEVEL STATE\n// =============================================================================\n\nconst PID_FILE = join(homedir(), \".vreko\", \"tui.pid\");\n\nlet isShuttingDown = false;\nlet inkInstance: { unmount: () => void; waitUntilExit: () => Promise<unknown> } | null = null;\n\n// =============================================================================\n// PID LOCKFILE HELPERS\n// =============================================================================\n\nasync function acquirePidLock(): Promise<void> {\n\ttry {\n\t\tconst existing = await readFile(PID_FILE, \"utf-8\");\n\t\tconst pid = Number.parseInt(existing.trim(), 10);\n\t\ttry {\n\t\t\tprocess.kill(pid, 0); // throws ESRCH if PID does not exist\n\t\t\tconsole.error(`A Vreko TUI session is already running (PID ${pid}). Run \\`vr stop\\` first.`);\n\t\t\tprocess.exit(1);\n\t\t} catch {\n\t\t\t// Stale PID - file exists but process is dead. Fall through to overwrite.\n\t\t}\n\t} catch {\n\t\t// PID file does not exist - first launch. Proceed.\n\t}\n\tawait writeFile(PID_FILE, String(process.pid), \"utf-8\");\n}\n\nasync function releasePidLock(): Promise<void> {\n\ttry {\n\t\tawait unlink(PID_FILE);\n\t} catch {\n\t\t// Best effort - file may already be removed\n\t}\n}\n\n// =============================================================================\n// SHUTDOWN CONTRACT\n// =============================================================================\n\nfunction shutdown(code: number): void {\n\tif (isShuttingDown) return;\n\tisShuttingDown = true;\n\tif (inkInstance) {\n\t\tinkInstance.unmount(); // triggers useEffect cleanup → daemon close() runs in TuiApp\n\t}\n\tprocess.stdout.write(\"\\x1b[?25h\"); // restore cursor\n\tprocess.stdout.write(\"\\x1b[0m\\n\"); // reset color + newline\n\treleasePidLock().finally(() => process.exit(code));\n}\n\n/**\n * Launch the Vreko TUI for a given initial panel.\n * Machine mode: if cliState.renderMode !== \"ink\", this function does nothing (caller handles output).\n */\nexport async function launchTui(\n\tpanelOrOptions: import(\"./TuiApp.js\").PanelId | LaunchTuiOptions = \"dashboard\",\n): Promise<void> {\n\tif (cliState.renderMode !== \"ink\") {\n\t\t// Machine mode: caller is responsible for JSON output\n\t\treturn;\n\t}\n\n\tawait acquirePidLock();\n\n\t// Register signal and error handlers before render\n\tfor (const sig of [\"SIGINT\", \"SIGTERM\"] as const) {\n\t\tprocess.on(sig, () => shutdown(0));\n\t}\n\tprocess.on(\"uncaughtException\", (err) => {\n\t\tSentry.captureException(err);\n\t\tshutdown(1);\n\t});\n\tprocess.on(\"unhandledRejection\", (reason) => {\n\t\tSentry.captureException(reason instanceof Error ? reason : new Error(String(reason)));\n\t\tshutdown(1);\n\t});\n\n\tconst opts: LaunchTuiOptions = typeof panelOrOptions === \"string\" ? { panel: panelOrOptions } : panelOrOptions;\n\tconst panel = opts.panel ?? opts.initialPanel ?? \"dashboard\";\n\tconst statusFocus = opts.statusFocus ?? false;\n\n\tconst client = createServiceClient();\n\ttry {\n\t\tawait connectServiceClient(client);\n\t} catch (err) {\n\t\tconsole.error(\n\t\t\tchalk.red(\"Failed to connect to Vreko service:\"),\n\t\t\terr instanceof Error ? err.message : String(err),\n\t\t);\n\t\tconsole.log(chalk.gray(\"Start the service with: vr service start\"));\n\t\tawait releasePidLock();\n\t\tprocess.exit(1);\n\t}\n\n\t// Gecko overlay - write before Ink render()\n\tconst { detectOverlayCapability, writeGeckoOverlay, renderGeckoArt } = await import(\"../gecko/index.js\");\n\tconst capability = detectOverlayCapability();\n\tif (capability !== \"none\") {\n\t\twriteGeckoOverlay(() => renderGeckoArt(capability));\n\t}\n\n\tconst { TuiApp } = await import(\"./TuiApp.js\");\n\tconst instance = render(React.createElement(TuiApp, { client, initialPanel: panel, statusFocus }), {\n\t\texitOnCtrlC: false, // We manage shutdown ourselves\n\t});\n\tinkInstance = instance;\n\n\ttry {\n\t\tawait instance.waitUntilExit();\n\t} finally {\n\t\tinkInstance = null;\n\t\tawait releasePidLock();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export { GlobalConfigSchema, GlobalCredentialsSchema, LearningEntrySchema, ProtectedFileSchema, SessionStateSchema, ViolationEntrySchema, WorkspaceConfigSchema, WorkspaceVitalsSchema, appendVrekoJsonl, clearCredentials, createGlobalDirectory, createVrekoDirectory, deleteGlobalJson, endCurrentSession, findWorkspaceRoot, getCredentials, getCurrentSession, getGlobalConfig, getGlobalDir, getGlobalPath, getLearnings, getProtectedFiles, getStats, getViolations, getWorkspaceConfig, getWorkspaceDir, getWorkspacePath, getWorkspaceVitals, isLoggedIn, isVrekoInitialized, loadVrekoJsonl, pathExists, readGlobalJson, readVrekoJson, recordLearning, recordViolation, saveBenchmarkOptIn, saveCredentials, saveCurrentSession, saveGlobalConfig, saveProtectedFiles, saveWorkspaceConfig, saveWorkspaceVitals, writeGlobalJson, writeVrekoJson } from './chunk-HFQHU5LC.js';
|
|
3
|
+
export { generateId } from './chunk-KJWKY4L4.js';
|
|
4
|
+
import './chunk-EWOJGXRX.js';
|
|
5
|
+
|
|
6
|
+
process.env.VREKO_CLI='true';process.env.NODE_NO_WARNINGS='1';
|
|
7
|
+
//# sourceMappingURL=vreko-dir-O3RLG7PI.js.map
|
|
8
|
+
//# sourceMappingURL=vreko-dir-O3RLG7PI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"vreko-dir-O3RLG7PI.js"}
|
package/package.json
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vreko/cli",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Vreko CLI — AI-aware developer intelligence for the command line",
|
|
5
|
+
"homepage": "https://vreko.dev",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/vreko-dev/vreko-cli.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/vreko-dev/vreko-cli/issues"
|
|
12
|
+
},
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Vreko Team",
|
|
16
|
+
"url": "https://vreko.dev"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"vreko",
|
|
20
|
+
"ambient-codebase-intelligence",
|
|
21
|
+
"codebase-intelligence",
|
|
22
|
+
"ai-codebase-intelligence",
|
|
23
|
+
"ai-coding-mistakes-prevention",
|
|
24
|
+
"fragile-files",
|
|
25
|
+
"cursor-companion",
|
|
26
|
+
"claude-code-companion",
|
|
27
|
+
"copilot-companion",
|
|
28
|
+
"ai-agent-context",
|
|
29
|
+
"cli",
|
|
30
|
+
"snapshots",
|
|
31
|
+
"intelligence"
|
|
32
|
+
],
|
|
33
|
+
"bin": {
|
|
34
|
+
"vreko": "dist/index.js",
|
|
35
|
+
"vr": "dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"scripts",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@better-auth/passkey": "1.6.5",
|
|
45
|
+
"@better-auth/sso": "1.6.5",
|
|
46
|
+
"@clack/prompts": "^1.1.0",
|
|
47
|
+
"@electric-sql/pglite": "0.2.0",
|
|
48
|
+
"@inkjs/ui": "^2.0.0",
|
|
49
|
+
"@modelcontextprotocol/sdk": "1.28.0",
|
|
50
|
+
"@sentry/node": "10.32.1",
|
|
51
|
+
"better-auth": "1.6.5",
|
|
52
|
+
"boxen": "8.0.1",
|
|
53
|
+
"chalk": "4.1.2",
|
|
54
|
+
"chokidar": "4.0.3",
|
|
55
|
+
"cli-table3": "0.6.5",
|
|
56
|
+
"commander": "9.5.0",
|
|
57
|
+
"conf": "13.0.1",
|
|
58
|
+
"cookie": "1.1.1",
|
|
59
|
+
"dotenv": "17.2.2",
|
|
60
|
+
"drizzle-orm": "0.45.2",
|
|
61
|
+
"eventemitter2": "6.4.9",
|
|
62
|
+
"execa": "9.5.2",
|
|
63
|
+
"ink": "^6.8.0",
|
|
64
|
+
"ky": "1.7.2",
|
|
65
|
+
"listr2": "10.2.1",
|
|
66
|
+
"llm-guard": "^0.1.8",
|
|
67
|
+
"log-update": "6.1.0",
|
|
68
|
+
"opossum": "9.0.0",
|
|
69
|
+
"ora": "5.4.1",
|
|
70
|
+
"react": "19.1.2",
|
|
71
|
+
"zod": "3.25.76",
|
|
72
|
+
"@vreko/claims-ledger": "0.1.0",
|
|
73
|
+
"@vreko/contracts": "1.0.0",
|
|
74
|
+
"@vreko/auth": "0.1.0",
|
|
75
|
+
"@vreko/intelligence": "0.1.0",
|
|
76
|
+
"@vreko/mcp": "0.1.0",
|
|
77
|
+
"@vreko/local-service-client": "0.0.1",
|
|
78
|
+
"@vreko/local-service": "3.0.0",
|
|
79
|
+
"@vreko/mcp-client": "0.1.0",
|
|
80
|
+
"@vreko/sentry-privacy": "0.0.1",
|
|
81
|
+
"@vreko/mcp-config": "1.0.0"
|
|
82
|
+
},
|
|
83
|
+
"optionalDependencies": {
|
|
84
|
+
"keytar": "7.9.0"
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@types/react": "19.1.13",
|
|
88
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
89
|
+
"ink-gradient": "^4.0.0",
|
|
90
|
+
"ink-testing-library": "^4.0.0",
|
|
91
|
+
"react-devtools-core": "^6.1.2",
|
|
92
|
+
"tsup": "8.5.0",
|
|
93
|
+
"tsx": "4.20.5",
|
|
94
|
+
"typescript": "5.9.2",
|
|
95
|
+
"vitest": "3.2.4"
|
|
96
|
+
},
|
|
97
|
+
"publishConfig": {
|
|
98
|
+
"access": "public",
|
|
99
|
+
"registry": "https://registry.npmjs.org/"
|
|
100
|
+
},
|
|
101
|
+
"exports": {
|
|
102
|
+
".": "./dist/index.js",
|
|
103
|
+
"./services/vreko-dir": "./dist/services/vreko-dir.js"
|
|
104
|
+
},
|
|
105
|
+
"type": "module",
|
|
106
|
+
"engines": {
|
|
107
|
+
"node": ">=20.9.0",
|
|
108
|
+
"npm": ">=8.0.0",
|
|
109
|
+
"pnpm": ">=8.0.0"
|
|
110
|
+
},
|
|
111
|
+
"scripts": {
|
|
112
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
113
|
+
"preuninstall": "node scripts/preuninstall.mjs",
|
|
114
|
+
"build": "tsup && tsc --build tsconfig.build.json --emitDeclarationOnly && node scripts/verify-jsx-transform.mjs",
|
|
115
|
+
"check": "biome check .",
|
|
116
|
+
"check:banned-words": "tsx scripts/check-banned-words.ts",
|
|
117
|
+
"dev": "tsup --watch",
|
|
118
|
+
"format": "biome format --write .",
|
|
119
|
+
"lint": "biome lint .",
|
|
120
|
+
"lint:fix": "biome lint --fix .",
|
|
121
|
+
"test": "vitest run",
|
|
122
|
+
"test:coverage": "vitest run --coverage",
|
|
123
|
+
"test:watch": "vitest",
|
|
124
|
+
"type-check": "tsc -p tsconfig.json --noEmit",
|
|
125
|
+
"cli:install": "pnpm build && pnpm link --global && echo '✅ CLI installed globally. Run: vreko --help or vr --help'",
|
|
126
|
+
"cli:uninstall": "pnpm unlink --global && echo '✅ CLI uninstalled globally'",
|
|
127
|
+
"cli:reinstall": "pnpm run cli:uninstall && pnpm run cli:install",
|
|
128
|
+
"mcp:configure": "node dist/index.js tools configure --yes",
|
|
129
|
+
"mcp:configure:dev": "node dist/index.js tools configure --yes --dev",
|
|
130
|
+
"mcp:status": "node dist/index.js tools configure --list"
|
|
131
|
+
}
|
|
132
|
+
}
|