tokenwait 0.1.1 → 0.1.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/bin/status.js CHANGED
@@ -29,13 +29,13 @@ function main() {
29
29
  orig = (r.stdout || '').replace(/\s+$/, '');
30
30
  }
31
31
 
32
- const { p, readJson, adLine } = require('../lib/util');
32
+ const { p, readJson, adLineLinked } = require('../lib/util');
33
33
  const { pick } = require('../lib/rotation');
34
34
  const worker = require('../lib/worker');
35
35
  const ads = readJson(p('ads.json'), null);
36
36
  const cfg = readJson(p('config.json'), {});
37
37
  const ad = ads && pick(ads.ads, Date.now(), cfg.rotate_seconds || 30);
38
- const line = adLine(ad, ads && ads.label);
38
+ const line = adLineLinked(ad, ads && ads.label);
39
39
  if (worker.adsStale()) worker.spawnWorker();
40
40
 
41
41
  let out;
package/lib/rotation.js CHANGED
@@ -33,12 +33,17 @@ function pick(ads, t, rotateSeconds) {
33
33
  function normalizeAds(list) {
34
34
  if (!Array.isArray(list)) return [];
35
35
  return list
36
- .map((a) => ({
37
- id: sanitizeText(a && a.id, 64),
38
- text: sanitizeText(a && a.text),
39
- url: safeUrl(a && a.url),
40
- weight: Math.max(1, Math.floor(Number(a && a.weight)) || 1),
41
- }))
36
+ .map((a) => {
37
+ const ad = {
38
+ id: sanitizeText(a && a.id, 64),
39
+ text: sanitizeText(a && a.text),
40
+ url: safeUrl(a && a.url),
41
+ weight: Math.max(1, Math.floor(Number(a && a.weight)) || 1),
42
+ };
43
+ // host — подпись в скобках с сервера (ссылка ведёт на наш /r/, поэтому домен из неё не показываем)
44
+ if (a && typeof a.host === 'string') ad.host = sanitizeText(a.host, 40);
45
+ return ad;
46
+ })
42
47
  .filter((a) => a.id && a.text)
43
48
  .slice(0, 50);
44
49
  }
package/lib/util.js CHANGED
@@ -13,13 +13,30 @@ function adLabel(label) {
13
13
  return /\b(ad|ads|sponsored|advert\w*|promo\w*)\b|реклам/i.test(s) ? s : s + ' · Ad';
14
14
  }
15
15
 
16
+ /** Что показать в скобках: host с сервера (имя рекламодателя или сайт), иначе домен ссылки. */
17
+ function adHost(ad) {
18
+ return typeof ad.host === 'string' ? sanitizeText(ad.host, 40) : hostOf(ad.url);
19
+ }
20
+
16
21
  /** Строка показа: «TokenWait · Ad · текст (сайт)». */
17
22
  function adLine(ad, label) {
18
23
  if (!ad || !ad.text) return '';
19
- const host = hostOf(ad.url);
24
+ const host = adHost(ad);
20
25
  return adLabel(label) + ' · ' + ad.text + (host ? ' (' + host + ')' : '');
21
26
  }
22
27
 
28
+ /**
29
+ * То же, но текст объявления — кликабельная ссылка OSC 8 (Cmd/Ctrl+клик в iTerm2, WezTerm, Kitty, терминалах IDE).
30
+ * Терминалы без поддержки показывают обычный текст. Отключается TOKENWAIT_NO_LINKS=1.
31
+ */
32
+ function adLineLinked(ad, label) {
33
+ const plain = adLine(ad, label);
34
+ const url = ad && safeUrl(ad.url);
35
+ if (!plain || !url || process.env.TOKENWAIT_NO_LINKS === '1' || /[\u0000-\u0020\u007f-\u009f]/.test(url)) return plain;
36
+ const head = adLabel(label) + ' · ';
37
+ return head + '\x1b]8;;' + url + '\x07' + plain.slice(head.length) + '\x1b]8;;\x07';
38
+ }
39
+
23
40
  function home() {
24
41
  return process.env.TOKENWAIT_HOME || path.join(os.homedir(), '.tokenwait');
25
42
  }
@@ -185,6 +202,8 @@ module.exports = {
185
202
  DEFAULT_LABEL,
186
203
  adLabel,
187
204
  adLine,
205
+ adLineLinked,
206
+ adHost,
188
207
  home,
189
208
  p,
190
209
  readJson,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenwait",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Get paid while your AI coding agent thinks. A clearly labelled sponsored line for Claude Code, Codex CLI and Gemini CLI.",
5
5
  "bin": {
6
6
  "tokenwait": "bin/tokenwait.js"