@tokenbuddy/tb-admin 1.0.31 → 1.0.32

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 (52) hide show
  1. package/dist/src/cli.d.ts.map +1 -1
  2. package/dist/src/cli.js +280 -19
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/client.d.ts +82 -2
  5. package/dist/src/client.d.ts.map +1 -1
  6. package/dist/src/client.js +93 -0
  7. package/dist/src/client.js.map +1 -1
  8. package/dist/src/provider.d.ts +120 -0
  9. package/dist/src/provider.d.ts.map +1 -0
  10. package/dist/src/provider.js +73 -0
  11. package/dist/src/provider.js.map +1 -0
  12. package/dist/src/seller.d.ts +104 -0
  13. package/dist/src/seller.d.ts.map +1 -0
  14. package/dist/src/seller.js +283 -0
  15. package/dist/src/seller.js.map +1 -0
  16. package/dist/src/ui-actions.d.ts +25 -0
  17. package/dist/src/ui-actions.d.ts.map +1 -1
  18. package/dist/src/ui-actions.js +81 -11
  19. package/dist/src/ui-actions.js.map +1 -1
  20. package/dist/src/ui-server.js +9 -0
  21. package/dist/src/ui-server.js.map +1 -1
  22. package/dist/src/ui-state.d.ts +77 -2
  23. package/dist/src/ui-state.d.ts.map +1 -1
  24. package/dist/src/ui-state.js +242 -14
  25. package/dist/src/ui-state.js.map +1 -1
  26. package/dist/src/ui-static.d.ts.map +1 -1
  27. package/dist/src/ui-static.js +95 -17
  28. package/dist/src/ui-static.js.map +1 -1
  29. package/dist/src/vendor-client.d.ts +23 -0
  30. package/dist/src/vendor-client.d.ts.map +1 -0
  31. package/dist/src/vendor-client.js +2 -0
  32. package/dist/src/vendor-client.js.map +1 -0
  33. package/dist/src/vendor-commands.d.ts +35 -0
  34. package/dist/src/vendor-commands.d.ts.map +1 -0
  35. package/dist/src/vendor-commands.js +33 -0
  36. package/dist/src/vendor-commands.js.map +1 -0
  37. package/package.json +1 -1
  38. package/src/cli.ts +305 -31
  39. package/src/client.ts +119 -2
  40. package/src/provider.ts +150 -0
  41. package/src/seller.ts +362 -0
  42. package/src/ui-actions.ts +89 -11
  43. package/src/ui-server.ts +9 -0
  44. package/src/ui-state.ts +293 -15
  45. package/src/ui-static.ts +95 -17
  46. package/src/vendor-client.ts +23 -0
  47. package/src/vendor-commands.ts +65 -0
  48. package/tests/admin.test.ts +20 -1
  49. package/tests/seller.test.ts +307 -0
  50. package/tests/ui-state-fleet.test.ts +257 -0
  51. package/tests/ui-static-row.test.ts +202 -0
  52. package/tests/vendor-cli.test.ts +197 -0
@@ -77,6 +77,25 @@ export function adminUiHtml() {
77
77
  .app-table-head{min-height:34px;color:var(--muted);font-size:var(--label-fs);font-weight:var(--label-weight);text-transform:uppercase;letter-spacing:var(--label-spacing)}
78
78
  .app-row{border:1px solid var(--hairline);border-radius:8px;background:#fff;min-height:76px;text-align:left}
79
79
  .app-row:hover{border-color:var(--hairline-strong);background:#fff}
80
+ /* Step 13 v1.1: 双源 (fly + registry) 4 类行视觉. dataSource 决定
81
+ dataSource="fly" → 灰/中性边, "未发布" 提示
82
+ dataSource="registry" → 整行红边 + 软红底, "立即下线 (registry-only)" 按钮
83
+ dataSource="both" → 正常边, 跟 1.0.31 老样式一致
84
+ */
85
+ .app-row.app-row-fly-only{border-color:#cdd2db;background:#f8f9fc}
86
+ .app-row.app-row-fly-only:hover{background:#f1f3f8}
87
+ .app-row.app-row-registry-only{border:2px solid var(--danger);background:var(--danger-soft);box-shadow:0 0 0 3px rgba(239,91,120,.08)}
88
+ .app-row.app-row-registry-only:hover{background:#ffe3ea}
89
+ .datasource-chip{display:inline-block;padding:1px 8px;border-radius:999px;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;line-height:16px;margin-left:6px;vertical-align:middle}
90
+ .datasource-chip.both{background:#e7f6ee;color:#0a8754}
91
+ .datasource-chip.fly{background:#e3e6ee;color:#4a5170}
92
+ .datasource-chip.registry{background:var(--danger-soft);color:var(--danger);border:1px solid var(--danger)}
93
+ .alert-reason{color:var(--danger);font-size:11px;line-height:1.4;font-weight:700;margin-top:4px;display:block}
94
+ .remove-hint-btn{margin-top:6px;background:var(--danger);color:#fff;border:0;border-radius:6px;padding:4px 10px;font-size:11px;font-weight:800;cursor:pointer;display:inline-block}
95
+ .remove-hint-btn:hover{background:#d63d5a}
96
+ .remove-hint-btn::before{content:"⚠ ";margin-right:2px}
97
+ .publish-hint-btn{margin-top:6px;background:#fff;color:var(--primary);border:1px solid var(--hairline-strong);border-radius:6px;padding:4px 10px;font-size:11px;font-weight:800;cursor:pointer;display:inline-block}
98
+ .publish-hint-btn:hover{background:#f5f3ff}
80
99
  /* Status dot — five spec tones (green/amber/red/blue/gray) */
81
100
  .app-dot{width:10px;height:10px;border-radius:999px;background:#c8ced8;box-shadow:0 0 0 4px #edf1f8}
82
101
  .app-dot.tone-green{background:var(--success);box-shadow:0 0 0 4px rgba(16,185,129,.18)}
@@ -193,7 +212,7 @@ export function adminUiHtml() {
193
212
  </style>
194
213
  </head>
195
214
  <body>
196
- <nav class="topnav"><div class="logo">TOKENBUDDY ADMIN</div><div class="top-links"><button class="top-link active" data-page="sellers">Sellers</button><button class="top-link" data-page="bootstrap">Bootstrap</button></div></nav>
215
+ <nav class="topnav"><div class="logo">TOKENBUDDY ADMIN</div><div class="top-links"><button class="top-link active" data-page="sellers">Sellers</button><button class="top-link" data-page="releases">Release Requests</button></div></nav>
197
216
  <main class="content">
198
217
  <section id="page-sellers" class="page active">
199
218
  <div class="panel">
@@ -221,13 +240,17 @@ export function adminUiHtml() {
221
240
  <section id="page-bootstrap" class="page">
222
241
  <div class="bootstrap-card">
223
242
  <div class="panel-head">
224
- <h1 class="title">Bootstrap</h1>
243
+ <h1 class="title">Release Requests</h1>
225
244
  <div class="modal-actions">
226
- <button id="openBootstrapConfig" class="btn primary">Edit Bootstrap Config</button>
227
- <button id="refreshBootstrap" class="btn">Refresh</button>
245
+ <button id="refreshReleases" class="btn">Refresh</button>
228
246
  </div>
229
247
  </div>
230
- <div id="bootstrapGrid" class="bootstrap-grid"></div>
248
+ <p class="hint" style="color:var(--muted);font-size:12px;margin:0 0 12px;">
249
+ Pending and historical release requests you have submitted to the wallet-bootstrap
250
+ registry. Force-publish is available to the platform super-admin via the registry
251
+ admin web; vendors do not publish their own releases.
252
+ </p>
253
+ <div id="releasesGrid" class="bootstrap-grid"></div>
231
254
  </div>
232
255
  </section>
233
256
  </main>
@@ -338,6 +361,17 @@ function scheduleSellerRefresh(){ clearTimeout(sellerRefreshTimer); sellerNextRe
338
361
  function updateSellerRefreshMeta(refreshing){ const state = document.getElementById("sellerRefreshState"); if (!state) return; const nextSeconds = sellerNextRefreshAt ? Math.max(0, Math.ceil((sellerNextRefreshAt.getTime() - Date.now()) / 1000)) : 0; state.classList.toggle("refreshing", Boolean(refreshing)); state.classList.toggle("error", Boolean(sellerRefreshError)); state.innerHTML = refreshing ? '<span class="spinner" aria-hidden="true"></span><span>Refreshing</span>' : esc(sellerRefreshError || (sellerRefreshLoaded ? "Next refresh: " + nextSeconds + "s" : "Starting")); }
339
362
  function sellerRow(row){
340
363
  const fmt = window.__tbFmt;
364
+ // Step 13 v1.1: dataSource 决定行 class + 标红/灰 + 按钮 + chip.
365
+ // row.dataSource ∈ "fly" | "registry" | "both". 老 1.0.31 没这个字段,
366
+ // 兜底当 "both" (老 UI 看到的所有行, 默认都按已发布处理).
367
+ const ds = row.dataSource || "both";
368
+ const rowClass = ds === "registry" ? "app-row app-row-registry-only"
369
+ : ds === "fly" ? "app-row app-row-fly-only"
370
+ : "app-row";
371
+ const dsChipLabel = ds === "registry" ? "Registry-only"
372
+ : ds === "fly" ? "未发布"
373
+ : "Both";
374
+ const dsChip = '<span class="datasource-chip '+esc(ds)+'" title="'+esc('Data source: ' + ds + '. 详见 docs/processes/seller-fleet-data-sources.md')+'">'+esc(dsChipLabel)+'</span>';
341
375
  const tip = [row.description, row.region, row.app, row.specs?.memoryGb ? row.specs.memoryGb + "GB" : "", row.specs?.machines ? row.specs.machines + " machines" : "", row.modelsCount ? row.modelsCount + " models" : ""].filter(Boolean).join(" · ") || "No specs";
342
376
  const ttftText = fmt.formatDuration(row.ttftMs);
343
377
  const ttft = "TTFT: " + (ttftText === fmt.UNKNOWN_VALUE ? "—" : ttftText);
@@ -348,9 +382,11 @@ function sellerRow(row){
348
382
  const balanceRaw = row.upstreamBalanceUsdMicros;
349
383
  const balanceText = (balanceRaw === undefined || balanceRaw === null) ? dash() : '<strong>'+esc(fmt.formatBalanceAmount(balanceRaw, row.upstreamBalanceCurrency || "USD"))+'</strong>';
350
384
  const switchText = row.lastSwitchAt ? "Switch " + esc(fmt.formatTimeCompact(row.lastSwitchAt)) : "";
385
+ // Step 13 v1.1: 绿点 (status + tone) 仍按 nodeStatus 决定 (probeManifest
386
+ // 200 → active 绿点; 失败 → unknown 灰). registryStatus 单独 tooltip.
351
387
  const status = fmt.formatSellerStatus(row.nodeStatus);
352
388
  const tone = fmt.sellerStatusTone(row.nodeStatus);
353
- const statusTip = "registry: " + esc(fmt.formatSellerStatus(row.registryStatus)) + " · upstream: " + esc(fmt.normalizeStatusLabel(row.upstreamStatus));
389
+ const statusTip = "registry: " + esc(fmt.formatSellerStatus(row.registryStatus)) + " · upstream: " + esc(fmt.normalizeStatusLabel(row.upstreamStatus)) + " · source: " + esc(ds);
354
390
  const sellerLine = [
355
391
  disc !== fmt.UNKNOWN_VALUE ? "Disc " + esc(disc) : null,
356
392
  capacity !== fmt.UNKNOWN_VALUE ? capacity : null,
@@ -358,19 +394,55 @@ function sellerRow(row){
358
394
  balanceText.includes("<strong>") ? "Balance " + esc(balanceText.replace(/<[^>]+>/g, "")) : null,
359
395
  switchText || null
360
396
  ].filter(Boolean).join(" · ");
361
- return '<button class="app-row" type="button" data-detail="'+esc(row.id)+'"><span class="app-dot tone-'+esc(tone)+'" aria-label="'+esc(status)+'"></span><span class="app-name"><span class="seller-title"><strong>'+esc(row.name)+'</strong><span class="spec-tip" title="'+esc(tip)+'" aria-label="Seller specs">'+infoIcon+'</span></span><span class="muted-value" style="font-size:12px;font-family:var(--font-mono)">'+esc(sellerLine || row.app || row.id)+'</span></span><span class="field-cell"><strong>'+esc(row.upstreamDomain)+'</strong></span><span class="field-cell"><strong>'+esc(disc === fmt.UNKNOWN_VALUE ? "—" : disc)+'</strong></span><span class="field-cell"><strong>'+esc(capacity)+'</strong></span><span class="speed-cell"><strong>'+esc(ttftText === fmt.UNKNOWN_VALUE ? "—" : ttftText)+'</strong><span>'+avgSpeed+'</span></span><span class="field-cell"><span class="balance-line">'+balanceText+(row.upstreamRechargeUrl ? '<a class="recharge-btn" href="'+esc(row.upstreamRechargeUrl)+'" target="_blank" rel="noreferrer">↗</a>' : '')+'</span></span><span class="field-cell"><strong title="'+esc(statusTip)+'">'+esc(status)+'</strong></span><span class="row-actions"><span class="detail-btn">›</span></span></button>';
397
+ // Step 13 v1.1: 4 类行的 status cell 文案不同.
398
+ // both → 正常 active / draining / offline
399
+ // fly-only → "未发布" (publishHint 提示走 vendor-bootstrap stage)
400
+ // registry → "**严重事故**" + alertReason 红字
401
+ let statusCell;
402
+ if (ds === "registry") {
403
+ statusCell = '<span class="field-cell"><strong class="registry-incident" title="'+esc(statusTip)+'">严重事故</strong>' +
404
+ (row.alertReason ? '<span class="alert-reason">'+esc(row.alertReason)+'</span>' : '') +
405
+ (row.removeHint ? '<button class="remove-hint-btn" type="button" data-action="remove" data-seller-id="'+esc(row.id)+'" title="'+esc(row.removeHint)+'">'+esc(row.removeHint)+'</button>' : '') +
406
+ '</span>';
407
+ } else if (ds === "fly") {
408
+ statusCell = '<span class="field-cell"><strong title="'+esc(statusTip)+'">未发布</strong>' +
409
+ (row.publishHint ? '<button class="publish-hint-btn" type="button" data-action="publish" data-seller-id="'+esc(row.id)+'" title="'+esc(row.publishHint)+'">'+esc(row.publishHint)+'</button>' : '') +
410
+ '</span>';
411
+ } else {
412
+ statusCell = '<span class="field-cell"><strong title="'+esc(statusTip)+'">'+esc(status)+'</strong></span>';
413
+ }
414
+ return '<button class="'+esc(rowClass)+'" type="button" data-detail="'+esc(row.id)+'"><span class="app-dot tone-'+esc(tone)+'" aria-label="'+esc(status)+'"></span><span class="app-name"><span class="seller-title"><strong>'+esc(row.name)+'</strong>'+dsChip+'<span class="spec-tip" title="'+esc(tip)+'" aria-label="Seller specs">'+infoIcon+'</span></span><span class="muted-value" style="font-size:12px;font-family:var(--font-mono)">'+esc(sellerLine || row.app || row.id)+'</span></span><span class="field-cell"><strong>'+esc(row.upstreamDomain)+'</strong></span><span class="field-cell"><strong>'+esc(disc === fmt.UNKNOWN_VALUE ? "—" : disc)+'</strong></span><span class="field-cell"><strong>'+esc(capacity)+'</strong></span><span class="speed-cell"><strong>'+esc(ttftText === fmt.UNKNOWN_VALUE ? "—" : ttftText)+'</strong><span>'+avgSpeed+'</span></span><span class="field-cell"><span class="balance-line">'+balanceText+(row.upstreamRechargeUrl ? '<a class="recharge-btn" href="'+esc(row.upstreamRechargeUrl)+'" target="_blank" rel="noreferrer">↗</a>' : '')+'</span></span>'+statusCell+'<span class="row-actions"><span class="detail-btn">›</span></span></button>';
362
415
  }
363
416
  async function loadBootstrap(){
417
+ // Step 6 of the registry redesign: the legacy Bootstrap tab now
418
+ // surfaces vendor release requests. We keep the function name
419
+ // (loadBootstrap) so the existing click wiring continues to work,
420
+ // but the rendered content comes from the new
421
+ // /api/vendor/release-requests endpoint (added in ui-server.ts).
364
422
  const fmt = window.__tbFmt;
365
423
  try {
366
- const data = await api("/api/bootstrap");
367
- const items = [
368
- { tone: "router", label: "Status", value: data.status || "unknown", secondary: data.url ? esc(fmt.formatSellerId(data.url)) : null },
369
- { tone: "spend", label: "Registry", value: data.registryVersion === undefined ? "" : "#" + esc(String(data.registryVersion)), secondary: data.registryUpdatedAt ? "Updated " + esc(fmt.formatTimeCompact(data.registryUpdatedAt)) : null },
370
- { tone: "tokens", label: "Sellers", value: data.sellerEntries === undefined ? "—" : esc(String(data.sellerEntries)), secondary: data.regions && data.regions.length ? esc(data.regions.join(", ")) : null },
371
- { tone: "inventory", label: "Default", value: data.defaultSeller || "—", secondary: data.profile ? "Profile " + esc(data.profile) : null }
372
- ];
373
- document.getElementById("bootstrapGrid").innerHTML = items.map(item => entryCardHtml(item)).join("");
424
+ const data = await api("/api/vendor/release-requests");
425
+ const rows = (data.releaseRequests || []);
426
+ if (rows.length === 0) {
427
+ document.getElementById("bootstrapGrid").innerHTML = '<div class="status-line">No release requests yet. Submit one with <code>tb-admin vendor-bootstrap stage</code> + <code>release submit</code>.</div>';
428
+ return;
429
+ }
430
+ const table = '<table style="width:100%;border-collapse:collapse;font-size:13px;">' +
431
+ '<thead><tr><th style="text-align:left;padding:6px 8px;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:0.04em;">ID</th><th style="text-align:left;padding:6px 8px;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:0.04em;">Status</th><th style="text-align:left;padding:6px 8px;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:0.04em;">Sellers</th><th style="text-align:left;padding:6px 8px;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:0.04em;">Submitted</th><th style="text-align:left;padding:6px 8px;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:0.04em;">Version</th><th style="text-align:left;padding:6px 8px;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:0.04em;">Error</th></tr></thead>' +
432
+ '<tbody>' + rows.map((r) => {
433
+ const summary = r.payloadSummary || { count: 0, sellerIds: [] };
434
+ const sellerList = (summary.sellerIds || []).join(", ") || "—";
435
+ const version = r.publishedVersion !== null && r.publishedVersion !== undefined ? "v" + esc(String(r.publishedVersion)) : "—";
436
+ return '<tr>' +
437
+ '<td style="padding:6px 8px;font-family:ui-monospace,monospace;">#' + esc(String(r.id)) + '</td>' +
438
+ '<td style="padding:6px 8px;">' + esc(r.status) + '</td>' +
439
+ '<td style="padding:6px 8px;">' + esc(String(summary.count)) + ' <span style="color:var(--muted);font-size:11px;">(' + esc(sellerList) + ')</span></td>' +
440
+ '<td style="padding:6px 8px;font-family:ui-monospace,monospace;">' + esc(fmt.formatTimeCompact(r.submittedAt)) + '</td>' +
441
+ '<td style="padding:6px 8px;">' + version + '</td>' +
442
+ '<td style="padding:6px 8px;color:var(--danger);">' + (r.errorMessage ? esc(r.errorMessage) : "—") + '</td>' +
443
+ '</tr>';
444
+ }).join("") + '</tbody></table>';
445
+ document.getElementById("bootstrapGrid").innerHTML = table;
374
446
  } catch (err) {
375
447
  document.getElementById("bootstrapGrid").innerHTML = '<div class="status-line">'+esc(uiErrorMessage(err))+'</div>';
376
448
  }
@@ -458,8 +530,14 @@ function renderCreateJob(job){ if (!job) return; currentCreateJob = job; const d
458
530
  function progressStep(event){ const result = event.result || {}; const log = [result.command ? "$ " + result.command.join(" ") : "", result.stdout || "", result.stderr || ""].filter(Boolean).join("\\n").slice(0, 1600); const expanded = expandedProgressSteps.has(event.stepId); const spinner = event.status === "running" ? '<span class="spinner" aria-hidden="true"></span>' : ""; return '<button type="button" class="progress-step '+esc(event.status)+'" data-progress-step="'+esc(event.stepId)+'" aria-expanded="'+String(expanded)+'"><div class="progress-title">'+spinner+'<strong>'+esc(event.title)+'</strong></div><div class="progress-meta"><span>'+esc(event.message || event.status)+'</span>'+(log ? '<span class="progress-toggle">'+(expanded ? "Hide details" : "Show details")+'</span>' : '')+'</div>'+(log && expanded ? '<pre class="progress-log">'+esc(log)+'</pre>' : '')+'</button>'; }
459
531
  function setCreateFormDisabled(disabled){ document.querySelectorAll("#createFields [data-field], #createFields [data-payment-tab], #createFields [data-payment-toggle]").forEach(input => { input.disabled = Boolean(disabled); }); if (!disabled) updatePaymentPanels(); }
460
532
  document.getElementById("createProgress").onclick = event => { const step = event.target.closest("[data-progress-step]"); if (!step) return; const id = step.dataset.progressStep; if (expandedProgressSteps.has(id)) expandedProgressSteps.delete(id); else expandedProgressSteps.add(id); if (currentCreateJob) renderCreateJob(currentCreateJob); };
461
- document.getElementById("refreshBootstrap").onclick = loadBootstrap;
462
- document.getElementById("openBootstrapConfig").onclick = openBootstrapConfig;
533
+ const refreshBootstrapEl = document.getElementById("refreshBootstrap");
534
+ if (refreshBootstrapEl) {
535
+ refreshBootstrapEl.onclick = loadBootstrap;
536
+ }
537
+ const refreshReleasesEl = document.getElementById("refreshReleases");
538
+ if (refreshReleasesEl) {
539
+ refreshReleasesEl.onclick = loadBootstrap;
540
+ }
463
541
  document.getElementById("closeBootstrapConfig").onclick = () => document.getElementById("bootstrapModal").classList.remove("open");
464
542
  function fieldValue(input){ return numeric(input.value); }
465
543
  function numeric(value){ const n = Number(value); return value !== "" && Number.isFinite(n) ? n : value; }
@@ -1 +1 @@
1
- {"version":3,"file":"ui-static.js","sourceRoot":"","sources":["../../src/ui-static.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,0DAA0D;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YA8RG,aAAa,EAAE;;QAEnB,CAAC;AACT,CAAC;AAED,SAAS,aAAa;IACpB,gFAAgF;IAChF,OAAO;EACP,mBAAmB,EAAE;EACrB,mBAAmB,EAAE;CACtB,CAAC;AACF,CAAC;AAED,SAAS,mBAAmB;IAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiKR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"ui-static.js","sourceRoot":"","sources":["../../src/ui-static.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,0DAA0D;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAqTG,aAAa,EAAE;;QAEnB,CAAC;AACT,CAAC;AAED,SAAS,aAAa;IACpB,gFAAgF;IAChF,OAAO;EACP,mBAAmB,EAAE;EACrB,mBAAmB,EAAE;CACtB,CAAC;AACF,CAAC;AAED,SAAS,mBAAmB;IAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwNR,CAAC;AACF,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Type alias for the seller entry shape vendor commands work with.
3
+ * Mirrors the server-side `SellerRegistryEntry` but stays decoupled
4
+ * from `wallet-bootstrap`'s internal types so this package does not
5
+ * become a build-time dependency of the registry service.
6
+ */
7
+ export interface SellerRegistryEntry {
8
+ id: string;
9
+ name?: string;
10
+ profile?: string;
11
+ app?: string;
12
+ url: string;
13
+ status?: string;
14
+ region?: string;
15
+ modelsCount?: number;
16
+ sampleModels?: string[];
17
+ models?: string[];
18
+ supportedProtocols: string[];
19
+ paymentMethods: string[];
20
+ recommendedFor?: string[];
21
+ }
22
+ export { RegistryVendorClient, RegistryAdminClient } from "./client.js";
23
+ //# sourceMappingURL=vendor-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor-client.d.ts","sourceRoot":"","sources":["../../src/vendor-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { RegistryVendorClient, RegistryAdminClient } from "./client.js";
2
+ //# sourceMappingURL=vendor-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor-client.js","sourceRoot":"","sources":["../../src/vendor-client.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { RegistryVendorClient } from "./vendor-client.js";
2
+ /**
3
+ * vendor CLI helpers for the `bootstrap sellers` and
4
+ * `bootstrap release` subcommands. Step 5 of the registry redesign.
5
+ *
6
+ * - `bootstrap sellers add --file <path>`: stage a single seller
7
+ * for inclusion in the next release request submitted by this
8
+ * vendor. The server validates the payload shape, persists it to
9
+ * `seller_pending`, and returns a `pendingSeller` row.
10
+ * - `bootstrap release submit --note <text> --staged <id>`: submit
11
+ * a release request that includes one or more staged sellers.
12
+ * - `bootstrap release list`: list the vendor's release requests.
13
+ * - `bootstrap release show <id>`: show one release request.
14
+ * - `bootstrap release force-publish <id>`: super-admin path; the
15
+ * vendor token cannot force-publish itself (the platform owns
16
+ * the scheduler loop in Step 7), but we keep the command here
17
+ * so CI scripts have a single entry point.
18
+ */
19
+ export interface StageSellerInput {
20
+ client: RegistryVendorClient;
21
+ file: string;
22
+ }
23
+ export declare function stageSellerFromFile({ client, file }: StageSellerInput): Promise<{
24
+ pendingSeller: unknown;
25
+ }>;
26
+ export interface SubmitReleaseInput {
27
+ client: RegistryVendorClient;
28
+ stagedSellerIds: string[];
29
+ note?: string;
30
+ }
31
+ export declare function submitRelease({ client, stagedSellerIds, note }: SubmitReleaseInput): Promise<{
32
+ releaseRequest: unknown;
33
+ }>;
34
+ export declare function printReleaseRequestSummary(record: any): string;
35
+ //# sourceMappingURL=vendor-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor-commands.d.ts","sourceRoot":"","sources":["../../src/vendor-commands.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAuB,MAAM,oBAAoB,CAAC;AAE/E;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,mBAAmB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAAC;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC,CAOjH;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAA;CAAE,CAAC,CAK/H;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAgB9D"}
@@ -0,0 +1,33 @@
1
+ import * as fs from "fs";
2
+ export async function stageSellerFromFile({ client, file }) {
3
+ const content = fs.readFileSync(file, "utf8");
4
+ const parsed = JSON.parse(content);
5
+ if (!parsed.id || typeof parsed.id !== "string") {
6
+ throw new Error(`seller id is required in ${file}`);
7
+ }
8
+ return client.stageSeller(parsed);
9
+ }
10
+ export async function submitRelease({ client, stagedSellerIds, note }) {
11
+ if (stagedSellerIds.length === 0) {
12
+ throw new Error("at least one --staged <seller-id> is required");
13
+ }
14
+ return client.submitRelease({ stagedSellerIds, note });
15
+ }
16
+ export function printReleaseRequestSummary(record) {
17
+ if (!record) {
18
+ return "no release request";
19
+ }
20
+ const summary = record.payloadSummary || { count: 0, sellerIds: [] };
21
+ return [
22
+ `id: #${record.id}`,
23
+ `status: ${record.status}`,
24
+ `vendor: ${record.vendorId}`,
25
+ `sellers: ${summary.count} (${(summary.sellerIds || []).join(", ") || "—"})`,
26
+ `submittedAt: ${record.submittedAt || "—"}`,
27
+ `decidedAt: ${record.decidedAt || "—"}`,
28
+ `version: ${record.publishedVersion !== null && record.publishedVersion !== undefined ? `v${record.publishedVersion}` : "—"}`,
29
+ record.note ? `note: ${record.note}` : null,
30
+ record.errorMessage ? `error: ${record.errorMessage}` : null
31
+ ].filter(Boolean).join("\n");
32
+ }
33
+ //# sourceMappingURL=vendor-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor-commands.js","sourceRoot":"","sources":["../../src/vendor-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AA0BzB,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAoB;IAC1E,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAwB,CAAC;IAC1D,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAsB;IACvF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,MAAM,CAAC,aAAa,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAW;IACpD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACrE,OAAO;QACL,kBAAkB,MAAM,CAAC,EAAE,EAAE;QAC7B,iBAAiB,MAAM,CAAC,MAAM,EAAE;QAChC,iBAAiB,MAAM,CAAC,QAAQ,EAAE;QAClC,iBAAiB,OAAO,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG;QACjF,iBAAiB,MAAM,CAAC,WAAW,IAAI,GAAG,EAAE;QAC5C,iBAAiB,MAAM,CAAC,SAAS,IAAI,GAAG,EAAE;QAC1C,iBAAiB,MAAM,CAAC,gBAAgB,KAAK,IAAI,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;QAClI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;QACnD,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,iBAAiB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI;KACpE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenbuddy/tb-admin",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "Remote admin CLI for TokenBuddy seller apps",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",