adsinagents 0.1.7 → 0.1.9
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 +273 -43
- package/daemon/dist/main.js +40 -13
- package/native/build.sh +10 -0
- package/package.json +1 -1
- package/statusline/dist/index.js +4 -2
package/cli/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
// ../cli/src/index.ts
|
|
11
|
-
import { dirname as dirname4, join as
|
|
11
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
12
12
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
13
13
|
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "node:fs";
|
|
14
14
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
@@ -4166,8 +4166,10 @@ var ConfigSchema = external_exports.object({
|
|
|
4166
4166
|
deviceId: external_exports.string().default(""),
|
|
4167
4167
|
/** Single-use token to claim this install at /claim (attach a login + payout). */
|
|
4168
4168
|
claimToken: external_exports.string().default(""),
|
|
4169
|
-
/** Ad cache poll interval (seconds).
|
|
4170
|
-
|
|
4169
|
+
/** Ad cache poll interval (seconds) — how often a new ad rotates in. Billing
|
|
4170
|
+
* is gated separately by rateCapSec, so a faster poll rotates creatives
|
|
4171
|
+
* without changing earnings. */
|
|
4172
|
+
pollIntervalSec: external_exports.number().int().positive().default(30),
|
|
4171
4173
|
/** Hard daily fired-impression cap. */
|
|
4172
4174
|
dailyCap: external_exports.number().int().positive().default(300),
|
|
4173
4175
|
/** Min seconds of verified view between two fired impressions. */
|
|
@@ -4542,8 +4544,8 @@ var ClaudeCodeAdapter = class {
|
|
|
4542
4544
|
else
|
|
4543
4545
|
priorValues.hooks = cur.hooks;
|
|
4544
4546
|
const mk = (path) => ({
|
|
4545
|
-
type: "
|
|
4546
|
-
|
|
4547
|
+
type: "command",
|
|
4548
|
+
command: hookCommand(path)
|
|
4547
4549
|
});
|
|
4548
4550
|
next.hooks = {
|
|
4549
4551
|
...hooks,
|
|
@@ -4568,8 +4570,8 @@ var ClaudeCodeAdapter = class {
|
|
|
4568
4570
|
const backup = this.backup();
|
|
4569
4571
|
const { next, diff } = this.merge(plan);
|
|
4570
4572
|
this.write(next);
|
|
4571
|
-
this.writeSlashCommand();
|
|
4572
|
-
return { backup, diff };
|
|
4573
|
+
const slashCommand = this.writeSlashCommand();
|
|
4574
|
+
return { backup, diff, slashCommand };
|
|
4573
4575
|
}
|
|
4574
4576
|
/** Path of the in-session settings command, e.g. ~/.claude/commands/adsinagents.md. */
|
|
4575
4577
|
slashCommandPath() {
|
|
@@ -4579,14 +4581,22 @@ var ClaudeCodeAdapter = class {
|
|
|
4579
4581
|
* Install /adsinagents — the status line itself is display-only, so this is
|
|
4580
4582
|
* the in-session settings surface: the user types /adsinagents and Claude
|
|
4581
4583
|
* drives the CLI. File is fully ours (sentinel comment), safe to delete on
|
|
4582
|
-
* remove.
|
|
4584
|
+
* remove. Reconciles: writes if missing or stale (content drift), skips if
|
|
4585
|
+
* already correct. Returns the outcome so init can REPORT failures instead of
|
|
4586
|
+
* silently swallowing them (the old try/catch hid a real install bug — the
|
|
4587
|
+
* file never wrote and doctor was the only thing that ever noticed).
|
|
4583
4588
|
*/
|
|
4584
4589
|
writeSlashCommand() {
|
|
4590
|
+
const p = this.slashCommandPath();
|
|
4585
4591
|
try {
|
|
4586
|
-
|
|
4592
|
+
if (existsSync2(p) && readFileSync2(p, "utf8") === SLASH_COMMAND_MD) {
|
|
4593
|
+
return { ok: true };
|
|
4594
|
+
}
|
|
4587
4595
|
mkdirSync2(dirname2(p), { recursive: true });
|
|
4588
4596
|
writeFileSync2(p, SLASH_COMMAND_MD);
|
|
4589
|
-
|
|
4597
|
+
return { ok: true };
|
|
4598
|
+
} catch (e) {
|
|
4599
|
+
return { ok: false, reason: e.message };
|
|
4590
4600
|
}
|
|
4591
4601
|
}
|
|
4592
4602
|
removeInstall() {
|
|
@@ -4716,10 +4726,18 @@ function spinnerVerbsFor(ad) {
|
|
|
4716
4726
|
`Brought to you by ${brand}`
|
|
4717
4727
|
];
|
|
4718
4728
|
}
|
|
4729
|
+
function hookCommand(path) {
|
|
4730
|
+
return [
|
|
4731
|
+
`p=$(node -e 'try{process.stdout.write(String(require(process.env.HOME+"/.adsinagents/daemon.json").port||""))}catch(e){}' 2>/dev/null)`,
|
|
4732
|
+
`[ -n "$p" ] && curl -s --max-time 2 -X POST "http://127.0.0.1:$p${path}" -H 'content-type: application/json' --data-binary @- >/dev/null 2>&1`,
|
|
4733
|
+
`true`
|
|
4734
|
+
].join("; ");
|
|
4735
|
+
}
|
|
4719
4736
|
function appendHook(existing, entry) {
|
|
4720
4737
|
const arr = Array.isArray(existing) ? existing.slice() : existing ? [existing] : [];
|
|
4721
|
-
arr.
|
|
4722
|
-
|
|
4738
|
+
const userOwned = arr.filter((e) => !(e && typeof e === "object" && e[ADLINE_SENTINEL]));
|
|
4739
|
+
userOwned.push({ hooks: [entry], [ADLINE_SENTINEL]: true });
|
|
4740
|
+
return userOwned;
|
|
4723
4741
|
}
|
|
4724
4742
|
function renderDiff(before, after) {
|
|
4725
4743
|
const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
@@ -4761,6 +4779,9 @@ function getAgent(name = "claude-code") {
|
|
|
4761
4779
|
|
|
4762
4780
|
// ../cli/src/settings.ts
|
|
4763
4781
|
var cc = () => getAgent("claude-code");
|
|
4782
|
+
function isInstalled() {
|
|
4783
|
+
return cc().isInstalled();
|
|
4784
|
+
}
|
|
4764
4785
|
function planMerge(plan) {
|
|
4765
4786
|
return cc().planInstall(plan);
|
|
4766
4787
|
}
|
|
@@ -4804,6 +4825,10 @@ function buildAndInstallNative() {
|
|
|
4804
4825
|
if (existsSync3(src)) {
|
|
4805
4826
|
copyFileSync2(src, dest);
|
|
4806
4827
|
chmodSync2(dest, 493);
|
|
4828
|
+
try {
|
|
4829
|
+
execFileSync3("codesign", ["-f", "--sign", "-", dest], { stdio: "pipe" });
|
|
4830
|
+
} catch {
|
|
4831
|
+
}
|
|
4807
4832
|
installed.push(dest);
|
|
4808
4833
|
}
|
|
4809
4834
|
}
|
|
@@ -4812,6 +4837,9 @@ function buildAndInstallNative() {
|
|
|
4812
4837
|
|
|
4813
4838
|
// ../cli/src/doctor.ts
|
|
4814
4839
|
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
4840
|
+
import { spawnSync } from "node:child_process";
|
|
4841
|
+
import { homedir as homedir3 } from "node:os";
|
|
4842
|
+
import { join as join5 } from "node:path";
|
|
4815
4843
|
function daemonAlive() {
|
|
4816
4844
|
if (!existsSync4(PATHS.daemonInfo)) return { ok: false, detail: "no daemon.json (not started)" };
|
|
4817
4845
|
try {
|
|
@@ -4863,8 +4891,133 @@ async function runDoctor() {
|
|
|
4863
4891
|
ok: key !== null,
|
|
4864
4892
|
detail: key ? "present (Keychain/file)" : "none \u2014 run `adsinagents login`"
|
|
4865
4893
|
});
|
|
4894
|
+
checks.push(checkStatusLine());
|
|
4895
|
+
checks.push(checkHookReachable());
|
|
4896
|
+
checks.push(checkSlashCommand());
|
|
4866
4897
|
return checks;
|
|
4867
4898
|
}
|
|
4899
|
+
function checkStatusLine() {
|
|
4900
|
+
const name = "Status line renders";
|
|
4901
|
+
const settingsPath = join5(claudeConfigDir(), "settings.json");
|
|
4902
|
+
if (!existsSync4(settingsPath)) {
|
|
4903
|
+
return { name, ok: true, detail: "no statusLine command configured" };
|
|
4904
|
+
}
|
|
4905
|
+
let settings;
|
|
4906
|
+
try {
|
|
4907
|
+
settings = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
4908
|
+
} catch {
|
|
4909
|
+
return { name, ok: true, detail: "settings.json unreadable (skipped)" };
|
|
4910
|
+
}
|
|
4911
|
+
const cmd = settings.statusLine?.command;
|
|
4912
|
+
if (!cmd) {
|
|
4913
|
+
return { name, ok: true, detail: "no statusLine command configured" };
|
|
4914
|
+
}
|
|
4915
|
+
let hasAd = false;
|
|
4916
|
+
try {
|
|
4917
|
+
if (existsSync4(PATHS.currentAd)) {
|
|
4918
|
+
const ad = JSON.parse(readFileSync3(PATHS.currentAd, "utf8"));
|
|
4919
|
+
hasAd = !!(ad.adId && String(ad.adId).trim().length > 0);
|
|
4920
|
+
}
|
|
4921
|
+
} catch {
|
|
4922
|
+
hasAd = false;
|
|
4923
|
+
}
|
|
4924
|
+
const parts = cmd.split(" ");
|
|
4925
|
+
const exe = parts[0];
|
|
4926
|
+
const args = parts.slice(1);
|
|
4927
|
+
const sessionJson = JSON.stringify({
|
|
4928
|
+
session_id: "doctor",
|
|
4929
|
+
workspace: { current_dir: "/tmp" },
|
|
4930
|
+
model: { display_name: "x" }
|
|
4931
|
+
});
|
|
4932
|
+
const result = spawnSync(exe, args, {
|
|
4933
|
+
input: sessionJson,
|
|
4934
|
+
encoding: "utf8",
|
|
4935
|
+
timeout: 3e3
|
|
4936
|
+
});
|
|
4937
|
+
if (result.error) {
|
|
4938
|
+
const errCode = result.error.code;
|
|
4939
|
+
const msg = errCode === "ENOENT" ? "not found" : result.error.message;
|
|
4940
|
+
return { name, ok: false, detail: `statusLine command not found or not executable: ${exe} (${msg})` };
|
|
4941
|
+
}
|
|
4942
|
+
if (result.status !== 0) {
|
|
4943
|
+
return { name, ok: false, detail: `statusLine command exited with error (code ${result.status ?? "?"})` };
|
|
4944
|
+
}
|
|
4945
|
+
const stdout = (result.stdout ?? "").trim();
|
|
4946
|
+
if (hasAd && stdout.length === 0) {
|
|
4947
|
+
return { name, ok: false, detail: "statusLine command produced no output (ad exists but nothing rendered)" };
|
|
4948
|
+
}
|
|
4949
|
+
if (stdout.length > 0) {
|
|
4950
|
+
return { name, ok: true, detail: `rendered ${stdout.length} chars` };
|
|
4951
|
+
}
|
|
4952
|
+
return { name, ok: true, detail: "ok (no ad to render right now)" };
|
|
4953
|
+
}
|
|
4954
|
+
function checkHookReachable() {
|
|
4955
|
+
const name = "Hook reachable";
|
|
4956
|
+
let daemonPort = null;
|
|
4957
|
+
if (existsSync4(PATHS.daemonInfo)) {
|
|
4958
|
+
try {
|
|
4959
|
+
const info = JSON.parse(readFileSync3(PATHS.daemonInfo, "utf8"));
|
|
4960
|
+
daemonPort = info.port ?? null;
|
|
4961
|
+
} catch {
|
|
4962
|
+
daemonPort = null;
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
if (daemonPort === null) {
|
|
4966
|
+
return { name, ok: true, detail: "daemon not running (skipped)" };
|
|
4967
|
+
}
|
|
4968
|
+
const settingsPath = join5(claudeConfigDir(), "settings.json");
|
|
4969
|
+
let hookPort = null;
|
|
4970
|
+
if (existsSync4(settingsPath)) {
|
|
4971
|
+
try {
|
|
4972
|
+
const s = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
4973
|
+
const hooksStr = JSON.stringify(s.hooks ?? {});
|
|
4974
|
+
const m = /http:\/\/127\.0\.0\.1:(\d+)/.exec(hooksStr);
|
|
4975
|
+
if (m) {
|
|
4976
|
+
hookPort = Number(m[1]);
|
|
4977
|
+
} else {
|
|
4978
|
+
hookPort = daemonPort;
|
|
4979
|
+
}
|
|
4980
|
+
} catch {
|
|
4981
|
+
hookPort = daemonPort;
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
const result = spawnSync(
|
|
4985
|
+
"node",
|
|
4986
|
+
[
|
|
4987
|
+
"-e",
|
|
4988
|
+
`const h=require("node:http");const r=h.get("http://127.0.0.1:${daemonPort}/health",res=>{let b="";res.on("data",d=>b+=d);res.on("end",()=>{process.stdout.write(b);process.exit(0)});});r.on("error",e=>{process.exit(1)});r.setTimeout(2000,()=>{r.destroy();process.exit(2)});`
|
|
4989
|
+
],
|
|
4990
|
+
{ encoding: "utf8", timeout: 4e3 }
|
|
4991
|
+
);
|
|
4992
|
+
if (result.status !== 0) {
|
|
4993
|
+
return { name, ok: false, detail: `daemon unreachable on port ${daemonPort}` };
|
|
4994
|
+
}
|
|
4995
|
+
if (hookPort !== null && hookPort !== daemonPort) {
|
|
4996
|
+
return {
|
|
4997
|
+
name,
|
|
4998
|
+
ok: false,
|
|
4999
|
+
detail: `hooks point at port ${hookPort} but daemon is on ${daemonPort}`
|
|
5000
|
+
};
|
|
5001
|
+
}
|
|
5002
|
+
return { name, ok: true, detail: `daemon OK on port ${daemonPort}` };
|
|
5003
|
+
}
|
|
5004
|
+
function checkSlashCommand() {
|
|
5005
|
+
const name = "Slash command installed";
|
|
5006
|
+
const SENTINEL = "<!-- adsinagents:managed -->";
|
|
5007
|
+
const cmdPath = join5(claudeConfigDir(), "commands", "adsinagents.md");
|
|
5008
|
+
if (!existsSync4(cmdPath)) {
|
|
5009
|
+
return { name, ok: false, detail: "run `adsinagents init` to reinstall the /adsinagents command" };
|
|
5010
|
+
}
|
|
5011
|
+
try {
|
|
5012
|
+
const contents = readFileSync3(cmdPath, "utf8");
|
|
5013
|
+
if (!contents.includes(SENTINEL)) {
|
|
5014
|
+
return { name, ok: false, detail: "run `adsinagents init` to reinstall the /adsinagents command" };
|
|
5015
|
+
}
|
|
5016
|
+
} catch {
|
|
5017
|
+
return { name, ok: false, detail: "run `adsinagents init` to reinstall the /adsinagents command" };
|
|
5018
|
+
}
|
|
5019
|
+
return { name, ok: true, detail: `present at ${cmdPath.replace(homedir3(), "~")}` };
|
|
5020
|
+
}
|
|
4868
5021
|
function printDoctor(checks) {
|
|
4869
5022
|
for (const c of checks) {
|
|
4870
5023
|
const mark = c.ok ? "\u2713" : "\u2717";
|
|
@@ -4873,19 +5026,73 @@ function printDoctor(checks) {
|
|
|
4873
5026
|
}
|
|
4874
5027
|
}
|
|
4875
5028
|
|
|
5029
|
+
// ../cli/src/init-ui.ts
|
|
5030
|
+
var useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
5031
|
+
var ID = (s) => s;
|
|
5032
|
+
var dim = useColor ? (s) => `\x1B[2m${s}\x1B[0m` : ID;
|
|
5033
|
+
var bold = useColor ? (s) => `\x1B[1m${s}\x1B[0m` : ID;
|
|
5034
|
+
var green = useColor ? (s) => `\x1B[32m${s}\x1B[0m` : ID;
|
|
5035
|
+
var amber = useColor ? (s) => `\x1B[38;5;215m${s}\x1B[0m` : ID;
|
|
5036
|
+
function renderInitSummary(data) {
|
|
5037
|
+
const lines = [];
|
|
5038
|
+
const tick = green("\u2713");
|
|
5039
|
+
const warn = amber("!");
|
|
5040
|
+
const dot = dim("\xB7");
|
|
5041
|
+
if (data.firstRun) {
|
|
5042
|
+
lines.push(`${amber("\u258C")} ${bold("AdsInAgents")} ${dim("\u2014 sponsored status line for Claude Code")}`);
|
|
5043
|
+
lines.push("");
|
|
5044
|
+
const labelW = maxLabelWidth(data.steps);
|
|
5045
|
+
for (const step of data.steps) {
|
|
5046
|
+
const icon = step.ok ? tick : warn;
|
|
5047
|
+
const label = step.label.padEnd(labelW);
|
|
5048
|
+
const note = step.note ? ` ${dim("(" + step.note + ")")}` : "";
|
|
5049
|
+
lines.push(` ${icon} ${label} ${dot} ${step.detail}${note}`);
|
|
5050
|
+
}
|
|
5051
|
+
lines.push("");
|
|
5052
|
+
lines.push(` ${bold("You're live.")} Restart Claude Code to see your first ad.`);
|
|
5053
|
+
lines.push("");
|
|
5054
|
+
lines.push(` ${dim("Verify ")} adsinagents doctor`);
|
|
5055
|
+
if (data.claimUrl) {
|
|
5056
|
+
lines.push(` ${dim("Get paid")} ${amber(data.claimUrl)}`);
|
|
5057
|
+
}
|
|
5058
|
+
lines.push("");
|
|
5059
|
+
lines.push(` ${dim("Ads are test-only for now.")}`);
|
|
5060
|
+
} else {
|
|
5061
|
+
lines.push(`${amber("\u258C")} ${bold("AdsInAgents")} ${dim("\u2014 already set up, refreshed")}`);
|
|
5062
|
+
lines.push("");
|
|
5063
|
+
const labelW = maxLabelWidth(data.steps);
|
|
5064
|
+
for (const step of data.steps) {
|
|
5065
|
+
const icon = step.ok ? tick : warn;
|
|
5066
|
+
const label = step.label.padEnd(labelW);
|
|
5067
|
+
let detail = step.detail;
|
|
5068
|
+
if (step.label === "Device" && data.balanceCents !== void 0) {
|
|
5069
|
+
const dollars = `$${(data.balanceCents / 100).toFixed(2)}`;
|
|
5070
|
+
detail = `${detail} ${dot} balance ${green(dollars)}`;
|
|
5071
|
+
}
|
|
5072
|
+
lines.push(` ${icon} ${label} ${dot} ${detail}`);
|
|
5073
|
+
}
|
|
5074
|
+
lines.push("");
|
|
5075
|
+
lines.push(` ${dim("Nothing changed.")} Run ${bold("adsinagents doctor")} to check status.`);
|
|
5076
|
+
}
|
|
5077
|
+
return lines.join("\n") + "\n";
|
|
5078
|
+
}
|
|
5079
|
+
function maxLabelWidth(steps) {
|
|
5080
|
+
return steps.reduce((m, s) => Math.max(m, s.label.length), 0);
|
|
5081
|
+
}
|
|
5082
|
+
|
|
4876
5083
|
// ../cli/src/index.ts
|
|
4877
5084
|
var HERE2 = dirname4(fileURLToPath2(import.meta.url));
|
|
4878
5085
|
function readVersion() {
|
|
4879
5086
|
for (const rel of ["../../package.json", "../package.json"]) {
|
|
4880
5087
|
try {
|
|
4881
|
-
return JSON.parse(readFileSync4(
|
|
5088
|
+
return JSON.parse(readFileSync4(join6(HERE2, rel), "utf8")).version ?? "0.0.0";
|
|
4882
5089
|
} catch {
|
|
4883
5090
|
}
|
|
4884
5091
|
}
|
|
4885
5092
|
return "0.0.0";
|
|
4886
5093
|
}
|
|
4887
5094
|
var VERSION = readVersion();
|
|
4888
|
-
var DAEMON_ENTRY =
|
|
5095
|
+
var DAEMON_ENTRY = join6(HERE2, "..", "..", "daemon", "dist", "main.js");
|
|
4889
5096
|
function parseFlags(argv) {
|
|
4890
5097
|
const positional = [];
|
|
4891
5098
|
const flags = /* @__PURE__ */ new Map();
|
|
@@ -4901,8 +5108,8 @@ function parseFlags(argv) {
|
|
|
4901
5108
|
return { positional, flags };
|
|
4902
5109
|
}
|
|
4903
5110
|
function buildPlan(placement, spinnerVerbs) {
|
|
4904
|
-
const nativeStatusline =
|
|
4905
|
-
const statuslineCommand = existsSync5(nativeStatusline) ? nativeStatusline : `node ${
|
|
5111
|
+
const nativeStatusline = join6(dirname4(PATHS.frontmostBin), "adsinagents-statusline");
|
|
5112
|
+
const statuslineCommand = existsSync5(nativeStatusline) ? nativeStatusline : `node ${join6(HERE2, "..", "..", "statusline", "dist", "index.js")}`;
|
|
4906
5113
|
return {
|
|
4907
5114
|
statuslineCommand,
|
|
4908
5115
|
hookBaseUrl: `http://127.0.0.1:${HOOK_PORT}`,
|
|
@@ -4922,17 +5129,33 @@ async function cmdInit(flags) {
|
|
|
4922
5129
|
process.stdout.write(diff + "\n");
|
|
4923
5130
|
return;
|
|
4924
5131
|
}
|
|
5132
|
+
const firstRun = !isInstalled();
|
|
5133
|
+
const steps = [];
|
|
4925
5134
|
const native = buildAndInstallNative();
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
`
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
5135
|
+
steps.push({
|
|
5136
|
+
ok: native.ok,
|
|
5137
|
+
label: "Native helpers",
|
|
5138
|
+
detail: native.ok ? `${native.installed.length} installed` : `degraded mode`,
|
|
5139
|
+
note: native.ok ? void 0 : native.reason
|
|
5140
|
+
});
|
|
5141
|
+
const { backup, slashCommand } = applyMerge(plan);
|
|
5142
|
+
steps.push({
|
|
5143
|
+
ok: true,
|
|
5144
|
+
label: "Settings",
|
|
5145
|
+
detail: "merged",
|
|
5146
|
+
note: backup ? "backup saved" : void 0
|
|
5147
|
+
});
|
|
5148
|
+
steps.push({
|
|
5149
|
+
ok: slashCommand.ok,
|
|
5150
|
+
label: "Slash command",
|
|
5151
|
+
detail: slashCommand.ok ? "/adsinagents ready" : "not installed",
|
|
5152
|
+
note: slashCommand.ok ? void 0 : slashCommand.reason
|
|
5153
|
+
});
|
|
4934
5154
|
const cfg = loadConfigSafe();
|
|
4935
5155
|
let claimUrl = "";
|
|
5156
|
+
let deviceStepOk = true;
|
|
5157
|
+
let deviceDetail = "";
|
|
5158
|
+
let deviceNote;
|
|
4936
5159
|
if (!cfg.deviceToken) {
|
|
4937
5160
|
try {
|
|
4938
5161
|
const deviceId = `dev_${randomBytes(8).toString("hex")}`;
|
|
@@ -4945,36 +5168,43 @@ async function cmdInit(flags) {
|
|
|
4945
5168
|
const reg = await res.json();
|
|
4946
5169
|
writeConfigPatch({ deviceId: reg.deviceId, deviceToken: reg.deviceToken, claimToken: reg.claimToken });
|
|
4947
5170
|
claimUrl = reg.claimUrl;
|
|
4948
|
-
|
|
5171
|
+
deviceDetail = "registered, earning anonymously";
|
|
4949
5172
|
} catch (e) {
|
|
4950
|
-
|
|
4951
|
-
|
|
5173
|
+
deviceStepOk = false;
|
|
5174
|
+
deviceDetail = "registration skipped";
|
|
5175
|
+
deviceNote = e.message;
|
|
4952
5176
|
}
|
|
4953
5177
|
} else {
|
|
4954
|
-
|
|
5178
|
+
deviceDetail = "registered";
|
|
4955
5179
|
if (cfg.claimToken) claimUrl = `${cfg.serverUrl}/claim?t=${cfg.claimToken}`;
|
|
4956
5180
|
}
|
|
5181
|
+
steps.push({ ok: deviceStepOk, label: "Device", detail: deviceDetail, note: deviceNote });
|
|
4957
5182
|
const platform = getPlatform();
|
|
4958
5183
|
const nodeBin = process.execPath;
|
|
4959
|
-
|
|
4960
|
-
|
|
5184
|
+
let daemonOk = true;
|
|
5185
|
+
let daemonNote;
|
|
5186
|
+
try {
|
|
5187
|
+
await platform.installAutostart(nodeBin, [DAEMON_ENTRY]);
|
|
5188
|
+
} catch (e) {
|
|
5189
|
+
daemonOk = false;
|
|
5190
|
+
daemonNote = e.message;
|
|
5191
|
+
}
|
|
5192
|
+
steps.push({ ok: daemonOk, label: "Daemon", detail: daemonOk ? "running" : "not loaded", note: daemonNote });
|
|
4961
5193
|
if (flags.has("show-earnings")) {
|
|
4962
5194
|
writeConfigPatch({ showEarnings: flags.get("show-earnings") !== "false" });
|
|
4963
|
-
process.stdout.write("\u2713 live earnings segment enabled\n");
|
|
4964
5195
|
}
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
You're earning anonymously. To see earnings + get paid, claim this install:
|
|
4973
|
-
${claimUrl}
|
|
4974
|
-
(or run \`adsinagents claim\` anytime to reprint the link)
|
|
4975
|
-
`
|
|
4976
|
-
);
|
|
5196
|
+
let balanceCents;
|
|
5197
|
+
if (!firstRun && existsSync5(PATHS.daemonState)) {
|
|
5198
|
+
try {
|
|
5199
|
+
const state = JSON.parse(readFileSync4(PATHS.daemonState, "utf8"));
|
|
5200
|
+
if (typeof state.balanceCents === "number") balanceCents = state.balanceCents;
|
|
5201
|
+
} catch {
|
|
5202
|
+
}
|
|
4977
5203
|
}
|
|
5204
|
+
const summarySteps = firstRun ? steps : steps.filter((s) => s.label !== "Native helpers");
|
|
5205
|
+
process.stdout.write(
|
|
5206
|
+
renderInitSummary({ firstRun, steps: summarySteps, claimUrl: claimUrl || void 0, balanceCents })
|
|
5207
|
+
);
|
|
4978
5208
|
}
|
|
4979
5209
|
async function cmdRemove() {
|
|
4980
5210
|
const platform = getPlatform();
|
package/daemon/dist/main.js
CHANGED
|
@@ -4173,8 +4173,10 @@ var ConfigSchema = external_exports.object({
|
|
|
4173
4173
|
deviceId: external_exports.string().default(""),
|
|
4174
4174
|
/** Single-use token to claim this install at /claim (attach a login + payout). */
|
|
4175
4175
|
claimToken: external_exports.string().default(""),
|
|
4176
|
-
/** Ad cache poll interval (seconds).
|
|
4177
|
-
|
|
4176
|
+
/** Ad cache poll interval (seconds) — how often a new ad rotates in. Billing
|
|
4177
|
+
* is gated separately by rateCapSec, so a faster poll rotates creatives
|
|
4178
|
+
* without changing earnings. */
|
|
4179
|
+
pollIntervalSec: external_exports.number().int().positive().default(30),
|
|
4178
4180
|
/** Hard daily fired-impression cap. */
|
|
4179
4181
|
dailyCap: external_exports.number().int().positive().default(300),
|
|
4180
4182
|
/** Min seconds of verified view between two fired impressions. */
|
|
@@ -4550,8 +4552,8 @@ var ClaudeCodeAdapter = class {
|
|
|
4550
4552
|
else
|
|
4551
4553
|
priorValues.hooks = cur.hooks;
|
|
4552
4554
|
const mk = (path) => ({
|
|
4553
|
-
type: "
|
|
4554
|
-
|
|
4555
|
+
type: "command",
|
|
4556
|
+
command: hookCommand(path)
|
|
4555
4557
|
});
|
|
4556
4558
|
next.hooks = {
|
|
4557
4559
|
...hooks,
|
|
@@ -4576,8 +4578,8 @@ var ClaudeCodeAdapter = class {
|
|
|
4576
4578
|
const backup = this.backup();
|
|
4577
4579
|
const { next, diff } = this.merge(plan);
|
|
4578
4580
|
this.write(next);
|
|
4579
|
-
this.writeSlashCommand();
|
|
4580
|
-
return { backup, diff };
|
|
4581
|
+
const slashCommand = this.writeSlashCommand();
|
|
4582
|
+
return { backup, diff, slashCommand };
|
|
4581
4583
|
}
|
|
4582
4584
|
/** Path of the in-session settings command, e.g. ~/.claude/commands/adsinagents.md. */
|
|
4583
4585
|
slashCommandPath() {
|
|
@@ -4587,14 +4589,22 @@ var ClaudeCodeAdapter = class {
|
|
|
4587
4589
|
* Install /adsinagents — the status line itself is display-only, so this is
|
|
4588
4590
|
* the in-session settings surface: the user types /adsinagents and Claude
|
|
4589
4591
|
* drives the CLI. File is fully ours (sentinel comment), safe to delete on
|
|
4590
|
-
* remove.
|
|
4592
|
+
* remove. Reconciles: writes if missing or stale (content drift), skips if
|
|
4593
|
+
* already correct. Returns the outcome so init can REPORT failures instead of
|
|
4594
|
+
* silently swallowing them (the old try/catch hid a real install bug — the
|
|
4595
|
+
* file never wrote and doctor was the only thing that ever noticed).
|
|
4591
4596
|
*/
|
|
4592
4597
|
writeSlashCommand() {
|
|
4598
|
+
const p = this.slashCommandPath();
|
|
4593
4599
|
try {
|
|
4594
|
-
|
|
4600
|
+
if (existsSync2(p) && readFileSync2(p, "utf8") === SLASH_COMMAND_MD) {
|
|
4601
|
+
return { ok: true };
|
|
4602
|
+
}
|
|
4595
4603
|
mkdirSync2(dirname2(p), { recursive: true });
|
|
4596
4604
|
writeFileSync2(p, SLASH_COMMAND_MD);
|
|
4597
|
-
|
|
4605
|
+
return { ok: true };
|
|
4606
|
+
} catch (e) {
|
|
4607
|
+
return { ok: false, reason: e.message };
|
|
4598
4608
|
}
|
|
4599
4609
|
}
|
|
4600
4610
|
removeInstall() {
|
|
@@ -4724,10 +4734,18 @@ function spinnerVerbsFor(ad) {
|
|
|
4724
4734
|
`Brought to you by ${brand}`
|
|
4725
4735
|
];
|
|
4726
4736
|
}
|
|
4737
|
+
function hookCommand(path) {
|
|
4738
|
+
return [
|
|
4739
|
+
`p=$(node -e 'try{process.stdout.write(String(require(process.env.HOME+"/.adsinagents/daemon.json").port||""))}catch(e){}' 2>/dev/null)`,
|
|
4740
|
+
`[ -n "$p" ] && curl -s --max-time 2 -X POST "http://127.0.0.1:$p${path}" -H 'content-type: application/json' --data-binary @- >/dev/null 2>&1`,
|
|
4741
|
+
`true`
|
|
4742
|
+
].join("; ");
|
|
4743
|
+
}
|
|
4727
4744
|
function appendHook(existing, entry) {
|
|
4728
4745
|
const arr = Array.isArray(existing) ? existing.slice() : existing ? [existing] : [];
|
|
4729
|
-
arr.
|
|
4730
|
-
|
|
4746
|
+
const userOwned = arr.filter((e) => !(e && typeof e === "object" && e[ADLINE_SENTINEL]));
|
|
4747
|
+
userOwned.push({ hooks: [entry], [ADLINE_SENTINEL]: true });
|
|
4748
|
+
return userOwned;
|
|
4731
4749
|
}
|
|
4732
4750
|
function renderDiff(before, after) {
|
|
4733
4751
|
const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
@@ -5437,7 +5455,8 @@ async function main() {
|
|
|
5437
5455
|
if (tickCount % SYNC_EVERY_TICKS === 0) {
|
|
5438
5456
|
void syncLedger(ledger, cfg).then((e) => {
|
|
5439
5457
|
if (e) earnings = e;
|
|
5440
|
-
}).catch(() => {
|
|
5458
|
+
}).catch((err) => {
|
|
5459
|
+
void fileLog(`sync error: ${String(err)}`);
|
|
5441
5460
|
});
|
|
5442
5461
|
}
|
|
5443
5462
|
}, TICK_MS);
|
|
@@ -5466,9 +5485,17 @@ async function syncLedger(ledger, cfg) {
|
|
|
5466
5485
|
}),
|
|
5467
5486
|
signal: AbortSignal.timeout(8e3)
|
|
5468
5487
|
});
|
|
5469
|
-
if (!res.ok)
|
|
5488
|
+
if (!res.ok) {
|
|
5489
|
+
const detail = await res.text().catch(() => "");
|
|
5490
|
+
await fileLog(`sync HTTP ${res.status}: ${detail.slice(0, 200)}`);
|
|
5491
|
+
return null;
|
|
5492
|
+
}
|
|
5470
5493
|
if (rows.length) ledger.markSynced(rows.map((r) => r.id));
|
|
5471
5494
|
const body = await res.json().catch(() => ({}));
|
|
5495
|
+
if (body.rejected && body.rejected > 0) {
|
|
5496
|
+
const why = Object.entries(body.rejectReasons ?? {}).map(([r, n]) => `${r}=${n}`).join(" ");
|
|
5497
|
+
await fileLog(`sync rejected ${body.rejected}${why ? ` (${why})` : ""}`);
|
|
5498
|
+
}
|
|
5472
5499
|
return {
|
|
5473
5500
|
balanceCents: Math.max(0, body.balanceCents ?? 0),
|
|
5474
5501
|
todayCents: Math.max(0, body.todayCents ?? 0)
|
package/native/build.sh
CHANGED
|
@@ -27,4 +27,14 @@ swiftc -O "$HERE/statusline.swift" -o "$OUT/adsinagents-statusline"
|
|
|
27
27
|
echo "Compiling frontmost..." >&2
|
|
28
28
|
swiftc -O "$HERE/frontmost.swift" -o "$OUT/frontmost"
|
|
29
29
|
|
|
30
|
+
# Re-sign with ad-hoc signature. macOS 26 (Tahoe) Taskgated invalidates the
|
|
31
|
+
# linker-signed signature when the binary is copied across paths (copyFileSync /
|
|
32
|
+
# install step). A fresh codesign -f after compile ensures the installed copy
|
|
33
|
+
# passes Taskgated validation without needing a Developer ID.
|
|
34
|
+
if command -v codesign >/dev/null 2>&1; then
|
|
35
|
+
echo "Signing binaries (ad-hoc)..." >&2
|
|
36
|
+
codesign -f --sign - "$OUT/adsinagents-statusline" >/dev/null 2>&1 || true
|
|
37
|
+
codesign -f --sign - "$OUT/frontmost" >/dev/null 2>&1 || true
|
|
38
|
+
fi
|
|
39
|
+
|
|
30
40
|
echo "Built: $OUT/adsinagents-statusline, $OUT/frontmost" >&2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adsinagents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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",
|
package/statusline/dist/index.js
CHANGED
|
@@ -4150,8 +4150,10 @@ var ConfigSchema = external_exports.object({
|
|
|
4150
4150
|
deviceId: external_exports.string().default(""),
|
|
4151
4151
|
/** Single-use token to claim this install at /claim (attach a login + payout). */
|
|
4152
4152
|
claimToken: external_exports.string().default(""),
|
|
4153
|
-
/** Ad cache poll interval (seconds).
|
|
4154
|
-
|
|
4153
|
+
/** Ad cache poll interval (seconds) — how often a new ad rotates in. Billing
|
|
4154
|
+
* is gated separately by rateCapSec, so a faster poll rotates creatives
|
|
4155
|
+
* without changing earnings. */
|
|
4156
|
+
pollIntervalSec: external_exports.number().int().positive().default(30),
|
|
4155
4157
|
/** Hard daily fired-impression cap. */
|
|
4156
4158
|
dailyCap: external_exports.number().int().positive().default(300),
|
|
4157
4159
|
/** Min seconds of verified view between two fired impressions. */
|