adsinagents 0.1.10 → 0.1.11

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/cli/dist/index.js CHANGED
@@ -4209,7 +4209,14 @@ var DaemonStateSchema = external_exports.object({
4209
4209
  todayCents: external_exports.number().int().nonnegative().default(0),
4210
4210
  /** Claude is mid-turn (prompt received, Stop not yet). Drives the statusline
4211
4211
  * working glyph. Additive — older daemons omit it. */
4212
- working: external_exports.boolean().default(false)
4212
+ working: external_exports.boolean().default(false),
4213
+ /** Unix ms of the last ad rotation (== lastPollMs). Anchors the rotation
4214
+ * countdown bar; the statusline derives fill from (now - rotatedAtMs).
4215
+ * Additive — 0 from older daemons hides the bar. */
4216
+ rotatedAtMs: external_exports.number().int().nonnegative().default(0),
4217
+ /** Rotation period in ms (== pollIntervalSec * 1000). Bar denominator.
4218
+ * Additive — 0 from older daemons hides the bar. */
4219
+ rotationMs: external_exports.number().int().nonnegative().default(0)
4213
4220
  });
4214
4221
  var ImpressionSchema = external_exports.object({
4215
4222
  id: external_exports.string(),
@@ -4246,11 +4253,20 @@ var TERMINAL_BUNDLE_IDS = /* @__PURE__ */ new Set([
4246
4253
  "com.mitchellh.ghostty",
4247
4254
  "dev.warp.Warp-Stable",
4248
4255
  "dev.warp.Warp-Preview",
4256
+ "com.cmuxterm.app",
4257
+ // cmux
4249
4258
  // VS Code / Cursor integrated terminal => coarser confidence, still counts.
4250
4259
  "com.microsoft.VSCode",
4251
4260
  "com.todesktop.230313mzl4w4u92"
4252
4261
  // Cursor
4253
4262
  ]);
4263
+ var ENV_TERMINAL_BUNDLE_IDS = new Set((process.env.ADSINAGENTS_TERMINAL_BUNDLE_IDS ?? "").split(",").map((s) => s.trim()).filter(Boolean));
4264
+ function isTerminalBundle(bundleId) {
4265
+ return TERMINAL_BUNDLE_IDS.has(bundleId) || ENV_TERMINAL_BUNDLE_IDS.has(bundleId);
4266
+ }
4267
+
4268
+ // ../../shared/dist/earnings.js
4269
+ var DAILY_CAP_MILLICENTS = 1e4 * 1e3;
4254
4270
 
4255
4271
  // ../../shared/dist/nonce.js
4256
4272
  var NONCE_TTL_MS = 30 * 60 * 1e3;
@@ -4277,10 +4293,12 @@ var DarwinPlatform = class {
4277
4293
  const { stdout } = await pExecFile(PATHS.frontmostBin, [], { timeout: 800 });
4278
4294
  const parsed = FocusProbeSchema.safeParse(JSON.parse(stdout));
4279
4295
  if (parsed.success) {
4296
+ const focused = isTerminalBundle(parsed.data.bundleId);
4280
4297
  return {
4281
- focused: TERMINAL_BUNDLE_IDS.has(parsed.data.bundleId),
4298
+ focused,
4282
4299
  locked: parsed.data.locked,
4283
- screenAwake: parsed.data.screenAwake
4300
+ screenAwake: parsed.data.screenAwake,
4301
+ unknownFrontApp: focused ? void 0 : parsed.data.bundleId || void 0
4284
4302
  };
4285
4303
  }
4286
4304
  } catch {
@@ -4296,7 +4314,13 @@ var DarwinPlatform = class {
4296
4314
  'tell application "System Events" to get bundle identifier of first application process whose frontmost is true'
4297
4315
  ], { timeout: 800 });
4298
4316
  const bundleId = stdout.trim();
4299
- return { focused: TERMINAL_BUNDLE_IDS.has(bundleId), locked: false, screenAwake: true };
4317
+ const focused = isTerminalBundle(bundleId);
4318
+ return {
4319
+ focused,
4320
+ locked: false,
4321
+ screenAwake: true,
4322
+ unknownFrontApp: focused ? void 0 : bundleId || void 0
4323
+ };
4300
4324
  } catch {
4301
4325
  return { focused: false, locked: false, screenAwake: true };
4302
4326
  }
@@ -4880,6 +4904,19 @@ async function runDoctor() {
4880
4904
  ok: focusOk,
4881
4905
  detail: focusOk ? "native frontmost helper present" : "Node fallback (osascript)"
4882
4906
  });
4907
+ try {
4908
+ const fm = await platform.frontmost();
4909
+ if (fm.unknownFrontApp) {
4910
+ checks.push({
4911
+ name: "Terminal recognized",
4912
+ ok: false,
4913
+ detail: `frontmost app "${fm.unknownFrontApp}" isn't a known terminal \u2014 impressions won't fire. If this is your terminal: set ADSINAGENTS_TERMINAL_BUNDLE_IDS=${fm.unknownFrontApp} and restart the daemon.`
4914
+ });
4915
+ } else if (fm.focused) {
4916
+ checks.push({ name: "Terminal recognized", ok: true, detail: "frontmost app is a known terminal" });
4917
+ }
4918
+ } catch {
4919
+ }
4883
4920
  let key = null;
4884
4921
  try {
4885
4922
  key = await platform.getSecret("publisher-key");
@@ -4217,7 +4217,14 @@ var DaemonStateSchema = external_exports.object({
4217
4217
  todayCents: external_exports.number().int().nonnegative().default(0),
4218
4218
  /** Claude is mid-turn (prompt received, Stop not yet). Drives the statusline
4219
4219
  * working glyph. Additive — older daemons omit it. */
4220
- working: external_exports.boolean().default(false)
4220
+ working: external_exports.boolean().default(false),
4221
+ /** Unix ms of the last ad rotation (== lastPollMs). Anchors the rotation
4222
+ * countdown bar; the statusline derives fill from (now - rotatedAtMs).
4223
+ * Additive — 0 from older daemons hides the bar. */
4224
+ rotatedAtMs: external_exports.number().int().nonnegative().default(0),
4225
+ /** Rotation period in ms (== pollIntervalSec * 1000). Bar denominator.
4226
+ * Additive — 0 from older daemons hides the bar. */
4227
+ rotationMs: external_exports.number().int().nonnegative().default(0)
4221
4228
  });
4222
4229
  var ImpressionSchema = external_exports.object({
4223
4230
  id: external_exports.string(),
@@ -4254,11 +4261,20 @@ var TERMINAL_BUNDLE_IDS = /* @__PURE__ */ new Set([
4254
4261
  "com.mitchellh.ghostty",
4255
4262
  "dev.warp.Warp-Stable",
4256
4263
  "dev.warp.Warp-Preview",
4264
+ "com.cmuxterm.app",
4265
+ // cmux
4257
4266
  // VS Code / Cursor integrated terminal => coarser confidence, still counts.
4258
4267
  "com.microsoft.VSCode",
4259
4268
  "com.todesktop.230313mzl4w4u92"
4260
4269
  // Cursor
4261
4270
  ]);
4271
+ var ENV_TERMINAL_BUNDLE_IDS = new Set((process.env.ADSINAGENTS_TERMINAL_BUNDLE_IDS ?? "").split(",").map((s) => s.trim()).filter(Boolean));
4272
+ function isTerminalBundle(bundleId) {
4273
+ return TERMINAL_BUNDLE_IDS.has(bundleId) || ENV_TERMINAL_BUNDLE_IDS.has(bundleId);
4274
+ }
4275
+
4276
+ // ../../shared/dist/earnings.js
4277
+ var DAILY_CAP_MILLICENTS = 1e4 * 1e3;
4262
4278
 
4263
4279
  // ../../shared/dist/nonce.js
4264
4280
  var NONCE_TTL_MS = 30 * 60 * 1e3;
@@ -4285,10 +4301,12 @@ var DarwinPlatform = class {
4285
4301
  const { stdout } = await pExecFile(PATHS.frontmostBin, [], { timeout: 800 });
4286
4302
  const parsed = FocusProbeSchema.safeParse(JSON.parse(stdout));
4287
4303
  if (parsed.success) {
4304
+ const focused = isTerminalBundle(parsed.data.bundleId);
4288
4305
  return {
4289
- focused: TERMINAL_BUNDLE_IDS.has(parsed.data.bundleId),
4306
+ focused,
4290
4307
  locked: parsed.data.locked,
4291
- screenAwake: parsed.data.screenAwake
4308
+ screenAwake: parsed.data.screenAwake,
4309
+ unknownFrontApp: focused ? void 0 : parsed.data.bundleId || void 0
4292
4310
  };
4293
4311
  }
4294
4312
  } catch {
@@ -4304,7 +4322,13 @@ var DarwinPlatform = class {
4304
4322
  'tell application "System Events" to get bundle identifier of first application process whose frontmost is true'
4305
4323
  ], { timeout: 800 });
4306
4324
  const bundleId = stdout.trim();
4307
- return { focused: TERMINAL_BUNDLE_IDS.has(bundleId), locked: false, screenAwake: true };
4325
+ const focused = isTerminalBundle(bundleId);
4326
+ return {
4327
+ focused,
4328
+ locked: false,
4329
+ screenAwake: true,
4330
+ unknownFrontApp: focused ? void 0 : bundleId || void 0
4331
+ };
4308
4332
  } catch {
4309
4333
  return { focused: false, locked: false, screenAwake: true };
4310
4334
  }
@@ -4961,6 +4985,14 @@ var FocusPoller = class {
4961
4985
  terminalFocused() {
4962
4986
  return this.last.focused;
4963
4987
  }
4988
+ /**
4989
+ * Bundle id of the frontmost app when it's a foreground app we DON'T recognize
4990
+ * as a terminal — the daemon warns on it so an unlisted terminal can't silently
4991
+ * zero out earnings. Undefined when on a known terminal. Diagnostics only.
4992
+ */
4993
+ unknownFrontApp() {
4994
+ return this.last.unknownFrontApp;
4995
+ }
4964
4996
  async poll() {
4965
4997
  try {
4966
4998
  this.last = await this.platform.frontmost();
@@ -5335,6 +5367,7 @@ function pendingReason(s) {
5335
5367
  var TICK_MS = 1e3;
5336
5368
  var SESSION_REAP_MS = 6 * 60 * 60 * 1e3;
5337
5369
  var SYNC_EVERY_TICKS = 30;
5370
+ var UNKNOWN_TERM_WARN_MS = 10 * 60 * 1e3;
5338
5371
  async function fileLog(line) {
5339
5372
  try {
5340
5373
  writeFileSync4(PATHS.daemonLog, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
@@ -5354,6 +5387,7 @@ async function main() {
5354
5387
  let currentAd = null;
5355
5388
  let lastPollMs = 0;
5356
5389
  let tickCount = 0;
5390
+ const unknownTermWarnedAt = /* @__PURE__ */ new Map();
5357
5391
  let earnings = { balanceCents: 0, todayCents: 0 };
5358
5392
  const now = () => Date.now();
5359
5393
  const { server, port } = await startServer({
@@ -5416,6 +5450,16 @@ async function main() {
5416
5450
  const lastStop = sessions.lastStop();
5417
5451
  const adDisplayable = working || lastStop !== null && nowMs - lastStop <= cfg.idleGraceSec * 1e3;
5418
5452
  const probe = focus.current();
5453
+ const unknownTerm = focus.unknownFrontApp();
5454
+ if (sessionLive && currentAd !== null && adDisplayable && unknownTerm) {
5455
+ const lastWarn = unknownTermWarnedAt.get(unknownTerm) ?? 0;
5456
+ if (nowMs - lastWarn >= UNKNOWN_TERM_WARN_MS) {
5457
+ unknownTermWarnedAt.set(unknownTerm, nowMs);
5458
+ await fileLog(
5459
+ `WARN unrecognized terminal "${unknownTerm}" is frontmost \u2014 impressions can't fire. If this is your terminal, set ADSINAGENTS_TERMINAL_BUNDLE_IDS=${unknownTerm} and restart the daemon.`
5460
+ );
5461
+ }
5462
+ }
5419
5463
  const sample = {
5420
5464
  nowMs,
5421
5465
  sessionLive,
@@ -5449,7 +5493,11 @@ async function main() {
5449
5493
  updatedAt: nowMs,
5450
5494
  balanceCents: earnings.balanceCents,
5451
5495
  todayCents: earnings.todayCents,
5452
- working
5496
+ working,
5497
+ // Anchor + period for the statusline rotation countdown bar. lastPollMs
5498
+ // is the last ad swap; the bar fills over pollIntervalSec toward the next.
5499
+ rotatedAtMs: lastPollMs,
5500
+ rotationMs: cfg.pollIntervalSec * 1e3
5453
5501
  };
5454
5502
  writeDaemonState(state);
5455
5503
  if (tickCount % SYNC_EVERY_TICKS === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adsinagents",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Get paid while you build. AdsInAgents is the terminal-native ad layer for AI coding agents. Verified impressions pay you.",
5
5
  "homepage": "https://adsinagents.com",
6
6
  "license": "UNLICENSED",
@@ -4193,7 +4193,14 @@ var DaemonStateSchema = external_exports.object({
4193
4193
  todayCents: external_exports.number().int().nonnegative().default(0),
4194
4194
  /** Claude is mid-turn (prompt received, Stop not yet). Drives the statusline
4195
4195
  * working glyph. Additive — older daemons omit it. */
4196
- working: external_exports.boolean().default(false)
4196
+ working: external_exports.boolean().default(false),
4197
+ /** Unix ms of the last ad rotation (== lastPollMs). Anchors the rotation
4198
+ * countdown bar; the statusline derives fill from (now - rotatedAtMs).
4199
+ * Additive — 0 from older daemons hides the bar. */
4200
+ rotatedAtMs: external_exports.number().int().nonnegative().default(0),
4201
+ /** Rotation period in ms (== pollIntervalSec * 1000). Bar denominator.
4202
+ * Additive — 0 from older daemons hides the bar. */
4203
+ rotationMs: external_exports.number().int().nonnegative().default(0)
4197
4204
  });
4198
4205
  var ImpressionSchema = external_exports.object({
4199
4206
  id: external_exports.string(),
@@ -4222,6 +4229,7 @@ var FocusProbeSchema = external_exports.object({
4222
4229
  locked: external_exports.boolean(),
4223
4230
  screenAwake: external_exports.boolean()
4224
4231
  });
4232
+ var ENV_TERMINAL_BUNDLE_IDS = new Set((process.env.ADSINAGENTS_TERMINAL_BUNDLE_IDS ?? "").split(",").map((s) => s.trim()).filter(Boolean));
4225
4233
 
4226
4234
  // ../../shared/dist/render.js
4227
4235
  function osc8(url, text) {
@@ -4253,6 +4261,15 @@ function renderEarningsSegment(balanceCents, todayCents, theme = "plain") {
4253
4261
  const d = (c) => t.money(`$${(Math.max(0, c) / 100).toFixed(2)}`);
4254
4262
  return `${d(todayCents)} today \xB7 ${d(balanceCents)}`;
4255
4263
  }
4264
+ var ROTATION_BAR_CELLS = 8;
4265
+ function renderRotationBar(nowMs, rotatedAtMs, rotationMs, theme = "plain") {
4266
+ if (rotationMs <= 0 || rotatedAtMs <= 0)
4267
+ return "";
4268
+ const frac = Math.min(1, Math.max(0, (nowMs - rotatedAtMs) / rotationMs));
4269
+ const filled = Math.min(ROTATION_BAR_CELLS, Math.round(frac * ROTATION_BAR_CELLS));
4270
+ const bar = "\u25B0".repeat(filled) + "\u25B1".repeat(ROTATION_BAR_CELLS - filled);
4271
+ return THEMES[theme ?? "plain"].mark(bar);
4272
+ }
4256
4273
  function renderLoadingSegment(theme = "plain") {
4257
4274
  const s = "Ad: loading\u2026";
4258
4275
  return theme === "plain" ? s : DIM(s);
@@ -4267,6 +4284,9 @@ function composeStatusLine(priorOutput, ...segments) {
4267
4284
  return parts.join(" \u2502 ");
4268
4285
  }
4269
4286
 
4287
+ // ../../shared/dist/earnings.js
4288
+ var DAILY_CAP_MILLICENTS = 1e4 * 1e3;
4289
+
4270
4290
  // ../../shared/dist/nonce.js
4271
4291
  var NONCE_TTL_MS = 30 * 60 * 1e3;
4272
4292
 
@@ -4370,6 +4390,12 @@ function main() {
4370
4390
  theme
4371
4391
  });
4372
4392
  const seg = state.working ? `${workingGlyph(Date.now(), theme)} ${adSeg}` : adSeg;
4373
- process.stdout.write(composeStatusLine(prior, seg, earningsSeg) + "\n");
4393
+ const rotationSeg = renderRotationBar(
4394
+ Date.now(),
4395
+ state.rotatedAtMs,
4396
+ state.rotationMs,
4397
+ theme
4398
+ );
4399
+ process.stdout.write(composeStatusLine(prior, seg, rotationSeg, earningsSeg) + "\n");
4374
4400
  }
4375
4401
  main();