@profullstack/nichedb 0.49.0 → 0.51.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +86 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/nichedb",
3
- "version": "0.49.0",
3
+ "version": "0.51.0",
4
4
  "description": "CLI and MCP bridge for NicheDB: browse collections, manage sources and feeds, search items, from any deployment",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -15,7 +15,7 @@ import { homedir, tmpdir } from 'node:os';
15
15
  import { join } from 'node:path';
16
16
  import { createInterface } from 'node:readline';
17
17
 
18
- export const VERSION = '0.49.0';
18
+ export const VERSION = '0.51.0';
19
19
  const GEO_FLAGS = ['lat', 'long', 'radius', 'unit', 'bbox', 'sort', 'offset'];
20
20
  const DEFAULT_API = process.env.NICHEDB_API ?? 'https://nichedb.dev';
21
21
  const CONFIG_DIR = join(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'nichedb');
@@ -321,6 +321,26 @@ export const COMMANDS = [
321
321
  summary: 'Take a handle: your URL becomes /c/profiles/<handle> (key required).',
322
322
  options: [],
323
323
  },
324
+ {
325
+ name: 'redeem',
326
+ usage: 'redeem <code>',
327
+ summary: 'Redeem a credit code for days of Premium (key required).',
328
+ options: ['--json'],
329
+ },
330
+ {
331
+ name: 'invites',
332
+ usage: 'invites [create [--note …] | revoke <id>]',
333
+ summary:
334
+ 'Your invites: how many are left, and each one. create makes one (its link is shown once); revoke withdraws an unused one (key required).',
335
+ options: ['--note <who it is for>', '--json'],
336
+ },
337
+ {
338
+ name: 'credits mint',
339
+ usage: 'credits mint --days <n> [--label …] [--max <n>] [--expires <days>]',
340
+ summary:
341
+ 'Admin: mint a code worth n days of Premium. The code is shown once. Also credits list|revoke <id>.',
342
+ options: ['--days <n>', '--label <text>', '--max <redemptions>', '--expires <days>', '--json'],
343
+ },
324
344
  {
325
345
  name: 'login',
326
346
  usage: 'login [--api <url>] [--key <ndb_…>]',
@@ -1281,6 +1301,71 @@ export async function run(
1281
1301
  out(await client.text(`/api/v1/profiles/${encodeURIComponent(sub)}/openprofile.md`));
1282
1302
  return 0;
1283
1303
  }
1304
+ case 'redeem': {
1305
+ const code = rest.join(' ').trim();
1306
+ if (!code) throw new Error('redeem <code>');
1307
+ const r = await client.post('/api/v1/credits/redeem', { code });
1308
+ out(json ? JSON.stringify(r, null, 2) : `Redeemed: ${r.days} days of Premium added.`);
1309
+ return 0;
1310
+ }
1311
+ case 'invites': {
1312
+ const [sub, arg] = rest;
1313
+ if (sub === 'create') {
1314
+ const r = await client.post('/api/v1/invites', { note: flags.note ?? '' });
1315
+ out(json ? JSON.stringify(r.invite, null, 2) : `${r.invite.url}\n(shown once)`);
1316
+ return 0;
1317
+ }
1318
+ if (sub === 'revoke') {
1319
+ if (!arg) throw new Error('invites revoke <id>');
1320
+ await client.post(`/api/v1/invites/${encodeURIComponent(arg)}/revoke`, {});
1321
+ out(`Withdrew ${arg}.`);
1322
+ return 0;
1323
+ }
1324
+ const r = await client.get('/api/v1/invites');
1325
+ if (json) {
1326
+ out(JSON.stringify(r, null, 2));
1327
+ return 0;
1328
+ }
1329
+ out(
1330
+ `${r.quota.left} of ${r.quota.total} invites left · ${r.bonus_days} days of Premium each when one is used`,
1331
+ );
1332
+ for (const i of r.invites) out(`${pad(i.id, 18)} ${pad(i.status, 8)} ${i.note}`);
1333
+ return 0;
1334
+ }
1335
+ case 'credits': {
1336
+ const [sub, arg] = rest;
1337
+ if (sub === 'mint') {
1338
+ const r = await client.post('/api/v1/admin/credits', {
1339
+ days: Number(flags.days),
1340
+ label: flags.label ?? '',
1341
+ max: flags.max ? Number(flags.max) : 1,
1342
+ expires_days: flags.expires ? Number(flags.expires) : null,
1343
+ });
1344
+ out(
1345
+ json
1346
+ ? JSON.stringify(r, null, 2)
1347
+ : `${r.code}\n${r.days} days of Premium · ${r.max_redemptions} use(s) · id ${r.id}`,
1348
+ );
1349
+ return 0;
1350
+ }
1351
+ if (sub === 'revoke') {
1352
+ if (!arg) throw new Error('credits revoke <id>');
1353
+ await client.post(`/api/v1/admin/credits/${encodeURIComponent(arg)}/revoke`, {});
1354
+ out(`Revoked ${arg}.`);
1355
+ return 0;
1356
+ }
1357
+ if (sub && sub !== 'list') throw new Error('credits mint|list|revoke <id>');
1358
+ const { codes } = await client.get('/api/v1/admin/credits');
1359
+ if (json) {
1360
+ out(JSON.stringify(codes, null, 2));
1361
+ return 0;
1362
+ }
1363
+ for (const c of codes)
1364
+ out(
1365
+ `${pad(c.id, 18)} ${pad(`${c.days}d`, 6)} ${pad(`${c.redemptions}/${c.max_redemptions}`, 8)} ${c.revoked_at ? 'revoked ' : ''}${c.label}`,
1366
+ );
1367
+ return 0;
1368
+ }
1284
1369
  case 'mcp':
1285
1370
  return serveMcp({ client, stdin, stdout });
1286
1371
  default: