@timecell/cli 0.1.0 → 0.1.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.
- package/dist/index.js +29 -15
- package/dist/index.js.map +1 -1
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +38 -22
- package/dist/wizard.js.map +1 -1
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -2,30 +2,44 @@
|
|
|
2
2
|
import { buildServer } from "@timecell/api";
|
|
3
3
|
import open from "open";
|
|
4
4
|
import { runWizard } from "./wizard.js";
|
|
5
|
-
const
|
|
5
|
+
const DEFAULT_PORT = 3737;
|
|
6
|
+
const MAX_PORT_ATTEMPTS = 10;
|
|
6
7
|
function printBanner() {
|
|
7
8
|
console.log("─────────────────────────────────────────────");
|
|
8
9
|
console.log(" TimeCell v0.1.0 — Crash Survival Calculator");
|
|
9
10
|
console.log("─────────────────────────────────────────────");
|
|
10
11
|
}
|
|
12
|
+
async function findAvailablePort(server, startPort) {
|
|
13
|
+
for (let port = startPort; port < startPort + MAX_PORT_ATTEMPTS; port++) {
|
|
14
|
+
try {
|
|
15
|
+
await server.listen({ port, host: "0.0.0.0" });
|
|
16
|
+
return port;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err.code === "EADDRINUSE") {
|
|
20
|
+
if (port === startPort) {
|
|
21
|
+
console.log(` Port ${port} in use, trying next...`);
|
|
22
|
+
}
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`No available port found (tried ${startPort}-${startPort + MAX_PORT_ATTEMPTS - 1})`);
|
|
29
|
+
}
|
|
11
30
|
async function main() {
|
|
12
31
|
printBanner();
|
|
13
32
|
await runWizard();
|
|
14
|
-
console.log("\nStarting TimeCell
|
|
33
|
+
console.log("\nStarting TimeCell server...");
|
|
15
34
|
const server = await buildServer();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
console.log(`\nTimeCell is running at ${APP_URL}`);
|
|
24
|
-
console.log("Opening browser...\n");
|
|
25
|
-
await open(APP_URL);
|
|
26
|
-
console.log("Press Ctrl+C to stop.\n");
|
|
35
|
+
const port = await findAvailablePort(server, DEFAULT_PORT);
|
|
36
|
+
const url = `http://localhost:${port}`;
|
|
37
|
+
console.log(`\n TimeCell is running at ${url}`);
|
|
38
|
+
console.log(" Opening browser...\n");
|
|
39
|
+
await open(url);
|
|
40
|
+
console.log(" Press Ctrl+C to stop.\n");
|
|
27
41
|
const shutdown = async () => {
|
|
28
|
-
console.log("\
|
|
42
|
+
console.log("\n Shutting down TimeCell...");
|
|
29
43
|
await server.close();
|
|
30
44
|
process.exit(0);
|
|
31
45
|
};
|
|
@@ -33,7 +47,7 @@ async function main() {
|
|
|
33
47
|
process.on("SIGTERM", shutdown);
|
|
34
48
|
}
|
|
35
49
|
main().catch((err) => {
|
|
36
|
-
console.error("
|
|
50
|
+
console.error("Error:", err.message || err);
|
|
37
51
|
process.exit(1);
|
|
38
52
|
});
|
|
39
53
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,SAAS,WAAW;IAClB,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;AAC/D,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,MAAW,EAAE,SAAiB;IAC7D,KAAK,IAAI,IAAI,GAAG,SAAS,EAAE,IAAI,GAAG,SAAS,GAAG,iBAAiB,EAAE,IAAI,EAAE,EAAE,CAAC;QACxE,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,yBAAyB,CAAC,CAAC;gBACvD,CAAC;gBACD,SAAS;YACX,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,kCAAkC,SAAS,IAAI,SAAS,GAAG,iBAAiB,GAAG,CAAC,GAAG,CAAC,CAAC;AACvG,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,WAAW,EAAE,CAAC;IAEd,MAAM,SAAS,EAAE,CAAC;IAElB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAG,MAAM,WAAW,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,oBAAoB,IAAI,EAAE,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAEtC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAEhB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAEzC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/wizard.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wizard.d.ts","sourceRoot":"","sources":["../src/wizard.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAKvD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,cAAc,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"wizard.d.ts","sourceRoot":"","sources":["../src/wizard.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAKvD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,cAAc,CAAC;CAC3B;AAiCD,wBAAsB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,CAgCvD"}
|
package/dist/wizard.js
CHANGED
|
@@ -1,42 +1,58 @@
|
|
|
1
|
-
import { number } from "@inquirer/prompts";
|
|
1
|
+
import { input, number } from "@inquirer/prompts";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
const CONFIG_DIR = join(homedir(), ".timecell");
|
|
6
6
|
const PORTFOLIO_PATH = join(CONFIG_DIR, "portfolio.json");
|
|
7
|
+
function parseNumber(raw) {
|
|
8
|
+
// Strip commas, currency symbols, whitespace
|
|
9
|
+
const cleaned = raw.replace(/[$,\s]/g, "");
|
|
10
|
+
const num = Number(cleaned);
|
|
11
|
+
return Number.isNaN(num) ? undefined : num;
|
|
12
|
+
}
|
|
13
|
+
async function askUsd(message, defaultVal) {
|
|
14
|
+
const formatted = defaultVal.toLocaleString("en-US");
|
|
15
|
+
const raw = await input({
|
|
16
|
+
message,
|
|
17
|
+
default: formatted,
|
|
18
|
+
validate: (v) => {
|
|
19
|
+
const num = parseNumber(v);
|
|
20
|
+
if (num === undefined || num <= 0)
|
|
21
|
+
return "Enter a positive number (commas OK, e.g. 5,000,000)";
|
|
22
|
+
return true;
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return parseNumber(raw);
|
|
26
|
+
}
|
|
27
|
+
async function fetchBtcPrice() {
|
|
28
|
+
try {
|
|
29
|
+
const res = await fetch("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd");
|
|
30
|
+
const data = await res.json();
|
|
31
|
+
return Math.round(data.bitcoin.usd);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return 84_000; // fallback
|
|
35
|
+
}
|
|
36
|
+
}
|
|
7
37
|
export async function runWizard() {
|
|
8
38
|
console.log("\nLet's configure your portfolio.\n");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
39
|
+
console.log(" All values in USD. Your data stays local (~/.timecell/).\n");
|
|
40
|
+
const btcPrice = await fetchBtcPrice();
|
|
41
|
+
console.log(` Current BTC price: $${btcPrice.toLocaleString("en-US")} (live)\n`);
|
|
42
|
+
const totalValueUsd = await askUsd("Total portfolio value (USD):", 5_000_000);
|
|
14
43
|
const btcPercentage = await number({
|
|
15
44
|
message: "Bitcoin allocation (%):",
|
|
16
45
|
default: 35,
|
|
17
46
|
validate: (v) => (v !== undefined && v >= 0 && v <= 100 ? true : "Must be between 0 and 100"),
|
|
18
47
|
});
|
|
19
|
-
const monthlyBurnUsd = await
|
|
20
|
-
|
|
21
|
-
default: 25_000,
|
|
22
|
-
validate: (v) => (v !== undefined && v > 0 ? true : "Must be a positive number"),
|
|
23
|
-
});
|
|
24
|
-
const liquidReserveUsd = await number({
|
|
25
|
-
message: "Liquid reserve (USD):",
|
|
26
|
-
default: 600_000,
|
|
27
|
-
validate: (v) => (v !== undefined && v >= 0 ? true : "Must be a non-negative number"),
|
|
28
|
-
});
|
|
29
|
-
const btcPriceUsd = await number({
|
|
30
|
-
message: "Current BTC price (USD):",
|
|
31
|
-
default: 84_000,
|
|
32
|
-
validate: (v) => (v !== undefined && v > 0 ? true : "Must be a positive number"),
|
|
33
|
-
});
|
|
48
|
+
const monthlyBurnUsd = await askUsd("Monthly burn rate (USD):", 25_000);
|
|
49
|
+
const liquidReserveUsd = await askUsd("Liquid reserve (USD):", 600_000);
|
|
34
50
|
const portfolio = {
|
|
35
51
|
totalValueUsd,
|
|
36
52
|
btcPercentage,
|
|
37
53
|
monthlyBurnUsd,
|
|
38
54
|
liquidReserveUsd,
|
|
39
|
-
btcPriceUsd,
|
|
55
|
+
btcPriceUsd: btcPrice,
|
|
40
56
|
};
|
|
41
57
|
await mkdir(CONFIG_DIR, { recursive: true });
|
|
42
58
|
await writeFile(PORTFOLIO_PATH, JSON.stringify({ portfolio }, null, 2));
|
package/dist/wizard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wizard.js","sourceRoot":"","sources":["../src/wizard.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"wizard.js","sourceRoot":"","sources":["../src/wizard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAChD,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;AAM1D,SAAS,WAAW,CAAC,GAAW;IAC9B,6CAA6C;IAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,OAAe,EAAE,UAAkB;IACvD,MAAM,SAAS,GAAG,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC;QACtB,OAAO;QACP,OAAO,EAAE,SAAS;QAClB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YACd,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC3B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,IAAI,CAAC;gBAAE,OAAO,qDAAqD,CAAC;YAChG,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC,CAAC;IACH,OAAO,WAAW,CAAC,GAAG,CAAE,CAAC;AAC3B,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,6EAA6E,CAAC,CAAC;QACvG,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,CAAC,WAAW;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAE5E,MAAM,QAAQ,GAAG,MAAM,aAAa,EAAE,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,yBAAyB,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAElF,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,8BAA8B,EAAE,SAAS,CAAC,CAAC;IAE9E,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC;QACjC,OAAO,EAAE,yBAAyB;QAClC,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,2BAA2B,CAAC;KAC9F,CAAW,CAAC;IAEb,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC;IAExE,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;IAExE,MAAM,SAAS,GAAmB;QAChC,aAAa;QACb,aAAa;QACb,cAAc;QACd,gBAAgB;QAChB,WAAW,EAAE,QAAQ;KACtB,CAAC;IAEF,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,SAAS,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,wBAAwB,cAAc,EAAE,CAAC,CAAC;IAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timecell/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "TimeCell CLI — Crash Survival Calculator",
|
|
6
6
|
"bin": {
|
|
7
7
|
"timecell": "dist/index.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
10
12
|
"publishConfig": {
|
|
11
13
|
"access": "public"
|
|
12
14
|
},
|
|
@@ -16,8 +18,8 @@
|
|
|
16
18
|
},
|
|
17
19
|
"dependencies": {
|
|
18
20
|
"@inquirer/prompts": "^7",
|
|
19
|
-
"@timecell/api": "0.1.
|
|
20
|
-
"@timecell/engine": "0.1.
|
|
21
|
+
"@timecell/api": "0.1.1",
|
|
22
|
+
"@timecell/engine": "0.1.1",
|
|
21
23
|
"open": "^10"
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|