latinfo 0.20.1 → 0.20.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/index.js +84 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ const odis_search_1 = require("./odis-search");
|
|
|
49
49
|
const mphf_search_1 = require("./mphf-search");
|
|
50
50
|
const VERSION = '0.18.1';
|
|
51
51
|
const API_URL = process.env.LATINFO_API_URL || 'https://api.latinfo.dev';
|
|
52
|
-
const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || '
|
|
52
|
+
const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || 'Ov23liZAqpaGnYQ6Kp5I';
|
|
53
53
|
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.latinfo');
|
|
54
54
|
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
55
55
|
// --- JSON mode ---
|
|
@@ -157,7 +157,35 @@ async function apiRequest(config, path) {
|
|
|
157
157
|
return res;
|
|
158
158
|
}
|
|
159
159
|
// --- Commands ---
|
|
160
|
-
async function login(token) {
|
|
160
|
+
async function login(token, username) {
|
|
161
|
+
// Zero-friction login: admin generates key for a username (no browser)
|
|
162
|
+
if (username) {
|
|
163
|
+
let adminSecret;
|
|
164
|
+
try {
|
|
165
|
+
adminSecret = requireAdmin();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
console.error('Admin access required for username login. Need ADMIN_SECRET in .dev.vars or env.');
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
const res = await fetch(`${API_URL}/team/members`, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: { Authorization: `Bearer ${adminSecret}`, 'Content-Type': 'application/json' },
|
|
174
|
+
body: JSON.stringify({ github_username: username }),
|
|
175
|
+
});
|
|
176
|
+
const data = await res.json();
|
|
177
|
+
if (!res.ok) {
|
|
178
|
+
console.error(data.message || data.error);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
if (!data.api_key) {
|
|
182
|
+
console.error('Failed to generate API key');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
saveConfig({ api_key: data.api_key, github_username: username, is_team: true, team_role: data.role });
|
|
186
|
+
console.log(`Logged in as ${username} (${data.role}).`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
161
189
|
// PAT login: no browser needed
|
|
162
190
|
if (token) {
|
|
163
191
|
const authRes = await fetch(`${API_URL}/auth/github`, {
|
|
@@ -188,21 +216,52 @@ async function login(token) {
|
|
|
188
216
|
console.log(`Logged in as ${authData.github_username}${config.is_team ? ` (team: ${config.team_role})` : ''}`);
|
|
189
217
|
return;
|
|
190
218
|
}
|
|
191
|
-
//
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
console.log(
|
|
199
|
-
console.log(
|
|
200
|
-
|
|
201
|
-
|
|
219
|
+
// Device Flow: no browser redirect needed, works in SSH/containers/agents
|
|
220
|
+
const deviceRes = await fetch('https://github.com/login/device/code', {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
223
|
+
body: JSON.stringify({ client_id: GITHUB_CLIENT_ID, scope: 'read:user,user:email' }),
|
|
224
|
+
});
|
|
225
|
+
const device = await deviceRes.json();
|
|
226
|
+
console.log(`\n Go to: ${device.verification_uri}`);
|
|
227
|
+
console.log(` Enter code: ${device.user_code}\n`);
|
|
228
|
+
// Try to open browser automatically
|
|
229
|
+
try {
|
|
230
|
+
openBrowser(device.verification_uri);
|
|
231
|
+
}
|
|
232
|
+
catch { }
|
|
233
|
+
// Poll until authorized
|
|
234
|
+
let ghAccessToken = null;
|
|
235
|
+
const deadline = Date.now() + device.expires_in * 1000;
|
|
236
|
+
while (Date.now() < deadline) {
|
|
237
|
+
await new Promise(r => setTimeout(r, (device.interval || 5) * 1000));
|
|
238
|
+
const pollRes = await fetch('https://github.com/login/oauth/access_token', {
|
|
239
|
+
method: 'POST',
|
|
240
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
241
|
+
body: JSON.stringify({ client_id: GITHUB_CLIENT_ID, device_code: device.device_code, grant_type: 'urn:ietf:params:oauth:grant-type:device_code' }),
|
|
242
|
+
});
|
|
243
|
+
const poll = await pollRes.json();
|
|
244
|
+
if (poll.access_token) {
|
|
245
|
+
ghAccessToken = poll.access_token;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
if (poll.error === 'expired_token') {
|
|
249
|
+
console.error('Code expired. Run latinfo login again.');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
if (poll.error && poll.error !== 'authorization_pending' && poll.error !== 'slow_down') {
|
|
253
|
+
console.error(`GitHub error: ${poll.error}`);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!ghAccessToken) {
|
|
258
|
+
console.error('Login timed out.');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
202
261
|
const authRes = await fetch(`${API_URL}/auth/github`, {
|
|
203
262
|
method: 'POST',
|
|
204
263
|
headers: { 'Content-Type': 'application/json' },
|
|
205
|
-
body: JSON.stringify({
|
|
264
|
+
body: JSON.stringify({ access_token: ghAccessToken }),
|
|
206
265
|
});
|
|
207
266
|
if (!authRes.ok) {
|
|
208
267
|
console.error('Error getting API key:', await authRes.text());
|
|
@@ -4000,9 +4059,16 @@ async function teamCmd(args) {
|
|
|
4000
4059
|
}
|
|
4001
4060
|
console.log(`Added ${username} to team (${data.role}).`);
|
|
4002
4061
|
if (data.api_key) {
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4062
|
+
if (args.includes('--login')) {
|
|
4063
|
+
// Save key locally — login as this user
|
|
4064
|
+
saveConfig({ api_key: data.api_key, github_username: username, is_team: true, team_role: data.role });
|
|
4065
|
+
console.log(`Logged in as ${username} (${data.role}).`);
|
|
4066
|
+
}
|
|
4067
|
+
else {
|
|
4068
|
+
console.log(`\n API key: ${data.api_key}\n`);
|
|
4069
|
+
console.log(` Share this with the member. They set:`);
|
|
4070
|
+
console.log(` export LATINFO_API_KEY=${data.api_key}`);
|
|
4071
|
+
}
|
|
4006
4072
|
}
|
|
4007
4073
|
try {
|
|
4008
4074
|
const { execSync: exec } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
@@ -4260,7 +4326,7 @@ else {
|
|
|
4260
4326
|
// Admin commands (flat)
|
|
4261
4327
|
switch (command) {
|
|
4262
4328
|
case 'login':
|
|
4263
|
-
login(tokenFlag).catch(e => { console.error(e); process.exit(1); });
|
|
4329
|
+
login(tokenFlag, args[0]).catch(e => { console.error(e); process.exit(1); });
|
|
4264
4330
|
break;
|
|
4265
4331
|
case 'logout':
|
|
4266
4332
|
logout();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "latinfo",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"description": "Tax registry & procurement API for Latin America. Query RUC, DNI, NIT, licitaciones from Peru & Colombia. Offline MPHF search, full OCDS data, updated daily.",
|
|
5
5
|
"homepage": "https://latinfo.dev",
|
|
6
6
|
"repository": {
|