trymeanwhile 1.2.1 → 1.3.1

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/bin/meanwhile.js +90 -10
  2. package/package.json +1 -1
package/bin/meanwhile.js CHANGED
@@ -15,10 +15,22 @@ const SERVER = "https://trymeanwhile.online";
15
15
  const INSTALL_DIR = path.join(os.homedir(), ".deadtime-client");
16
16
  const STATE_DIR = path.join(os.homedir(), ".deadtime");
17
17
 
18
+ const VERBOSE = process.argv.includes("--verbose");
19
+
18
20
  function log(msg) {
19
21
  console.log(`meanwhile: ${msg}`);
20
22
  }
21
23
 
24
+ // Wiring detail nobody needs to see on a normal run -- which settings.json,
25
+ // which script path, that kind of thing. Real information, just not the
26
+ // first-run experience: it used to be seven identical-looking lines with no
27
+ // hierarchy, so the one line that actually mattered (restart your tool)
28
+ // read the same as housekeeping noise. Still here for anyone debugging why
29
+ // it didn't wire correctly -- just behind --verbose instead of always on.
30
+ function vlog(msg) {
31
+ if (VERBOSE) log(msg);
32
+ }
33
+
22
34
  function commandExists(cmd) {
23
35
  return spawnSync(cmd, ["--version"], { stdio: "ignore" }).status === 0;
24
36
  }
@@ -37,12 +49,12 @@ async function downloadTo(remoteName, localPath) {
37
49
  fs.writeFileSync(localPath, await res.text());
38
50
  }
39
51
 
40
- function wireSettings(settingsPath, runtime, scriptPath) {
52
+ function wireSettings(settingsPath, runtime, scriptPath, extra = {}) {
41
53
  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
42
54
  const settings = fs.existsSync(settingsPath) ? JSON.parse(fs.readFileSync(settingsPath, "utf8") || "{}") : {};
43
- settings.statusLine = { type: "command", command: `"${runtime}" "${scriptPath}"` };
55
+ settings.statusLine = { type: "command", command: `"${runtime}" "${scriptPath}"`, ...extra };
44
56
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
45
- log(`wired into ${settingsPath}`);
57
+ vlog(`wired into ${settingsPath}`);
46
58
  }
47
59
 
48
60
  // Wraps any long-running shell command (npm install, docker build, terraform
@@ -90,13 +102,49 @@ async function wrapCommand(args) {
90
102
  });
91
103
  }
92
104
 
105
+ // `npx trymeanwhile claim` -- the whole point of this is that nobody
106
+ // should ever need to remember (or read) a raw node/path/--claim
107
+ // incantation just to check what they've earned. Same install ID file,
108
+ // same /earnings endpoint statusline.js already hits, just reachable
109
+ // through the one command name people already typed once.
110
+ async function claimCommand() {
111
+ const idPath = path.join(STATE_DIR, "install_id");
112
+ if (!fs.existsSync(idPath)) {
113
+ log("nothing to claim yet -- you haven't installed. run `npx trymeanwhile` first.");
114
+ process.exit(1);
115
+ }
116
+ const installId = fs.readFileSync(idPath, "utf8").trim();
117
+ const claimUrl = `${SERVER}/claim?id=${installId}`;
118
+
119
+ console.log("meanwhile -- your account");
120
+ console.log(` ID: ${installId}`);
121
+ try {
122
+ const res = await fetch(`${SERVER}/earnings?id=${installId}`);
123
+ if (res.ok) {
124
+ const earnings = await res.json();
125
+ console.log(` earned: $${Number(earnings.user_earnings).toFixed(2)}`);
126
+ console.log(` shown: ${earnings.total_calls} lines (${earnings.sponsor_calls} sponsored)`);
127
+ } else {
128
+ console.log(" earned: (couldn't reach server -- check your connection)");
129
+ }
130
+ } catch {
131
+ console.log(" earned: (couldn't reach server -- check your connection)");
132
+ }
133
+ console.log();
134
+ console.log(` register a payout email: ${claimUrl}`);
135
+ }
136
+
93
137
  async function main() {
94
138
  if (process.argv[2] === "wrap") {
95
139
  await wrapCommand(process.argv.slice(3));
96
140
  return;
97
141
  }
142
+ if (process.argv[2] === "claim") {
143
+ await claimCommand();
144
+ return;
145
+ }
98
146
 
99
- log(`installing to ${INSTALL_DIR}`);
147
+ vlog(`installing to ${INSTALL_DIR}`);
100
148
  fs.mkdirSync(INSTALL_DIR, { recursive: true });
101
149
  fs.mkdirSync(STATE_DIR, { recursive: true });
102
150
 
@@ -128,18 +176,50 @@ async function main() {
128
176
 
129
177
  const claudeScript = path.join(INSTALL_DIR, "statusline.js");
130
178
  await downloadTo("statusline.js", claudeScript);
131
- wireSettings(path.join(os.homedir(), ".claude", "settings.json"), node, claudeScript);
179
+ wireSettings(path.join(os.homedir(), ".claude", "settings.json"), node, claudeScript, { refreshInterval: 10 });
132
180
 
133
181
  if (wireCopilot) {
134
182
  const copilotScript = path.join(INSTALL_DIR, "copilot_statusline.js");
135
183
  await downloadTo("copilot_statusline.js", copilotScript);
136
184
  wireSettings(path.join(os.homedir(), ".copilot", "settings.json"), node, copilotScript);
137
- log("Copilot CLI detected -- wired that too, same install ID, earnings share one balance.");
185
+ vlog("Copilot CLI detected -- wired that too, same install ID, earnings share one balance.");
138
186
  }
139
187
 
140
- log("installed. Restart Claude Code (and Copilot CLI, if wired) to see it live.");
141
- log("to check earnings or register a payout email later, run:");
142
- log(` "${node}" "${claudeScript}" --claim`);
188
+ // Prove the connection actually works right now, before asking anyone
189
+ // to trust a restart they can't yet see the result of. A skeptical
190
+ // first-time installer shouldn't have to take "it'll work after you
191
+ // restart" on faith -- this is the exact same /line call the real
192
+ // client makes, just fired once, synchronously, during install.
193
+ // Non-fatal if it fails (slow network, offline install) -- the real
194
+ // client will pick it up fine once the tool actually restarts.
195
+ let sampleLine = null;
196
+ try {
197
+ const params = new URLSearchParams({ id: installId, event: "install_check", sid: "", cost: "", tok: "", cwd: "" });
198
+ const res = await fetch(`${SERVER}/line?${params}`, { signal: AbortSignal.timeout(5000) });
199
+ if (res.ok) {
200
+ const data = await res.json();
201
+ if (data.line) sampleLine = data.line;
202
+ }
203
+ } catch {}
204
+
205
+ // The one-time summary a real person actually reads. This used to be
206
+ // seven identically-styled lines -- the one thing that actually matters
207
+ // (restart your tool) read no differently than housekeeping. Blank
208
+ // lines and indentation do the hierarchy work no --verbose flag can.
209
+ console.log("meanwhile: installed. congratulations, you now get paid to wait.");
210
+ console.log();
211
+ if (sampleLine) {
212
+ console.log(` just tested the connection -- here's a real line: "${sampleLine}"`);
213
+ console.log();
214
+ }
215
+ console.log(" restart Claude Code to see it live");
216
+ console.log(" (the one step between you and money -- yes, actually do it)");
217
+ console.log();
218
+ if (wireCopilot) {
219
+ console.log("Copilot CLI's in on this too, if it's here. Same balance either way.");
220
+ console.log();
221
+ }
222
+ console.log("Check what you've earned: npx trymeanwhile claim");
143
223
 
144
224
  // Open the browser straight to the claim page -- same pattern as
145
225
  // `gh auth login` / `vercel login` / `wrangler login`. This is the only
@@ -148,7 +228,7 @@ async function main() {
148
228
  // because someone merely copied a command.
149
229
  const claimUrl = `${SERVER}/claim.html?id=${installId}`;
150
230
  openUrl(claimUrl);
151
- log(`if a browser tab didn't open: ${claimUrl}`);
231
+ console.log(`(if a browser tab didn't open: ${claimUrl})`);
152
232
  }
153
233
 
154
234
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trymeanwhile",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "Get paid while your AI coding agent thinks. Wires a disclosed status line into Claude Code / Copilot CLI.",
5
5
  "bin": {
6
6
  "meanwhile": "bin/meanwhile.js"