repofence 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -119,9 +119,9 @@ program
119
119
  // Auth command
120
120
  program
121
121
  .command('auth')
122
- .description('Autentica el CLI con token emitido por la web repofence')
123
- .option('--token <token>', 'Token emitido por la web repofence')
124
- .option('--web', 'Abrir la web de login/pago y obtener token manualmente')
122
+ .description('Authenticate the CLI with a token from repofence.com')
123
+ .option('--token <token>', 'Token from repofence.com (after purchase)')
124
+ .option('--web', 'Open login/checkout in the browser to get a token')
125
125
  .action(async (options) => {
126
126
  const cwd = process.cwd();
127
127
  try {
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const promises_1 = __importDefault(require("readline/promises"));
9
9
  const process_1 = require("process");
10
10
  const auth_1 = require("../core/auth");
11
+ const api_client_1 = require("../core/api-client");
11
12
  const open_1 = __importDefault(require("open"));
12
13
  const promptToken = async () => {
13
14
  const rl = promises_1.default.createInterface({ input: process_1.stdin, output: process_1.stdout });
@@ -15,6 +16,7 @@ const promptToken = async () => {
15
16
  rl.close();
16
17
  return answer;
17
18
  };
19
+ const isJwt = (token) => token.split('.').length === 3;
18
20
  const authCommand = async (_cwd, options) => {
19
21
  if (options.web) {
20
22
  const authBase = process.env.REPOFENCE_AUTH_URL || 'https://repofence.com/pricing';
@@ -34,12 +36,30 @@ const authCommand = async (_cwd, options) => {
34
36
  console.log(chalk_1.default.dim('─'.repeat(60)) + '\n');
35
37
  return;
36
38
  }
37
- const token = options.token ?? (await promptToken());
38
- if (!token) {
39
- throw new Error('No se proporcionó token; vuelve a intentarlo con --token <valor>.');
39
+ const rawToken = options.token ?? (await promptToken());
40
+ if (!rawToken) {
41
+ throw new Error('No token provided; try again with --token <value>.');
42
+ }
43
+ // Raw license tokens (hex) need to be exchanged for a JWT via /activate.
44
+ // Already-valid JWTs (e.g. from refresh) are saved directly.
45
+ if (isJwt(rawToken)) {
46
+ await (0, auth_1.saveToken)(rawToken);
47
+ console.log(chalk_1.default.green(`✅ Token saved to ${auth_1.tokenPath}`));
48
+ return;
49
+ }
50
+ process.stdout.write('Activating license...');
51
+ try {
52
+ const client = new api_client_1.ApiClient();
53
+ const { token: jwt } = await client.activateToken(rawToken);
54
+ await (0, auth_1.saveToken)(jwt);
55
+ process.stdout.write('\r');
56
+ console.log(chalk_1.default.green(`✅ License activated and token saved to ${auth_1.tokenPath}`));
57
+ }
58
+ catch (error) {
59
+ process.stdout.write('\r');
60
+ const message = error instanceof Error ? error.message : String(error);
61
+ throw new Error(`Failed to activate license: ${message}`);
40
62
  }
41
- await (0, auth_1.saveToken)(token);
42
- console.log(chalk_1.default.green(`✅ Token guardado en ${auth_1.tokenPath}`));
43
63
  };
44
64
  exports.authCommand = authCommand;
45
65
  //# sourceMappingURL=auth.js.map
@@ -185,17 +185,17 @@ async function initCommand(cwd, options) {
185
185
  }
186
186
  catch (error) {
187
187
  const message = error instanceof Error ? error.message : String(error);
188
- console.error(chalk_1.default.red(`❌ Error al descargar pack "${packId}" (${resolvedBase}): ${message}`));
188
+ console.error(chalk_1.default.red(`❌ Failed to download pack "${packId}" (${resolvedBase}): ${message}`));
189
189
  throw error;
190
190
  }
191
191
  const isValid = await (0, pack_manager_1.validateSignature)(pack);
192
192
  if (!isValid) {
193
- throw new Error('La firma del pack no es válida.');
193
+ throw new Error('Invalid pack signature.');
194
194
  }
195
195
  await (0, pack_manager_1.installPack)(pack, { force, verbose, target: ide }, profile.installBaseDir);
196
196
  packInstalled = true;
197
197
  if (verbose) {
198
- console.log(chalk_1.default.green(`✓ Pack "${pack.packId}" instalado`));
198
+ console.log(chalk_1.default.green(`✓ Pack "${pack.packId}" installed`));
199
199
  }
200
200
  }
201
201
  else {
@@ -13,13 +13,13 @@ const printManifest = async (target) => {
13
13
  const manifest = await (0, pack_manager_1.readManifest)(profile.installBaseDir);
14
14
  console.log(chalk_1.default.bold(`\n[${profile.displayName}]`));
15
15
  if (!manifest) {
16
- console.log(chalk_1.default.yellow(`⚠️ No se encontró manifest de pack en ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
16
+ console.log(chalk_1.default.yellow(`⚠️ No pack manifest found at ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
17
17
  return;
18
18
  }
19
19
  console.log(chalk_1.default.cyan(`Pack: ${manifest.pack_id} v${manifest.pack_version}`));
20
- console.log(chalk_1.default.cyan(`Instalado: ${manifest.installed_at}`));
21
- console.log(chalk_1.default.cyan(`Expira: ${manifest.expires_at ?? 'sin expirar'}`));
22
- console.log(chalk_1.default.cyan(`Artefactos:`));
20
+ console.log(chalk_1.default.cyan(`Installed: ${manifest.installed_at}`));
21
+ console.log(chalk_1.default.cyan(`Expires: ${manifest.expires_at ?? 'never'}`));
22
+ console.log(chalk_1.default.cyan(`Artifacts:`));
23
23
  manifest.commands.forEach((cmd) => console.log(` - ${cmd}`));
24
24
  console.log(chalk_1.default.gray(`Manifest: ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
25
25
  };
@@ -28,10 +28,10 @@ const statusCommand = async (cwd, options = {}) => {
28
28
  const target = options.ide || 'all';
29
29
  const token = await (0, auth_1.readToken)();
30
30
  if (token) {
31
- console.log(chalk_1.default.green(`✅ Token presente (${auth_1.tokenPath})`));
31
+ console.log(chalk_1.default.green(`✅ Token present (${auth_1.tokenPath})`));
32
32
  }
33
33
  else {
34
- console.log(chalk_1.default.yellow('⚠️ Token no configurado. Ejecuta `repofence auth --token <valor>`'));
34
+ console.log(chalk_1.default.yellow('⚠️ No token configured. Run `repofence auth --token <value>`'));
35
35
  }
36
36
  if (target === 'all') {
37
37
  await printManifest('cursor');
@@ -21,16 +21,16 @@ const DEMO_PACK = {
21
21
  files: [
22
22
  {
23
23
  path: 'repofence-spec.md',
24
- contentBase64: Buffer.from('# repofence spec (demo)\n\n_TODO: contenido del pack_\n').toString('base64'),
24
+ contentBase64: Buffer.from('# repofence spec (demo)\n\n_TODO: pack content_\n').toString('base64'),
25
25
  },
26
26
  {
27
27
  path: 'repofence-plan.md',
28
- contentBase64: Buffer.from('# repofence plan (demo)\n\n_TODO: contenido del pack_\n').toString('base64'),
28
+ contentBase64: Buffer.from('# repofence plan (demo)\n\n_TODO: pack content_\n').toString('base64'),
29
29
  },
30
30
  ],
31
31
  };
32
32
  const notImplemented = () => {
33
- throw new Error('API client no implementado. Usa REPOFENCE_MOCK=1 para datos dummy.');
33
+ throw new Error('API client not implemented. Set REPOFENCE_MOCK=1 for dummy data.');
34
34
  };
35
35
  /**
36
36
  * Production hosts expose routes under `/api/v1/...`.
@@ -86,6 +86,15 @@ class ApiClient {
86
86
  }
87
87
  return json;
88
88
  }
89
+ async activateToken(rawToken) {
90
+ if (process.env.REPOFENCE_MOCK === '1') {
91
+ return { token: 'demo-token', license_id: 'lic-demo', packs: ['core'] };
92
+ }
93
+ return this.request('/v1/auth/activate', {
94
+ method: 'POST',
95
+ body: JSON.stringify({ token: rawToken }),
96
+ });
97
+ }
89
98
  async loginWithEmail(email) {
90
99
  if (process.env.REPOFENCE_MOCK === '1') {
91
100
  return { token: 'demo-token', license_id: 'lic-demo', packs: ['core'] };
package/dist/core/auth.js CHANGED
@@ -16,7 +16,7 @@ exports.tokenPath = tokenFile;
16
16
  const saveToken = async (token) => {
17
17
  const normalized = token.trim();
18
18
  if (!normalized) {
19
- throw new Error('Token vacío; provee un token válido.');
19
+ throw new Error('Empty token; provide a valid token.');
20
20
  }
21
21
  await ensureDir();
22
22
  await promises_1.default.writeFile(tokenFile, normalized, { mode: 0o600 });
@@ -49,7 +49,7 @@ const sha256Base64 = (buf) => crypto_1.default.createHash('sha256').update(buf).
49
49
  const downloadBuffer = async (url) => {
50
50
  const res = await fetch(url);
51
51
  if (!res.ok) {
52
- throw new Error(`Descarga fallida (${res.status}) ${res.statusText}`);
52
+ throw new Error(`Download failed (${res.status}) ${res.statusText}`);
53
53
  }
54
54
  const arrayBuffer = await res.arrayBuffer();
55
55
  return Buffer.from(arrayBuffer);
@@ -137,29 +137,29 @@ const installFromArchive = async (pack, options, baseDir) => {
137
137
  const dir = commandsDir(baseDir);
138
138
  await promises_1.default.mkdir(dir, { recursive: true });
139
139
  const archiveUrl = pack.archiveUrl;
140
- // Descargar manifest y recalcular hash (robustez)
140
+ // Download manifest and recalculate hash (robustness)
141
141
  let manifestHashCalculated = undefined;
142
142
  if (pack.manifestUrl) {
143
143
  const manifestBuf = await downloadBuffer(pack.manifestUrl);
144
144
  manifestHashCalculated = sha256Base64(manifestBuf);
145
145
  if (pack.manifestHash && pack.manifestHash !== manifestHashCalculated) {
146
- throw new Error(`Hash de manifest inválido. Esperado ${pack.manifestHash} obtenido ${manifestHashCalculated}`);
146
+ throw new Error(`Invalid manifest hash. Expected ${pack.manifestHash}, got ${manifestHashCalculated}`);
147
147
  }
148
148
  }
149
149
  const archiveBuf = await downloadBuffer(archiveUrl);
150
150
  if (pack.archiveHash) {
151
151
  const hash = sha256Base64(archiveBuf);
152
152
  if (hash !== pack.archiveHash) {
153
- throw new Error(`Hash de archive inválido. Esperado ${pack.archiveHash} obtenido ${hash}`);
153
+ throw new Error(`Invalid archive hash. Expected ${pack.archiveHash}, got ${hash}`);
154
154
  }
155
155
  }
156
- // Extraer lista de archivos y luego extraer contenido
156
+ // List files, then extract archive contents
157
157
  const tmpDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'repofence-archive-'));
158
158
  const archivePath = path_1.default.join(tmpDir, 'pack.tar.gz');
159
159
  const extractedPath = path_1.default.join(tmpDir, 'extracted');
160
160
  await promises_1.default.mkdir(extractedPath, { recursive: true });
161
161
  await promises_1.default.writeFile(archivePath, archiveBuf);
162
- // Validación de firma (manifestHash ya viene en el pack; archiveHash validada arriba)
162
+ // Signature validation (manifestHash from pack; archiveHash validated above)
163
163
  await (0, exports.validateSignature)({
164
164
  ...pack,
165
165
  manifestHash: pack.manifestHash || manifestHashCalculated,
@@ -173,7 +173,7 @@ const installFromArchive = async (pack, options, baseDir) => {
173
173
  : await installCursorFromExtracted(extractedPath, pack, baseDir);
174
174
  await promises_1.default.rm(tmpDir, { recursive: true, force: true });
175
175
  if (options.verbose) {
176
- console.log(`[repofence] instalado desde archive ${archiveUrl}`);
176
+ console.log(`[repofence] installed from archive ${archiveUrl}`);
177
177
  }
178
178
  return manifest;
179
179
  };
@@ -209,7 +209,7 @@ const installClaudeFromExtracted = async (extractedPath, pack, baseDir, options)
209
209
  const { skillName, content } = toClaudeSkillMarkdown(rel, sourceMarkdown);
210
210
  const targetPath = path_1.default.join(dir, skillName, 'SKILL.md');
211
211
  if (!options.force && (await fileExists(targetPath))) {
212
- throw new Error(`El skill ya existe: ${targetPath} (usa --force para sobrescribir)`);
212
+ throw new Error(`Skill already exists: ${targetPath} (use --force to overwrite)`);
213
213
  }
214
214
  await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
215
215
  await promises_1.default.writeFile(targetPath, content, 'utf8');
@@ -227,7 +227,7 @@ const installClaudeFromExtracted = async (extractedPath, pack, baseDir, options)
227
227
  };
228
228
  const installPack = async (pack, options = {}, baseDir) => {
229
229
  const target = options.target || 'cursor';
230
- // Si viene archiveUrl, descargar tar.gz y extraer.
230
+ // If archiveUrl is set, download tar.gz and extract.
231
231
  if (pack.archiveUrl) {
232
232
  return installFromArchive(pack, options, baseDir);
233
233
  }
@@ -240,13 +240,13 @@ const installPack = async (pack, options = {}, baseDir) => {
240
240
  const { skillName, content } = toClaudeSkillMarkdown(file.path, source);
241
241
  const targetPath = path_1.default.join(dir, skillName, 'SKILL.md');
242
242
  if (!options.force && (await fileExists(targetPath))) {
243
- throw new Error(`El skill ya existe: ${targetPath} (usa --force para sobrescribir)`);
243
+ throw new Error(`Skill already exists: ${targetPath} (use --force to overwrite)`);
244
244
  }
245
245
  await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
246
246
  await promises_1.default.writeFile(targetPath, content, 'utf8');
247
247
  artifacts.push(path_1.default.relative(dir, targetPath));
248
248
  if (options.verbose) {
249
- console.log(`[repofence] skill escrito ${targetPath}`);
249
+ console.log(`[repofence] wrote skill ${targetPath}`);
250
250
  }
251
251
  }
252
252
  const manifest = {
@@ -262,13 +262,13 @@ const installPack = async (pack, options = {}, baseDir) => {
262
262
  for (const file of pack.files) {
263
263
  const target = path_1.default.join(dir, file.path);
264
264
  if (!options.force && (await fileExists(target))) {
265
- throw new Error(`El archivo ya existe: ${target} (usa --force para sobrescribir)`);
265
+ throw new Error(`File already exists: ${target} (use --force to overwrite)`);
266
266
  }
267
267
  await promises_1.default.mkdir(path_1.default.dirname(target), { recursive: true });
268
268
  const buffer = Buffer.from(file.contentBase64, 'base64');
269
269
  await promises_1.default.writeFile(target, buffer);
270
270
  if (options.verbose) {
271
- console.log(`[repofence] escrito ${target}`);
271
+ console.log(`[repofence] wrote ${target}`);
272
272
  }
273
273
  }
274
274
  const manifest = {
@@ -286,11 +286,11 @@ const manifestFilePath = (baseDir) => manifestPath(baseDir);
286
286
  exports.manifestFilePath = manifestFilePath;
287
287
  const validateSignature = async (_pack) => {
288
288
  const pack = _pack;
289
- // Si no hay firma ni hashes, no validamos (modo demo/mock).
289
+ // If there is no signature or hashes, skip validation (demo/mock mode).
290
290
  if (!pack.signature || !pack.manifestHash) {
291
291
  return true;
292
292
  }
293
- // Construir payload igual que en backend: manifestHash[:archiveHash]
293
+ // Build payload same as backend: manifestHash[:archiveHash]
294
294
  const payloadToVerify = pack.archiveHash
295
295
  ? `${pack.manifestHash}:${pack.archiveHash}`
296
296
  : pack.manifestHash;
@@ -302,12 +302,12 @@ const validateSignature = async (_pack) => {
302
302
  const keyObject = crypto_1.default.createPublicKey(publicKeyPem);
303
303
  const isValid = crypto_1.default.verify(null, Buffer.from(payloadToVerify), keyObject, Buffer.from(pack.signature, 'base64'));
304
304
  if (!isValid) {
305
- throw new Error('Firma de pack inválida.');
305
+ throw new Error('Invalid pack signature.');
306
306
  }
307
307
  return true;
308
308
  }
309
309
  catch (error) {
310
- const msg = error instanceof Error ? error.message : 'error_validando_firma';
310
+ const msg = error instanceof Error ? error.message : 'signature_validation_error';
311
311
  throw new Error(msg);
312
312
  }
313
313
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repofence",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Repofence CLI (packs + backend auth)",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -33,10 +33,12 @@
33
33
  "dependencies": {
34
34
  "@aws-sdk/client-s3": "^3.705.0",
35
35
  "@aws-sdk/s3-request-presigner": "^3.705.0",
36
+ "@supabase/supabase-js": "^2.103.0",
36
37
  "chalk": "^4.1.2",
37
38
  "commander": "^11.1.0",
38
39
  "dotenv": "^16.4.5",
39
40
  "open": "^11.0.0",
41
+ "resend": "^6.10.0",
40
42
  "tar": "^6.2.1"
41
43
  },
42
44
  "devDependencies": {