shark-ai 0.0.2 → 0.2.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/CHANGELOG.md +63 -63
- package/LICENSE +21 -21
- package/README.en.md +349 -349
- package/README.md +349 -349
- package/dist/bin/shark.js +60 -93
- package/dist/bin/shark.js.map +1 -1
- package/dist/{chunk-B7PNFPUX.js → chunk-R3MIUNVD.js} +402 -220
- package/dist/chunk-R3MIUNVD.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +83 -85
- package/dist/chunk-B7PNFPUX.js.map +0 -1
|
@@ -25,6 +25,280 @@ import { Command } from "commander";
|
|
|
25
25
|
|
|
26
26
|
// src/ui/tui.ts
|
|
27
27
|
import * as p from "@clack/prompts";
|
|
28
|
+
|
|
29
|
+
// src/core/config-manager.ts
|
|
30
|
+
import os2 from "os";
|
|
31
|
+
import path2 from "path";
|
|
32
|
+
import fs2 from "fs";
|
|
33
|
+
|
|
34
|
+
// src/core/config/schema.ts
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
var ConfigSchema = z.object({
|
|
37
|
+
logLevel: z.enum(["debug", "info", "warn", "error"]).default("info"),
|
|
38
|
+
preferredStack: z.array(z.string()).default([]),
|
|
39
|
+
apiBaseUrl: z.string().optional(),
|
|
40
|
+
language: z.enum(["pt-br", "en-us", "es-es"]).default("pt-br"),
|
|
41
|
+
project: z.string().optional(),
|
|
42
|
+
environment: z.string().optional(),
|
|
43
|
+
activeRealm: z.string().optional()
|
|
44
|
+
// Currently logged-in realm
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// src/core/config/sharkrc-loader.ts
|
|
48
|
+
import fs from "fs";
|
|
49
|
+
import path from "path";
|
|
50
|
+
import os from "os";
|
|
51
|
+
import { z as z2 } from "zod";
|
|
52
|
+
var SharkRCFileSchema = z2.object({
|
|
53
|
+
project: z2.string().optional(),
|
|
54
|
+
environment: z2.string().optional(),
|
|
55
|
+
logLevel: z2.string().optional(),
|
|
56
|
+
language: z2.string().optional(),
|
|
57
|
+
preferredStack: z2.array(z2.string()).optional()
|
|
58
|
+
// Add other keys as needed from the main config
|
|
59
|
+
}).passthrough();
|
|
60
|
+
function loadFile(filePath) {
|
|
61
|
+
try {
|
|
62
|
+
if (!fs.existsSync(filePath)) {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
66
|
+
const json = JSON.parse(content);
|
|
67
|
+
const result = SharkRCFileSchema.safeParse(json);
|
|
68
|
+
if (result.success) {
|
|
69
|
+
return result.data;
|
|
70
|
+
} else {
|
|
71
|
+
console.warn(colors.warning(`\u26A0\uFE0F Invalid config in ${filePath}: ${result.error.message}`));
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.warn(colors.warning(`\u26A0\uFE0F Failed to read ${filePath}: ${error.message}`));
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function loadSharkRC() {
|
|
80
|
+
const homeDir = os.homedir();
|
|
81
|
+
const currentDir = process.cwd();
|
|
82
|
+
const globalPath = path.join(homeDir, ".sharkrc");
|
|
83
|
+
const localPath = path.join(currentDir, ".sharkrc");
|
|
84
|
+
const globalConfig = loadFile(globalPath);
|
|
85
|
+
const localConfig = loadFile(localPath);
|
|
86
|
+
return {
|
|
87
|
+
...globalConfig,
|
|
88
|
+
...localConfig
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function saveGlobalRC(configUpdates) {
|
|
92
|
+
const homeDir = os.homedir();
|
|
93
|
+
const globalPath = path.join(homeDir, ".sharkrc");
|
|
94
|
+
const currentConfig = loadFile(globalPath);
|
|
95
|
+
const newConfig = { ...currentConfig, ...configUpdates };
|
|
96
|
+
try {
|
|
97
|
+
fs.writeFileSync(globalPath, JSON.stringify(newConfig, null, 2), "utf-8");
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(`Failed to save global config to ${globalPath}: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/core/config-manager.ts
|
|
104
|
+
var ConfigManager = class _ConfigManager {
|
|
105
|
+
static instance;
|
|
106
|
+
config = null;
|
|
107
|
+
constructor() {
|
|
108
|
+
}
|
|
109
|
+
static getInstance() {
|
|
110
|
+
if (!_ConfigManager.instance) {
|
|
111
|
+
_ConfigManager.instance = new _ConfigManager();
|
|
112
|
+
}
|
|
113
|
+
return _ConfigManager.instance;
|
|
114
|
+
}
|
|
115
|
+
getConfig() {
|
|
116
|
+
if (!this.config) {
|
|
117
|
+
this.config = this.loadConfig();
|
|
118
|
+
}
|
|
119
|
+
return this.config;
|
|
120
|
+
}
|
|
121
|
+
reloadConfig() {
|
|
122
|
+
this.config = this.loadConfig();
|
|
123
|
+
}
|
|
124
|
+
loadConfig() {
|
|
125
|
+
let mergedConfig = {};
|
|
126
|
+
const rcConfig = loadSharkRC();
|
|
127
|
+
mergedConfig = { ...mergedConfig, ...rcConfig };
|
|
128
|
+
const envConfig = this.loadEnvConfig();
|
|
129
|
+
mergedConfig = { ...mergedConfig, ...envConfig };
|
|
130
|
+
const parsed = ConfigSchema.safeParse(mergedConfig);
|
|
131
|
+
if (!parsed.success) {
|
|
132
|
+
console.warn("\u26A0\uFE0F Invalid configuration detected, falling back to defaults or partial config");
|
|
133
|
+
console.warn(parsed.error.message);
|
|
134
|
+
return ConfigSchema.parse({});
|
|
135
|
+
}
|
|
136
|
+
return parsed.data;
|
|
137
|
+
}
|
|
138
|
+
readJsonFile(filePath) {
|
|
139
|
+
try {
|
|
140
|
+
if (fs2.existsSync(filePath)) {
|
|
141
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
142
|
+
return JSON.parse(content);
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.warn(`Failed to read config file at ${filePath}`, error);
|
|
146
|
+
}
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
async set(key, value) {
|
|
150
|
+
if (!this.config) {
|
|
151
|
+
this.config = this.loadConfig();
|
|
152
|
+
}
|
|
153
|
+
this.config[key] = value;
|
|
154
|
+
const homeDir = os2.homedir();
|
|
155
|
+
const configPath = path2.join(homeDir, ".sharkrc");
|
|
156
|
+
try {
|
|
157
|
+
let currentFileConfig = {};
|
|
158
|
+
if (fs2.existsSync(configPath)) {
|
|
159
|
+
currentFileConfig = JSON.parse(fs2.readFileSync(configPath, "utf8"));
|
|
160
|
+
}
|
|
161
|
+
const newConfig = { ...currentFileConfig, [key]: value };
|
|
162
|
+
fs2.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error("Failed to save configuration:", error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
loadEnvConfig() {
|
|
168
|
+
const config = {};
|
|
169
|
+
if (process.env.SHARK_LOG_LEVEL) {
|
|
170
|
+
config.logLevel = process.env.SHARK_LOG_LEVEL;
|
|
171
|
+
}
|
|
172
|
+
if (process.env.SHARK_API_BASE_URL) {
|
|
173
|
+
config.apiBaseUrl = process.env.SHARK_API_BASE_URL;
|
|
174
|
+
}
|
|
175
|
+
if (process.env.SHARK_LANGUAGE) {
|
|
176
|
+
config.language = process.env.SHARK_LANGUAGE;
|
|
177
|
+
}
|
|
178
|
+
if (process.env.SHARK_PREFERRED_STACK) {
|
|
179
|
+
config.preferredStack = process.env.SHARK_PREFERRED_STACK.split(",").map((s) => s.trim());
|
|
180
|
+
}
|
|
181
|
+
return config;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// src/core/i18n/locales/en-us.ts
|
|
186
|
+
var enUs = {
|
|
187
|
+
common: {
|
|
188
|
+
loading: "Loading...",
|
|
189
|
+
success: "Success",
|
|
190
|
+
error: "Error",
|
|
191
|
+
cancel: "Cancel",
|
|
192
|
+
operationCancelled: "Operation cancelled."
|
|
193
|
+
},
|
|
194
|
+
commands: {
|
|
195
|
+
config: {
|
|
196
|
+
title: "Shark AI Configuration",
|
|
197
|
+
selectAction: "What matches your needs?",
|
|
198
|
+
actions: {
|
|
199
|
+
language: "Set Language",
|
|
200
|
+
logLevel: "Log Level",
|
|
201
|
+
back: "Back"
|
|
202
|
+
},
|
|
203
|
+
selectLanguage: "Select language:",
|
|
204
|
+
languageUpdated: "Updated language to: {0}"
|
|
205
|
+
},
|
|
206
|
+
login: {
|
|
207
|
+
intro: "StackSpot Login",
|
|
208
|
+
alreadyLoggedIn: "You are already logged in",
|
|
209
|
+
success: "Successfully logged in!",
|
|
210
|
+
error: "Login failed"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/core/i18n/locales/es-es.ts
|
|
216
|
+
var esEs = {
|
|
217
|
+
common: {
|
|
218
|
+
loading: "Cargando...",
|
|
219
|
+
success: "\xC9xito",
|
|
220
|
+
error: "Error",
|
|
221
|
+
cancel: "Cancelar",
|
|
222
|
+
operationCancelled: "Operaci\xF3n cancelada."
|
|
223
|
+
},
|
|
224
|
+
commands: {
|
|
225
|
+
config: {
|
|
226
|
+
title: "Configuraci\xF3n Shark AI",
|
|
227
|
+
selectAction: "\xBFQu\xE9 desea configurar?",
|
|
228
|
+
actions: {
|
|
229
|
+
language: "Cambiar Idioma",
|
|
230
|
+
logLevel: "Nivel de Log",
|
|
231
|
+
back: "Volver"
|
|
232
|
+
},
|
|
233
|
+
selectLanguage: "Seleccione el idioma:",
|
|
234
|
+
languageUpdated: "Idioma actualizado a: {0}"
|
|
235
|
+
},
|
|
236
|
+
login: {
|
|
237
|
+
intro: "Login StackSpot",
|
|
238
|
+
alreadyLoggedIn: "Ya has iniciado sesi\xF3n",
|
|
239
|
+
success: "\xA1Inicio de sesi\xF3n exitoso!",
|
|
240
|
+
error: "Error de inicio de sesi\xF3n"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// src/core/i18n/locales/pt-br.ts
|
|
246
|
+
var ptBr = {
|
|
247
|
+
common: {
|
|
248
|
+
loading: "Carregando...",
|
|
249
|
+
success: "Sucesso",
|
|
250
|
+
error: "Erro",
|
|
251
|
+
cancel: "Cancelar",
|
|
252
|
+
operationCancelled: "Opera\xE7\xE3o cancelada."
|
|
253
|
+
},
|
|
254
|
+
commands: {
|
|
255
|
+
config: {
|
|
256
|
+
title: "Configura\xE7\xF5es do Shark AI",
|
|
257
|
+
selectAction: "O que voc\xEA deseja configurar?",
|
|
258
|
+
actions: {
|
|
259
|
+
language: "Alterar Idioma",
|
|
260
|
+
logLevel: "N\xEDvel de Log",
|
|
261
|
+
back: "Voltar"
|
|
262
|
+
},
|
|
263
|
+
selectLanguage: "Selecione o idioma:",
|
|
264
|
+
languageUpdated: "Idioma atualizado para: {0}"
|
|
265
|
+
},
|
|
266
|
+
login: {
|
|
267
|
+
intro: "Login StackSpot",
|
|
268
|
+
alreadyLoggedIn: "Voc\xEA j\xE1 est\xE1 logado",
|
|
269
|
+
success: "Login realizado com sucesso!",
|
|
270
|
+
error: "Falha no login"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// src/core/i18n/index.ts
|
|
276
|
+
var locales = {
|
|
277
|
+
"pt-br": ptBr,
|
|
278
|
+
"en-us": enUs,
|
|
279
|
+
"es-es": esEs
|
|
280
|
+
};
|
|
281
|
+
function getNestedValue(obj, path5) {
|
|
282
|
+
return path5.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
283
|
+
}
|
|
284
|
+
function t(key, ...args) {
|
|
285
|
+
const config = ConfigManager.getInstance().getConfig();
|
|
286
|
+
const lang = config.language || "pt-br";
|
|
287
|
+
const normalizedLang = lang.toLowerCase();
|
|
288
|
+
const locale = locales[normalizedLang] || locales["pt-br"];
|
|
289
|
+
let template = getNestedValue(locale, key);
|
|
290
|
+
if (!template) {
|
|
291
|
+
template = getNestedValue(locales["pt-br"], key);
|
|
292
|
+
}
|
|
293
|
+
if (!template) {
|
|
294
|
+
return key;
|
|
295
|
+
}
|
|
296
|
+
return template.replace(/\{(\d+)\}/g, (match, index) => {
|
|
297
|
+
return typeof args[index] !== "undefined" ? args[index] : match;
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/ui/tui.ts
|
|
28
302
|
var tui = {
|
|
29
303
|
intro(title) {
|
|
30
304
|
p.intro(colors.inverse(` ${title} `));
|
|
@@ -74,7 +348,7 @@ var tui = {
|
|
|
74
348
|
*/
|
|
75
349
|
handleCancel(value) {
|
|
76
350
|
if (p.isCancel(value)) {
|
|
77
|
-
p.cancel("
|
|
351
|
+
p.cancel(t("common.operationCancelled"));
|
|
78
352
|
process.exit(0);
|
|
79
353
|
}
|
|
80
354
|
},
|
|
@@ -112,80 +386,97 @@ async function authenticate(realm, clientId, clientSecret) {
|
|
|
112
386
|
}
|
|
113
387
|
|
|
114
388
|
// src/core/auth/token-storage.ts
|
|
115
|
-
import
|
|
116
|
-
|
|
117
|
-
|
|
389
|
+
import fs3 from "fs";
|
|
390
|
+
import path3 from "path";
|
|
391
|
+
import os3 from "os";
|
|
392
|
+
var SHARK_DIR = ".shark-ai";
|
|
393
|
+
var CREDENTIALS_FILE = "credentials.json";
|
|
118
394
|
var tokenStorage = {
|
|
119
395
|
/**
|
|
120
|
-
*
|
|
121
|
-
* Handles large tokens by splitting them into chunks if necessary.
|
|
396
|
+
* Get the secure file path
|
|
122
397
|
*/
|
|
123
|
-
|
|
398
|
+
getFilePath() {
|
|
399
|
+
const homeDir = os3.homedir();
|
|
400
|
+
const sharkDir = path3.join(homeDir, SHARK_DIR);
|
|
401
|
+
if (!fs3.existsSync(sharkDir)) {
|
|
402
|
+
fs3.mkdirSync(sharkDir, { mode: 448 });
|
|
403
|
+
}
|
|
404
|
+
return path3.join(sharkDir, CREDENTIALS_FILE);
|
|
405
|
+
},
|
|
406
|
+
/**
|
|
407
|
+
* Reads the credentials file safely
|
|
408
|
+
*/
|
|
409
|
+
readCredentials() {
|
|
410
|
+
const filePath = this.getFilePath();
|
|
124
411
|
try {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
await keytar.setPassword(SERVICE_NAME, realm, token);
|
|
128
|
-
} else {
|
|
129
|
-
const chunks = [];
|
|
130
|
-
for (let i = 0; i < token.length; i += CHUNK_SIZE) {
|
|
131
|
-
chunks.push(token.slice(i, i + CHUNK_SIZE));
|
|
132
|
-
}
|
|
133
|
-
await keytar.setPassword(SERVICE_NAME, `${realm}_meta_chunks`, chunks.length.toString());
|
|
134
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
135
|
-
await keytar.setPassword(SERVICE_NAME, `${realm}_chunk_${i}`, chunks[i]);
|
|
136
|
-
}
|
|
412
|
+
if (!fs3.existsSync(filePath)) {
|
|
413
|
+
return {};
|
|
137
414
|
}
|
|
415
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
416
|
+
return JSON.parse(content);
|
|
138
417
|
} catch (error) {
|
|
139
|
-
|
|
140
|
-
Original Error: ${error.message}`);
|
|
418
|
+
return {};
|
|
141
419
|
}
|
|
142
420
|
},
|
|
143
421
|
/**
|
|
144
|
-
*
|
|
145
|
-
* Reassembles chunks if they exist.
|
|
422
|
+
* Save credentials to file with 600 permissions
|
|
146
423
|
*/
|
|
147
|
-
|
|
424
|
+
writeCredentials(creds) {
|
|
425
|
+
const filePath = this.getFilePath();
|
|
148
426
|
try {
|
|
149
|
-
|
|
150
|
-
if (meta) {
|
|
151
|
-
const count = parseInt(meta, 10);
|
|
152
|
-
let fullToken = "";
|
|
153
|
-
for (let i = 0; i < count; i++) {
|
|
154
|
-
const chunk = await keytar.getPassword(SERVICE_NAME, `${realm}_chunk_${i}`);
|
|
155
|
-
if (!chunk) return null;
|
|
156
|
-
fullToken += chunk;
|
|
157
|
-
}
|
|
158
|
-
return fullToken;
|
|
159
|
-
} else {
|
|
160
|
-
return await keytar.getPassword(SERVICE_NAME, realm);
|
|
161
|
-
}
|
|
427
|
+
fs3.writeFileSync(filePath, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
162
428
|
} catch (error) {
|
|
163
|
-
|
|
164
|
-
return null;
|
|
429
|
+
throw new Error(`Failed to save credentials to ${filePath}: ${error.message}`);
|
|
165
430
|
}
|
|
166
431
|
},
|
|
167
432
|
/**
|
|
168
|
-
*
|
|
169
|
-
|
|
433
|
+
* Saves the access token (and optional client credentials) for the given realm.
|
|
434
|
+
*/
|
|
435
|
+
async saveToken(realm, token, clientId, clientKey, expiresIn) {
|
|
436
|
+
const creds = this.readCredentials();
|
|
437
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
438
|
+
const expiresAt = expiresIn ? now + expiresIn : void 0;
|
|
439
|
+
creds[realm] = {
|
|
440
|
+
...creds[realm],
|
|
441
|
+
// Preserve existing data if any
|
|
442
|
+
accessToken: token,
|
|
443
|
+
...clientId && { clientId },
|
|
444
|
+
...clientKey && { clientKey },
|
|
445
|
+
...expiresAt && { expiresAt }
|
|
446
|
+
};
|
|
447
|
+
this.writeCredentials(creds);
|
|
448
|
+
},
|
|
449
|
+
/**
|
|
450
|
+
* Retrieves the access token for the given realm.
|
|
451
|
+
* Checks ENV var first (SHARK_ACCESS_TOKEN).
|
|
452
|
+
*/
|
|
453
|
+
async getToken(realm) {
|
|
454
|
+
if (process.env.SHARK_ACCESS_TOKEN) {
|
|
455
|
+
return process.env.SHARK_ACCESS_TOKEN;
|
|
456
|
+
}
|
|
457
|
+
const creds = this.readCredentials();
|
|
458
|
+
const realmCreds = creds[realm];
|
|
459
|
+
if (!realmCreds) return null;
|
|
460
|
+
return realmCreds.accessToken;
|
|
461
|
+
},
|
|
462
|
+
/**
|
|
463
|
+
* Retrieves full credentials object (for refresh logic)
|
|
464
|
+
*/
|
|
465
|
+
async getCredentials(realm) {
|
|
466
|
+
const creds = this.readCredentials();
|
|
467
|
+
return creds[realm] || null;
|
|
468
|
+
},
|
|
469
|
+
/**
|
|
470
|
+
* Deletes the access token/credentials for the given realm.
|
|
170
471
|
*/
|
|
171
472
|
async deleteToken(realm) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
for (let i = 0; i < count; i++) {
|
|
178
|
-
await keytar.deletePassword(SERVICE_NAME, `${realm}_chunk_${i}`);
|
|
179
|
-
}
|
|
180
|
-
await keytar.deletePassword(SERVICE_NAME, `${realm}_meta_chunks`);
|
|
181
|
-
deletedSomething = true;
|
|
182
|
-
}
|
|
183
|
-
const standardDelete = await keytar.deletePassword(SERVICE_NAME, realm);
|
|
184
|
-
return deletedSomething || standardDelete;
|
|
185
|
-
} catch (error) {
|
|
186
|
-
console.warn(`WARNING: Failed to delete from OS Keychain (${error.message}).`);
|
|
187
|
-
return false;
|
|
473
|
+
const creds = this.readCredentials();
|
|
474
|
+
if (creds[realm]) {
|
|
475
|
+
delete creds[realm];
|
|
476
|
+
this.writeCredentials(creds);
|
|
477
|
+
return true;
|
|
188
478
|
}
|
|
479
|
+
return false;
|
|
189
480
|
}
|
|
190
481
|
};
|
|
191
482
|
|
|
@@ -236,167 +527,47 @@ var Connectivity = class _Connectivity {
|
|
|
236
527
|
};
|
|
237
528
|
var connectivity = Connectivity.getInstance();
|
|
238
529
|
|
|
239
|
-
// src/core/
|
|
240
|
-
import
|
|
241
|
-
import
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
logLevel: z.enum(["debug", "info", "warn", "error"]).default("info"),
|
|
248
|
-
preferredStack: z.array(z.string()).default([]),
|
|
249
|
-
apiBaseUrl: z.string().optional(),
|
|
250
|
-
language: z.enum(["pt-br", "en-us"]).default("pt-br"),
|
|
251
|
-
project: z.string().optional(),
|
|
252
|
-
environment: z.string().optional(),
|
|
253
|
-
activeRealm: z.string().optional()
|
|
254
|
-
// Currently logged-in realm
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
// src/core/config/sharkrc-loader.ts
|
|
258
|
-
import fs from "fs";
|
|
259
|
-
import path from "path";
|
|
260
|
-
import os from "os";
|
|
261
|
-
import { z as z2 } from "zod";
|
|
262
|
-
var SharkRCFileSchema = z2.object({
|
|
263
|
-
project: z2.string().optional(),
|
|
264
|
-
environment: z2.string().optional(),
|
|
265
|
-
logLevel: z2.string().optional(),
|
|
266
|
-
language: z2.string().optional(),
|
|
267
|
-
preferredStack: z2.array(z2.string()).optional()
|
|
268
|
-
// Add other keys as needed from the main config
|
|
269
|
-
}).passthrough();
|
|
270
|
-
function loadFile(filePath) {
|
|
271
|
-
try {
|
|
272
|
-
if (!fs.existsSync(filePath)) {
|
|
273
|
-
return {};
|
|
274
|
-
}
|
|
275
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
276
|
-
const json = JSON.parse(content);
|
|
277
|
-
const result = SharkRCFileSchema.safeParse(json);
|
|
278
|
-
if (result.success) {
|
|
279
|
-
return result.data;
|
|
280
|
-
} else {
|
|
281
|
-
console.warn(colors.warning(`\u26A0\uFE0F Invalid config in ${filePath}: ${result.error.message}`));
|
|
282
|
-
return {};
|
|
283
|
-
}
|
|
284
|
-
} catch (error) {
|
|
285
|
-
console.warn(colors.warning(`\u26A0\uFE0F Failed to read ${filePath}: ${error.message}`));
|
|
286
|
-
return {};
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
function loadSharkRC() {
|
|
290
|
-
const homeDir = os.homedir();
|
|
291
|
-
const currentDir = process.cwd();
|
|
292
|
-
const globalPath = path.join(homeDir, ".sharkrc");
|
|
293
|
-
const localPath = path.join(currentDir, ".sharkrc");
|
|
294
|
-
const globalConfig = loadFile(globalPath);
|
|
295
|
-
const localConfig = loadFile(localPath);
|
|
296
|
-
return {
|
|
297
|
-
...globalConfig,
|
|
298
|
-
...localConfig
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
function saveGlobalRC(configUpdates) {
|
|
302
|
-
const homeDir = os.homedir();
|
|
303
|
-
const globalPath = path.join(homeDir, ".sharkrc");
|
|
304
|
-
const currentConfig = loadFile(globalPath);
|
|
305
|
-
const newConfig = { ...currentConfig, ...configUpdates };
|
|
306
|
-
try {
|
|
307
|
-
fs.writeFileSync(globalPath, JSON.stringify(newConfig, null, 2), "utf-8");
|
|
308
|
-
} catch (error) {
|
|
309
|
-
throw new Error(`Failed to save global config to ${globalPath}: ${error.message}`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// src/core/config-manager.ts
|
|
314
|
-
var ConfigManager = class _ConfigManager {
|
|
315
|
-
static instance;
|
|
316
|
-
config = null;
|
|
317
|
-
constructor() {
|
|
318
|
-
}
|
|
319
|
-
static getInstance() {
|
|
320
|
-
if (!_ConfigManager.instance) {
|
|
321
|
-
_ConfigManager.instance = new _ConfigManager();
|
|
322
|
-
}
|
|
323
|
-
return _ConfigManager.instance;
|
|
324
|
-
}
|
|
325
|
-
getConfig() {
|
|
326
|
-
if (!this.config) {
|
|
327
|
-
this.config = this.loadConfig();
|
|
328
|
-
}
|
|
329
|
-
return this.config;
|
|
330
|
-
}
|
|
331
|
-
reloadConfig() {
|
|
332
|
-
this.config = this.loadConfig();
|
|
333
|
-
}
|
|
334
|
-
loadConfig() {
|
|
335
|
-
let mergedConfig = {};
|
|
336
|
-
const rcConfig = loadSharkRC();
|
|
337
|
-
mergedConfig = { ...mergedConfig, ...rcConfig };
|
|
338
|
-
const envConfig = this.loadEnvConfig();
|
|
339
|
-
mergedConfig = { ...mergedConfig, ...envConfig };
|
|
340
|
-
const parsed = ConfigSchema.safeParse(mergedConfig);
|
|
341
|
-
if (!parsed.success) {
|
|
342
|
-
console.warn("\u26A0\uFE0F Invalid configuration detected, falling back to defaults or partial config");
|
|
343
|
-
console.warn(parsed.error.message);
|
|
344
|
-
return ConfigSchema.parse({});
|
|
345
|
-
}
|
|
346
|
-
return parsed.data;
|
|
347
|
-
}
|
|
348
|
-
readJsonFile(filePath) {
|
|
530
|
+
// src/core/debug/file-logger.ts
|
|
531
|
+
import fs4 from "fs";
|
|
532
|
+
import path4 from "path";
|
|
533
|
+
var FileLogger = class {
|
|
534
|
+
static logPath = path4.resolve(process.cwd(), "shark-debug.log");
|
|
535
|
+
static enabled = true;
|
|
536
|
+
// Enabled by default for this debugging session
|
|
537
|
+
static init() {
|
|
349
538
|
try {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
} catch (error) {
|
|
355
|
-
console.warn(`Failed to read config file at ${filePath}`, error);
|
|
539
|
+
fs4.writeFileSync(this.logPath, `--- Shark CLI Debug Log Started at ${(/* @__PURE__ */ new Date()).toISOString()} ---
|
|
540
|
+
`);
|
|
541
|
+
} catch (e) {
|
|
542
|
+
console.error("Failed to initialize debug log:", e);
|
|
356
543
|
}
|
|
357
|
-
return {};
|
|
358
544
|
}
|
|
359
|
-
|
|
360
|
-
if (!this.
|
|
361
|
-
this.config = this.loadConfig();
|
|
362
|
-
}
|
|
363
|
-
this.config[key] = value;
|
|
364
|
-
const homeDir = os2.homedir();
|
|
365
|
-
const configPath = path2.join(homeDir, ".sharkrc");
|
|
545
|
+
static log(category, message, data) {
|
|
546
|
+
if (!this.enabled) return;
|
|
366
547
|
try {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
548
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
549
|
+
let logEntry = `[${timestamp}] [${category.toUpperCase()}] ${message}
|
|
550
|
+
`;
|
|
551
|
+
if (data !== void 0) {
|
|
552
|
+
if (typeof data === "object") {
|
|
553
|
+
logEntry += JSON.stringify(data, null, 2) + "\n";
|
|
554
|
+
} else {
|
|
555
|
+
logEntry += String(data) + "\n";
|
|
556
|
+
}
|
|
370
557
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
} catch (
|
|
374
|
-
console.error("Failed to save configuration:", error);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
loadEnvConfig() {
|
|
378
|
-
const config = {};
|
|
379
|
-
if (process.env.SHARK_LOG_LEVEL) {
|
|
380
|
-
config.logLevel = process.env.SHARK_LOG_LEVEL;
|
|
381
|
-
}
|
|
382
|
-
if (process.env.SHARK_API_BASE_URL) {
|
|
383
|
-
config.apiBaseUrl = process.env.SHARK_API_BASE_URL;
|
|
384
|
-
}
|
|
385
|
-
if (process.env.SHARK_LANGUAGE) {
|
|
386
|
-
config.language = process.env.SHARK_LANGUAGE;
|
|
558
|
+
logEntry += "-".repeat(40) + "\n";
|
|
559
|
+
fs4.appendFileSync(this.logPath, logEntry);
|
|
560
|
+
} catch (e) {
|
|
387
561
|
}
|
|
388
|
-
if (process.env.SHARK_PREFERRED_STACK) {
|
|
389
|
-
config.preferredStack = process.env.SHARK_PREFERRED_STACK.split(",").map((s) => s.trim());
|
|
390
|
-
}
|
|
391
|
-
return config;
|
|
392
562
|
}
|
|
393
563
|
};
|
|
394
564
|
|
|
395
565
|
// src/commands/login.ts
|
|
396
566
|
var loginCommand = new Command("login").description("Authenticate with StackSpot").action(async () => {
|
|
397
567
|
try {
|
|
568
|
+
FileLogger.init();
|
|
398
569
|
await connectivity.requireOnline();
|
|
399
|
-
tui.intro("
|
|
570
|
+
tui.intro(t("commands.login.intro"));
|
|
400
571
|
const realm = await tui.text({
|
|
401
572
|
message: "Account Realm (Slug)",
|
|
402
573
|
placeholder: "e.g. stackspot-freemium",
|
|
@@ -437,18 +608,27 @@ var loginCommand = new Command("login").description("Authenticate with StackSpot
|
|
|
437
608
|
clientId.trim(),
|
|
438
609
|
clientKey.trim()
|
|
439
610
|
);
|
|
440
|
-
|
|
611
|
+
FileLogger.log("LOGIN", "Authentication success", { realm });
|
|
612
|
+
await tokenStorage.saveToken(
|
|
613
|
+
realm,
|
|
614
|
+
tokens.access_token,
|
|
615
|
+
clientId,
|
|
616
|
+
clientKey,
|
|
617
|
+
tokens.expires_in
|
|
618
|
+
);
|
|
441
619
|
const configManager = ConfigManager.getInstance();
|
|
442
620
|
await configManager.set("activeRealm", realm);
|
|
443
|
-
spinner2.stop("
|
|
444
|
-
tui.outro(
|
|
621
|
+
spinner2.stop(t("commands.login.success"));
|
|
622
|
+
tui.outro(t("commands.login.success"));
|
|
445
623
|
} catch (error) {
|
|
446
|
-
spinner2.stop("
|
|
624
|
+
spinner2.stop(t("commands.login.error"), 1);
|
|
447
625
|
tui.log.error(error.message);
|
|
626
|
+
FileLogger.log("LOGIN", "Authentication failed", error);
|
|
448
627
|
process.exit(1);
|
|
449
628
|
}
|
|
450
629
|
} catch (error) {
|
|
451
630
|
tui.log.error(error.message);
|
|
631
|
+
FileLogger.log("LOGIN", "Unexpected error", error);
|
|
452
632
|
process.exit(1);
|
|
453
633
|
}
|
|
454
634
|
});
|
|
@@ -456,7 +636,7 @@ var loginCommand = new Command("login").description("Authenticate with StackSpot
|
|
|
456
636
|
// src/commands/config.ts
|
|
457
637
|
var configCommand = {
|
|
458
638
|
action: async () => {
|
|
459
|
-
tui.intro("
|
|
639
|
+
tui.intro(t("commands.config.title"));
|
|
460
640
|
const manager = ConfigManager.getInstance();
|
|
461
641
|
const currentConfig = manager.getConfig();
|
|
462
642
|
tui.log.info(colors.dim("Current Configuration:"));
|
|
@@ -464,12 +644,12 @@ var configCommand = {
|
|
|
464
644
|
tui.log.message(`\u2022 Language: ${colors.primary(currentConfig.language)}`);
|
|
465
645
|
tui.log.message(`\u2022 Log Level: ${colors.primary(currentConfig.logLevel)}`);
|
|
466
646
|
const action = await tui.select({
|
|
467
|
-
message: "
|
|
647
|
+
message: t("commands.config.selectAction"),
|
|
468
648
|
options: [
|
|
469
649
|
{ value: "project", label: "Set Default Project" },
|
|
470
|
-
{ value: "language", label: "
|
|
471
|
-
{ value: "logLevel", label: "
|
|
472
|
-
{ value: "exit", label: "
|
|
650
|
+
{ value: "language", label: t("commands.config.actions.language") },
|
|
651
|
+
{ value: "logLevel", label: t("commands.config.actions.logLevel") },
|
|
652
|
+
{ value: "exit", label: t("commands.config.actions.back") }
|
|
473
653
|
]
|
|
474
654
|
});
|
|
475
655
|
if (tui.isCancel(action) || action === "exit") {
|
|
@@ -490,16 +670,17 @@ var configCommand = {
|
|
|
490
670
|
}
|
|
491
671
|
if (action === "language") {
|
|
492
672
|
const lang = await tui.select({
|
|
493
|
-
message: "
|
|
673
|
+
message: t("commands.config.selectLanguage"),
|
|
494
674
|
options: [
|
|
495
|
-
{ value: "pt-br", label: "
|
|
496
|
-
{ value: "en-us", label: "English (US)" }
|
|
675
|
+
{ value: "pt-br", label: "Portugu\xEAs (Brasil)" },
|
|
676
|
+
{ value: "en-us", label: "English (US)" },
|
|
677
|
+
{ value: "es-es", label: "Espa\xF1ol" }
|
|
497
678
|
],
|
|
498
679
|
initialValue: currentConfig.language
|
|
499
680
|
});
|
|
500
681
|
if (!tui.isCancel(lang)) {
|
|
501
682
|
saveGlobalRC({ language: lang });
|
|
502
|
-
tui.log.success(
|
|
683
|
+
tui.log.success(t("commands.config.languageUpdated", lang));
|
|
503
684
|
}
|
|
504
685
|
}
|
|
505
686
|
if (action === "logLevel") {
|
|
@@ -529,10 +710,11 @@ var configCommand = {
|
|
|
529
710
|
|
|
530
711
|
export {
|
|
531
712
|
colors,
|
|
713
|
+
ConfigManager,
|
|
532
714
|
tui,
|
|
533
715
|
tokenStorage,
|
|
534
|
-
|
|
716
|
+
FileLogger,
|
|
535
717
|
loginCommand,
|
|
536
718
|
configCommand
|
|
537
719
|
};
|
|
538
|
-
//# sourceMappingURL=chunk-
|
|
720
|
+
//# sourceMappingURL=chunk-R3MIUNVD.js.map
|