trymeanwhile 1.0.0
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/meanwhile.js +109 -0
- package/package.json +26 -0
package/bin/meanwhile.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Node reimplementation of install.sh/install.ps1 -- one script instead of
|
|
3
|
+
// two, since Node itself is already cross-platform. Behavior is kept in
|
|
4
|
+
// lockstep with those scripts (same install dir, same settings.json shape,
|
|
5
|
+
// same non-fatal certifi step) so all three installers produce an
|
|
6
|
+
// identical result regardless of which one someone happens to run.
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const crypto = require("crypto");
|
|
12
|
+
const { spawnSync, exec } = require("child_process");
|
|
13
|
+
|
|
14
|
+
const SERVER = "https://trymeanwhile.online";
|
|
15
|
+
const INSTALL_DIR = path.join(os.homedir(), ".deadtime-client");
|
|
16
|
+
const STATE_DIR = path.join(os.homedir(), ".deadtime");
|
|
17
|
+
|
|
18
|
+
function log(msg) {
|
|
19
|
+
console.log(`meanwhile: ${msg}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function findPython() {
|
|
23
|
+
for (const candidate of ["python3", "python", "py"]) {
|
|
24
|
+
const result = spawnSync(candidate, ["--version"], { stdio: "ignore" });
|
|
25
|
+
if (result.status === 0) return candidate;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function openUrl(url) {
|
|
31
|
+
const cmd =
|
|
32
|
+
process.platform === "darwin" ? `open "${url}"`
|
|
33
|
+
: process.platform === "win32" ? `start "" "${url}"`
|
|
34
|
+
: `xdg-open "${url}"`;
|
|
35
|
+
exec(cmd, () => {});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
log(`installing to ${INSTALL_DIR}`);
|
|
40
|
+
fs.mkdirSync(INSTALL_DIR, { recursive: true });
|
|
41
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
42
|
+
|
|
43
|
+
// Real install ID, generated on the device the moment an install
|
|
44
|
+
// actually happens -- not guessed ahead of time by the website. Only
|
|
45
|
+
// if one doesn't already exist, so re-running this never clobbers an
|
|
46
|
+
// existing install's history with a fresh random ID.
|
|
47
|
+
const idPath = path.join(STATE_DIR, "install_id");
|
|
48
|
+
if (!fs.existsSync(idPath)) {
|
|
49
|
+
fs.writeFileSync(idPath, crypto.randomUUID(), { mode: 0o600 });
|
|
50
|
+
}
|
|
51
|
+
const installId = fs.readFileSync(idPath, "utf8").trim();
|
|
52
|
+
|
|
53
|
+
const res = await fetch(`${SERVER}/statusline.py`);
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
console.error(`meanwhile: couldn't download statusline.py (${res.status}) -- try again in a moment.`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
fs.writeFileSync(path.join(INSTALL_DIR, "statusline.py"), await res.text());
|
|
59
|
+
|
|
60
|
+
const python = findPython();
|
|
61
|
+
if (!python) {
|
|
62
|
+
log("couldn't find Python on your PATH.");
|
|
63
|
+
log("install it from https://python.org, then run `npx trymeanwhile` again.");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// certifi is a nice-to-have, not a hard requirement -- statusline.py
|
|
68
|
+
// falls back to the system's own certificate store if it's missing.
|
|
69
|
+
// Never let a failed pip install here take down the rest of the setup.
|
|
70
|
+
const hasCertifi = spawnSync(python, ["-c", "import certifi"], { stdio: "ignore" }).status === 0;
|
|
71
|
+
if (!hasCertifi) {
|
|
72
|
+
log("installing certifi (helps with HTTPS, not required)...");
|
|
73
|
+
const pipAttempts = [
|
|
74
|
+
["-m", "pip", "install", "--quiet", "certifi"],
|
|
75
|
+
["-m", "pip", "install", "--quiet", "--user", "certifi"],
|
|
76
|
+
["-m", "pip", "install", "--quiet", "--break-system-packages", "certifi"],
|
|
77
|
+
];
|
|
78
|
+
const installed = pipAttempts.some(
|
|
79
|
+
(args) => spawnSync(python, args, { stdio: "ignore" }).status === 0
|
|
80
|
+
);
|
|
81
|
+
if (!installed) log("couldn't install certifi, continuing without it (statusline.py falls back automatically)");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const scriptPath = path.join(INSTALL_DIR, "statusline.py");
|
|
85
|
+
const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
86
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
87
|
+
const settings = fs.existsSync(settingsPath) ? JSON.parse(fs.readFileSync(settingsPath, "utf8") || "{}") : {};
|
|
88
|
+
settings.statusLine = { type: "command", command: `${python} "${scriptPath}"` };
|
|
89
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
90
|
+
log(`wired into ${settingsPath}`);
|
|
91
|
+
|
|
92
|
+
log("installed. Restart Claude Code (close and reopen your terminal) to see it live.");
|
|
93
|
+
log("to check earnings or register a payout email later, run:");
|
|
94
|
+
log(` ${python} "${scriptPath}" --claim`);
|
|
95
|
+
|
|
96
|
+
// Open the browser straight to the claim page -- same pattern as
|
|
97
|
+
// `gh auth login` / `vercel login` / `wrangler login`. This is the only
|
|
98
|
+
// honest way for the site to know an install actually happened: it
|
|
99
|
+
// fires because this really did just write a real install ID, not
|
|
100
|
+
// because someone merely copied a command.
|
|
101
|
+
const claimUrl = `${SERVER}/claim.html?id=${installId}`;
|
|
102
|
+
openUrl(claimUrl);
|
|
103
|
+
log(`if a browser tab didn't open: ${claimUrl}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
main().catch((err) => {
|
|
107
|
+
console.error(`meanwhile: install failed -- ${err.message}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "trymeanwhile",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Get paid while your AI coding agent thinks. Wires a disclosed status line into Claude Code / Copilot CLI.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"meanwhile": "bin/meanwhile.js"
|
|
7
|
+
},
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=18"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://trymeanwhile.online",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/heenatrivedi321-max/deadtime.git"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"claude-code",
|
|
19
|
+
"copilot-cli",
|
|
20
|
+
"status-line",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"bin"
|
|
25
|
+
]
|
|
26
|
+
}
|