supcoflow 0.7.0 → 0.8.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.
@@ -1,7 +1,8 @@
1
1
  import { FalloDeLaTerminal, resultadoDeFallo, SALIDA, taparSecretos } from "../contrato.js";
2
+ import { NOMBRE_DE_LA_FUENTE } from "../guardado/index.js";
2
3
  import { PLAZO_MINIMO_MS, RESERVA_PARA_CERRAR_MS, sondearUnTramo, TRAMO_MAXIMO_MS } from "../sondeo.js";
3
4
  const TECHO_DE_UN_PASO_MS = 5_000;
4
- const VOLVER_A_EMPEZAR = "Volvé a empezar con `supcoflow login --start`.";
5
+ const VOLVER_A_EMPEZAR = "Start over with `supcoflow login --start`.";
5
6
  const fallo = (codigo, error, message, extra = {}) => ({
6
7
  codigo, json: { ok: false, error, message, ...extra }, texto: message,
7
8
  });
@@ -12,7 +13,7 @@ const motivo = (e) => e instanceof FalloDeLaTerminal ? e.message : taparSecretos
12
13
  export async function reintentarPorRevocar(d, limite) {
13
14
  const origen = d.api.origin;
14
15
  const noSe = () => {
15
- d.avisar("No pude leer entera la lista de claves por revocar (el llavero no respondió); la reintento la próxima vez.");
16
+ d.avisar("I couldn't read the whole list of keys waiting to be revoked (the keychain didn't respond); I'll try again next time.");
16
17
  };
17
18
  let lectura;
18
19
  try {
@@ -54,8 +55,8 @@ export async function reintentarPorRevocar(d, limite) {
54
55
  export async function loginStart(d, flags) {
55
56
  const origen = d.api.origin;
56
57
  if (flags.dryRun) {
57
- const haria = `pedir un código de dispositivo a ${origen}`;
58
- return { codigo: SALIDA.ok, json: { ok: true, dry_run: true, haria }, texto: `Con --dry-run no hago nada; sin él, iría a ${haria}.` };
58
+ const haria = `ask ${origen} for a device code`;
59
+ return { codigo: SALIDA.ok, json: { ok: true, dry_run: true, haria }, texto: `With --dry-run I do nothing; without it: ${haria}.` };
59
60
  }
60
61
  await reintentarPorRevocar(d, d.ahora() + 15_000);
61
62
  let c;
@@ -75,7 +76,7 @@ export async function loginStart(d, flags) {
75
76
  await d.guardado.guardarPendiente(origen, pendiente);
76
77
  }
77
78
  catch (e) {
78
- return fallo(SALIDA.inesperado, e instanceof FalloDeLaTerminal ? e.error : "no_se_pudo_guardar", `No pude guardar el inicio de sesión en curso: ${motivo(e)}`);
79
+ return fallo(SALIDA.inesperado, e instanceof FalloDeLaTerminal ? e.error : "no_se_pudo_guardar", `I couldn't save the login in progress: ${motivo(e)}`);
79
80
  }
80
81
  return {
81
82
  codigo: SALIDA.esperandoAUnaPersona,
@@ -83,7 +84,7 @@ export async function loginStart(d, flags) {
83
84
  ok: false, esperando: "persona", verification_uri: c.verificationUri,
84
85
  verification_uri_complete: c.verificationUriComplete, user_code: c.userCode, expires_in: c.expiresIn,
85
86
  },
86
- texto: `Abrí ${c.verificationUriComplete} y confirmá el código ${c.userCode}. Después corré \`supcoflow login --wait\`.`,
87
+ texto: `Open ${c.verificationUriComplete} and confirm the code ${c.userCode}. Then run \`supcoflow login --wait\`.`,
87
88
  };
88
89
  }
89
90
  export async function loginWait(d) {
@@ -96,7 +97,7 @@ export async function loginWait(d) {
96
97
  const techo = () => Math.max(0, Math.min(TECHO_DE_UN_PASO_MS, limite - d.ahora()));
97
98
  const p = await d.guardado.leerPendiente(origen);
98
99
  if (p === null) {
99
- return fallo(SALIDA.entradaInvalida, "sin_login_en_curso", "No hay un inicio de sesión en curso: empezá con `supcoflow login --start`.");
100
+ return fallo(SALIDA.entradaInvalida, "sin_login_en_curso", "There is no login in progress: start with `supcoflow login --start`.");
100
101
  }
101
102
  // La anterior se lee ANTES de sondear, así no consume la reserva del cierre. Se pide por
102
103
  // `leerClaveCompleta` para saber de paso si SUPCOFLOW_TOKEN está definida (sin costo: la variable
@@ -110,9 +111,9 @@ export async function loginWait(d) {
110
111
  // y sin borrar el pendiente, así el mismo `--wait` sirve cuando el llavero vuelva.
111
112
  if (!lectura.completa) {
112
113
  const salida = conVariable
113
- ? "desbloqueá el llavero y volvé a llamar `supcoflow login --wait`."
114
- : "desbloqueá el llavero y volvé a llamar `supcoflow login --wait` (o, sin login, exportá SUPCOFLOW_TOKEN con una clave vigente).";
115
- return fallo(SALIDA.inesperado, "no_pude_leer_la_clave", `No pude leer tu clave guardada (el llavero no respondió), y sin saber cuál es no la puedo reemplazar sin dejarla viva: ${salida}`);
114
+ ? "unlock the keychain and run `supcoflow login --wait` again."
115
+ : "unlock the keychain and run `supcoflow login --wait` again (or, without logging in, export SUPCOFLOW_TOKEN with a valid key).";
116
+ return fallo(SALIDA.inesperado, "no_pude_leer_la_clave", `I couldn't read your saved key (the keychain didn't respond), and without knowing which one it is I can't replace it without leaving it active: ${salida}`);
116
117
  }
117
118
  const anterior = lectura.valor;
118
119
  let fin;
@@ -132,26 +133,26 @@ export async function loginWait(d) {
132
133
  return {
133
134
  codigo: SALIDA.esperandoAUnaPersona,
134
135
  json: { ok: false, esperando: "persona", user_code: fin.pendiente.userCode, verification_uri_complete: fin.pendiente.verificationUriComplete },
135
- texto: `Todavía no se confirmó. Abrí ${fin.pendiente.verificationUriComplete} y confirmá el código ${fin.pendiente.userCode}; después volvé a correr \`supcoflow login --wait\`.`,
136
+ texto: `Not confirmed yet. Open ${fin.pendiente.verificationUriComplete} and confirm the code ${fin.pendiente.userCode}; then run \`supcoflow login --wait\` again.`,
136
137
  };
137
138
  case "token": {
138
139
  const r = await reemplazarClave(d, { clave: fin.clave, equipo: fin.equipo }, anterior?.clave ?? null, limite);
139
140
  // Va acá y no adentro de `reemplazarClave` porque es acá donde se supo, antes de sondear, sin
140
141
  // gastar reserva: preguntarlo al final costaría otra lectura lenta del guardado.
141
142
  if (conVariable && r.json.guardada_en !== undefined) {
142
- d.avisar("SUPCOFLOW_TOKEN está definida y tiene prioridad sobre la clave que acabo de guardar.");
143
+ d.avisar("SUPCOFLOW_TOKEN is set and takes priority over the key I just saved.");
143
144
  }
144
145
  return r;
145
146
  }
146
147
  case "rechazado":
147
148
  await d.guardado.borrarPendiente(origen, techo());
148
- return fallo(SALIDA.haceFaltaUnaPersona, "acceso_rechazado", "Rechazaron el acceso desde la consola; tu sesión anterior, si había, sigue igual.");
149
+ return fallo(SALIDA.haceFaltaUnaPersona, "acceso_rechazado", "Access was denied in the console; your previous session, if there was one, is unchanged.");
149
150
  case "vencido":
150
151
  await d.guardado.borrarPendiente(origen, techo());
151
- return fallo(SALIDA.faltaLogin, "codigo_vencido", `El código venció. ${VOLVER_A_EMPEZAR}`);
152
+ return fallo(SALIDA.faltaLogin, "codigo_vencido", `The code has expired. ${VOLVER_A_EMPEZAR}`);
152
153
  case "invalido":
153
154
  await d.guardado.borrarPendiente(origen, techo());
154
- return fallo(SALIDA.faltaLogin, "codigo_invalido", `El código ya no vale. ${VOLVER_A_EMPEZAR}`);
155
+ return fallo(SALIDA.faltaLogin, "codigo_invalido", `The code is no longer valid. ${VOLVER_A_EMPEZAR}`);
155
156
  }
156
157
  }
157
158
  // El login de una persona frente a la terminal: los mismos dos pasos, encadenados. Sin TTY (o con
@@ -160,14 +161,14 @@ export async function loginWait(d) {
160
161
  const PAUSA_ENTRE_TRAMOS_MS = 1_000;
161
162
  export async function loginInteractivo(d, flags) {
162
163
  if (!d.esTty || flags.json) {
163
- return fallo(SALIDA.entradaInvalida, "hace_falta_tty", "Sin terminal interactiva usá `supcoflow login --start --json` y después `supcoflow login --wait --json`.");
164
+ return fallo(SALIDA.entradaInvalida, "hace_falta_tty", "Without an interactive terminal, use `supcoflow login --start --json` and then `supcoflow login --wait --json`.");
164
165
  }
165
166
  const inicio = await loginStart(d, flags);
166
167
  if (inicio.codigo !== SALIDA.esperandoAUnaPersona)
167
168
  return inicio;
168
169
  // El texto de `--start` le dice a la persona que corra `--wait`; acá eso lo hace el comando solo.
169
170
  const { verification_uri_complete: url, user_code: codigo } = inicio.json;
170
- d.mostrar(`Abrí ${String(url)} y confirmá el código ${String(codigo)}. Espero acá…`);
171
+ d.mostrar(`Open ${String(url)} and confirm the code ${String(codigo)}. I'll wait here...`);
171
172
  // Cada tramo termina por su cuenta (≤ 90 s) y el código vence a los pocos minutos, así que el
172
173
  // bucle tiene fin aunque la persona nunca conteste: termina en `codigo_vencido`.
173
174
  let r;
@@ -206,9 +207,9 @@ export async function reemplazarClave(d, nueva, anterior, limite) {
206
207
  revocada = false;
207
208
  }
208
209
  const despues = revocada
209
- ? "La revoqué para que no quede viva; tu sesión anterior, si había, sigue como estaba."
210
- : "Tampoco pude revocarla: revocala en la consola, en «Accesos desde la terminal».";
211
- return fallo(SALIDA.inesperado, "no_se_pudo_guardar", `No pude guardar la clave nueva (${motivo(e)}). ${despues}`);
210
+ ? "I revoked it so it doesn't stay active; your previous session, if there was one, is as it was."
211
+ : "I couldn't revoke it either: revoke it in the console, under \"Accesos desde la terminal\" (the console is in Spanish).";
212
+ return fallo(SALIDA.inesperado, "no_se_pudo_guardar", `I couldn't save the new key (${motivo(e)}). ${despues}`);
212
213
  }
213
214
  // 2. Revocar la anterior, o anotarla.
214
215
  let anteriorRevocada = null;
@@ -228,10 +229,10 @@ export async function reemplazarClave(d, nueva, anterior, limite) {
228
229
  try {
229
230
  await d.guardado.anotarPorRevocar(origen, anterior, techo());
230
231
  quedaPorRevocar = true;
231
- d.avisar("No pude revocar tu clave anterior; quedó anotada y la reintento en el próximo login o logout.");
232
+ d.avisar("I couldn't revoke your previous key; it's noted down and I'll try again on the next login or logout.");
232
233
  }
233
234
  catch {
234
- const message = "No pude revocar ni anotar tu clave anterior: revocala en la consola, en «Accesos desde la terminal».";
235
+ const message = "I couldn't revoke or note down your previous key: revoke it in the console, under \"Accesos desde la terminal\" (the console is in Spanish).";
235
236
  d.avisar(message);
236
237
  if (queda() >= PLAZO_MINIMO_MS)
237
238
  await d.guardado.borrarPendiente(origen, techo());
@@ -245,10 +246,10 @@ export async function reemplazarClave(d, nueva, anterior, limite) {
245
246
  // 3. Borrar el pendiente; su `false` no cambia nada.
246
247
  if (queda() >= PLAZO_MINIMO_MS)
247
248
  await d.guardado.borrarPendiente(origen, techo());
248
- const donde = guardadaEn === "llavero" ? "el llavero del sistema" : "el archivo de credenciales";
249
+ const donde = guardadaEn === "llavero" ? NOMBRE_DE_LA_FUENTE.llavero : NOMBRE_DE_LA_FUENTE.archivo;
249
250
  return {
250
251
  codigo: SALIDA.ok,
251
252
  json: { ok: true, equipo: nueva.equipo, guardada_en: guardadaEn, anterior_revocada: anteriorRevocada, queda_por_revocar: quedaPorRevocar },
252
- texto: `Sesión iniciada en el equipo ${nueva.equipo}; la clave quedó en ${donde}.`,
253
+ texto: `Logged in to team ${nueva.equipo}; the key is saved in ${donde}.`,
253
254
  };
254
255
  }
@@ -2,6 +2,7 @@
2
2
  // (Review Focus 9 y 14). Borrar sin revocar dejaría la clave sirviendo en el motor y sin nadie que
3
3
  // la recuerde: por eso, si el motor no confirma, no se borra nada y el comando sale 1.
4
4
  import { FalloDeLaTerminal, SALIDA, taparSecretos } from "../contrato.js";
5
+ import { NOMBRE_DE_LA_FUENTE } from "../guardado/index.js";
5
6
  import { reintentarPorRevocar } from "./login.js";
6
7
  const motivo = (e) => e instanceof FalloDeLaTerminal ? e.message : taparSecretos(e instanceof Error ? e.message : String(e));
7
8
  export async function comandoLogout(d, flags) {
@@ -9,7 +10,7 @@ export async function comandoLogout(d, flags) {
9
10
  // Sin nada legible y un almacén que no contestó, no se sabe si hay sesión: decir «no había»
10
11
  // dejaría creer que no queda ninguna clave viva (Review Focus 12).
11
12
  const noPudeLeer = (extra = {}) => {
12
- const message = "No pude leer la clave guardada (el llavero no respondió); no cerré nada. Probá de nuevo.";
13
+ const message = "I couldn't read the saved key (the keychain didn't respond); I didn't log out. Try again.";
13
14
  return { codigo: SALIDA.inesperado, json: { ok: false, error: "no_pude_leer_la_clave", message, ...extra }, texto: message };
14
15
  };
15
16
  if (flags.dryRun) {
@@ -18,20 +19,20 @@ export async function comandoLogout(d, flags) {
18
19
  if (vigente === null && !lectura.completa)
19
20
  return noPudeLeer({ dry_run: true });
20
21
  if (vigente === null) {
21
- return { codigo: SALIDA.ok, json: { ok: true, dry_run: true, habia_sesion: false }, texto: "No hay sesión iniciada: no haría nada." };
22
+ return { codigo: SALIDA.ok, json: { ok: true, dry_run: true, habia_sesion: false }, texto: "You are not logged in: there is nothing to do." };
22
23
  }
23
- const haria = `revocar la clave de ${vigente.fuente} en ${origen}` + (vigente.fuente !== "env" ? " y borrarla" : "");
24
- return { codigo: SALIDA.ok, json: { ok: true, dry_run: true, haria }, texto: `Con --dry-run no hago nada; sin él, iría a ${haria}.` };
24
+ const haria = `revoke the key in ${NOMBRE_DE_LA_FUENTE[vigente.fuente]} at ${origen}` + (vigente.fuente !== "env" ? " and delete it" : "");
25
+ return { codigo: SALIDA.ok, json: { ok: true, dry_run: true, haria }, texto: `With --dry-run I do nothing; without it: ${haria}.` };
25
26
  }
26
27
  // Lo pendiente de antes va primero: un logout es justo cuando la persona espera que no quede
27
28
  // ninguna clave suya viva, y es la última oportunidad que tiene la terminal de revocarlas.
28
29
  const { quedan } = await reintentarPorRevocar(d, d.ahora() + 15_000);
29
30
  // `null` es «no lo pude comprobar» y se dice así: un 0 haría creer que no queda nada vivo.
30
31
  if (quedan === null) {
31
- d.avisar("No pude comprobar cuántas claves quedan por revocar; las que haya siguen anotadas y las reintento en el próximo login o logout.");
32
+ d.avisar("I couldn't check how many keys are left to revoke; any that exist are still noted down and I'll try again on the next login or logout.");
32
33
  }
33
34
  else if (quedan > 0) {
34
- d.avisar(`Quedan ${quedan} clave(s) anterior(es) sin revocar; siguen anotadas y las reintento en el próximo login o logout.`);
35
+ d.avisar(`${quedan} previous key(s) are still not revoked; they are noted down and I'll try again on the next login or logout.`);
35
36
  }
36
37
  const lectura = await d.guardado.leerClaveCompleta(origen);
37
38
  const vigente = lectura.valor;
@@ -40,22 +41,22 @@ export async function comandoLogout(d, flags) {
40
41
  const conPendientes = (json) => ({ ...json, quedan_por_revocar: quedan, ...(lectura.completa ? {} : { lectura_completa: false }) });
41
42
  if (vigente === null) {
42
43
  await d.guardado.borrarPendiente(origen);
43
- return { codigo: SALIDA.ok, json: conPendientes({ ok: true, habia_sesion: false }), texto: "No había sesión iniciada." };
44
+ return { codigo: SALIDA.ok, json: conPendientes({ ok: true, habia_sesion: false }), texto: "You were not logged in." };
44
45
  }
45
46
  let revocada;
46
47
  try {
47
48
  revocada = (await d.revocarPropia(d.api, vigente.clave)) === "revocada";
48
49
  }
49
50
  catch (e) {
50
- const cual = vigente.fuente === "env" ? "la clave de SUPCOFLOW_TOKEN sigue viva" : "la clave sigue guardada";
51
- const message = `No pude revocar la clave, así que no cerré la sesión (${motivo(e)}); ${cual}. Probá de nuevo.`;
51
+ const cual = vigente.fuente === "env" ? "the key in SUPCOFLOW_TOKEN is still active" : "the key is still saved";
52
+ const message = `I couldn't revoke the key, so I didn't log out (${motivo(e)}); ${cual}. Try again.`;
52
53
  const error = e instanceof FalloDeLaTerminal ? e.error : "inesperado";
53
54
  return { codigo: SALIDA.inesperado, json: conPendientes({ ok: false, error, message }), texto: message };
54
55
  }
55
56
  // La variable no es nuestra: se revoca la clave que trae, pero lo guardado es OTRA credencial,
56
57
  // que sólo se cierra revocándola a ella. Borrarla acá la dejaría viva y olvidada.
57
58
  if (vigente.fuente === "env") {
58
- d.avisar("Revoqué la clave de SUPCOFLOW_TOKEN; sacala de tu entorno.");
59
+ d.avisar("I revoked the key in SUPCOFLOW_TOKEN; remove it from your environment.");
59
60
  // `undefined` = no se pudo leer: no se afirma que no haya, el json lleva `null` en vez de mentir.
60
61
  let guardada;
61
62
  try {
@@ -66,19 +67,19 @@ export async function comandoLogout(d, flags) {
66
67
  guardada = undefined;
67
68
  }
68
69
  if (guardada) {
69
- d.avisar(`Sigue guardada otra clave en ${guardada.fuente}; para cerrarla, corré \`supcoflow logout\` sin SUPCOFLOW_TOKEN.`);
70
+ d.avisar(`Another key is still saved in ${NOMBRE_DE_LA_FUENTE[guardada.fuente]}; to log it out, run \`supcoflow logout\` without SUPCOFLOW_TOKEN.`);
70
71
  }
71
72
  return {
72
73
  codigo: SALIDA.ok,
73
74
  json: conPendientes({ ok: true, revocada, fuente: "env", queda_clave_guardada: guardada === undefined ? null : guardada !== null }),
74
- texto: "Revoqué la clave de SUPCOFLOW_TOKEN.",
75
+ texto: "I revoked the key in SUPCOFLOW_TOKEN.",
75
76
  };
76
77
  }
77
78
  // Con la lectura incompleta se borra sólo la copia que se leyó y se acaba de revocar: la del
78
79
  // almacén que no contestó puede ser otra clave, viva, y borrarla sin revocarla la olvidaría.
79
80
  if (!lectura.completa) {
80
- const otro = vigente.fuente === "archivo" ? "el llavero del sistema" : "el archivo de credenciales";
81
- d.avisar(`Cierro la clave de ${vigente.fuente}, pero no pude leer ${otro}: puede quedar otra copia guardada ahí; cuando responda, volvé a correr \`supcoflow logout\`.`);
81
+ const otro = vigente.fuente === "archivo" ? NOMBRE_DE_LA_FUENTE.llavero : NOMBRE_DE_LA_FUENTE.archivo;
82
+ d.avisar(`I'm logging out the key in ${NOMBRE_DE_LA_FUENTE[vigente.fuente]}, but I couldn't read ${otro}: another copy may still be saved there; when it responds, run \`supcoflow logout\` again.`);
82
83
  }
83
84
  try {
84
85
  if (lectura.completa)
@@ -88,9 +89,9 @@ export async function comandoLogout(d, flags) {
88
89
  }
89
90
  catch (e) {
90
91
  // La clave ya no sirve en el motor, pero quedó escrita: el comando no puede decir que terminó bien.
91
- const message = `La clave quedó revocada pero no pude borrarla: ${motivo(e)}`;
92
+ const message = `The key was revoked but I couldn't delete it: ${motivo(e)}`;
92
93
  return { codigo: SALIDA.inesperado, json: conPendientes({ ok: false, error: "no_se_pudo_borrar", message }), texto: message };
93
94
  }
94
95
  await d.guardado.borrarPendiente(origen);
95
- return { codigo: SALIDA.ok, json: conPendientes({ ok: true, revocada }), texto: "Sesión cerrada." };
96
+ return { codigo: SALIDA.ok, json: conPendientes({ ok: true, revocada }), texto: "Logged out." };
96
97
  }