@marina-cloud/cli 0.0.1 → 0.0.2

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/dist/marina.mjs +88 -59
  2. package/package.json +1 -1
package/dist/marina.mjs CHANGED
@@ -75,6 +75,7 @@ function writeLink(dir, link) {
75
75
  }
76
76
 
77
77
  // src/api.ts
78
+ var LOGIN_EXCHANGE_TIMEOUT_MS = 15e3;
78
79
  var ApiError = class extends Error {
79
80
  code;
80
81
  status;
@@ -102,11 +103,24 @@ async function request(path, init) {
102
103
  return body;
103
104
  }
104
105
  async function exchangeCliLogin(code, codeVerifier) {
105
- const res = await fetch(`${apiUrl()}/cli/auth/exchange`, {
106
- method: "POST",
107
- headers: { "content-type": "application/json" },
108
- body: JSON.stringify({ code, code_verifier: codeVerifier })
109
- });
106
+ let res;
107
+ try {
108
+ res = await fetch(`${apiUrl()}/cli/auth/exchange`, {
109
+ method: "POST",
110
+ headers: { "content-type": "application/json" },
111
+ body: JSON.stringify({ code, code_verifier: codeVerifier }),
112
+ signal: AbortSignal.timeout(LOGIN_EXCHANGE_TIMEOUT_MS)
113
+ });
114
+ } catch (error) {
115
+ if (error.name === "TimeoutError") {
116
+ throw new ApiError(
117
+ "login_timeout",
118
+ "the login confirmation request timed out after 15 seconds \u2014 run `marina setup` to try again",
119
+ 408
120
+ );
121
+ }
122
+ throw error;
123
+ }
110
124
  const body = await res.json().catch(() => ({}));
111
125
  if (!res.ok || !body.token) {
112
126
  throw new ApiError(
@@ -1414,22 +1428,53 @@ import { spawn } from "node:child_process";
1414
1428
  import { createServer } from "node:http";
1415
1429
  import { hostname } from "node:os";
1416
1430
  var LOGIN_TIMEOUT_MS = 5 * 6e4;
1431
+ var PROGRESS_INTERVAL_MS = 1e3;
1417
1432
  var page = (title, message) => `<!doctype html>
1418
1433
  <html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width">
1419
1434
  <title>${title}</title><style>
1420
1435
  body{margin:0;display:grid;place-items:center;min-height:100vh;font:15px/1.5 system-ui,sans-serif;color:#142238;background:#faf9f7}
1421
1436
  main{text-align:center;padding:32px}h1{font-size:22px;margin:0 0 6px}p{margin:0;color:#667085}
1422
1437
  </style></head><body><main><h1>${title}</h1><p>${message}</p></main></body></html>`;
1423
- var closeServer = (server) => new Promise((resolve2) => server.close(() => resolve2()));
1438
+ var closeLoginServer = (server) => new Promise((resolve2) => {
1439
+ server.close(() => resolve2());
1440
+ server.closeAllConnections();
1441
+ });
1424
1442
  function openBrowser(url) {
1425
1443
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
1426
1444
  const child = spawn(command, [url], { detached: true, stdio: "ignore" });
1427
1445
  child.on("error", () => void 0);
1428
1446
  child.unref();
1429
1447
  }
1430
- function callbackCode(server, expectedState) {
1448
+ function timeoutLabel(timeoutMs) {
1449
+ if (timeoutMs % 6e4 === 0) {
1450
+ const minutes = timeoutMs / 6e4;
1451
+ return `${String(minutes)} minute${minutes === 1 ? "" : "s"}`;
1452
+ }
1453
+ const seconds = Math.ceil(timeoutMs / 1e3);
1454
+ return `${String(seconds)} second${seconds === 1 ? "" : "s"}`;
1455
+ }
1456
+ function waitForLoginCallback(server, expectedState, onProgress, timeoutMs = LOGIN_TIMEOUT_MS) {
1431
1457
  return new Promise((resolve2, reject) => {
1432
- const timer = setTimeout(() => reject(new Error("browser login timed out")), LOGIN_TIMEOUT_MS);
1458
+ const startedAt = Date.now();
1459
+ const waiting = () => {
1460
+ const elapsedMs = Date.now() - startedAt;
1461
+ onProgress({
1462
+ phase: "waiting",
1463
+ elapsedMs,
1464
+ remainingMs: Math.max(0, timeoutMs - elapsedMs)
1465
+ });
1466
+ };
1467
+ waiting();
1468
+ const progress = setInterval(waiting, PROGRESS_INTERVAL_MS);
1469
+ progress.unref();
1470
+ const timer = setTimeout(() => {
1471
+ clearInterval(progress);
1472
+ reject(
1473
+ new Error(
1474
+ `browser login timed out after ${timeoutLabel(timeoutMs)} \u2014 run \`marina setup\` to try again`
1475
+ )
1476
+ );
1477
+ }, timeoutMs);
1433
1478
  timer.unref();
1434
1479
  server.on("request", (request2, response) => {
1435
1480
  const url = new URL(request2.url ?? "/", "http://127.0.0.1");
@@ -1439,6 +1484,7 @@ function callbackCode(server, expectedState) {
1439
1484
  }
1440
1485
  response.setHeader("cache-control", "no-store");
1441
1486
  response.setHeader("content-type", "text/html; charset=utf-8");
1487
+ response.setHeader("connection", "close");
1442
1488
  const state = url.searchParams.get("state");
1443
1489
  const code = url.searchParams.get("code");
1444
1490
  const error = url.searchParams.get("error");
@@ -1448,6 +1494,7 @@ function callbackCode(server, expectedState) {
1448
1494
  return;
1449
1495
  }
1450
1496
  clearTimeout(timer);
1497
+ clearInterval(progress);
1451
1498
  if (error || !code) {
1452
1499
  response.writeHead(400);
1453
1500
  response.end(
@@ -1460,6 +1507,7 @@ function callbackCode(server, expectedState) {
1460
1507
  );
1461
1508
  return;
1462
1509
  }
1510
+ onProgress({ phase: "received" });
1463
1511
  response.writeHead(200);
1464
1512
  response.end(
1465
1513
  page("Marina CLI is signed in", "You can close this window and return to the terminal."),
@@ -1468,7 +1516,7 @@ function callbackCode(server, expectedState) {
1468
1516
  });
1469
1517
  });
1470
1518
  }
1471
- async function loginWithBrowser(onOpen) {
1519
+ async function loginWithBrowser(onOpen, onProgress = () => void 0) {
1472
1520
  const verifier = randomBytes(32).toString("base64url");
1473
1521
  const challenge = createHash("sha256").update(verifier).digest("base64url");
1474
1522
  const state = randomBytes(32).toString("base64url");
@@ -1489,13 +1537,13 @@ async function loginWithBrowser(onOpen) {
1489
1537
  "device_name",
1490
1538
  `Marina CLI on ${hostname().split(".")[0]}`.slice(0, 80)
1491
1539
  );
1492
- const codePromise = callbackCode(server, state);
1540
+ const codePromise = waitForLoginCallback(server, state, onProgress);
1493
1541
  onOpen(authorize.toString());
1494
1542
  openBrowser(authorize.toString());
1495
1543
  const code = await codePromise;
1496
1544
  return await exchangeCliLogin(code, verifier);
1497
1545
  } finally {
1498
- await closeServer(server);
1546
+ await closeLoginServer(server);
1499
1547
  }
1500
1548
  }
1501
1549
 
@@ -1690,21 +1738,6 @@ function install(target, update) {
1690
1738
  status: existing === null ? "installed" : "updated"
1691
1739
  };
1692
1740
  }
1693
- function autoInstallSkills() {
1694
- const detectedTargets = targets();
1695
- const installed = [];
1696
- const updatesAvailable = [];
1697
- for (const target of detectedTargets) {
1698
- const result2 = install(target, false);
1699
- if (result2) installed.push(result2);
1700
- else updatesAvailable.push(target.agent);
1701
- }
1702
- return {
1703
- detected: detectedTargets.map((target) => target.agent),
1704
- installed,
1705
- updatesAvailable
1706
- };
1707
- }
1708
1741
  function installSkills(agent) {
1709
1742
  const selected = targets(agent);
1710
1743
  if (selected.length === 0) {
@@ -1716,7 +1749,7 @@ function installSkills(agent) {
1716
1749
  // package.json
1717
1750
  var package_default = {
1718
1751
  name: "@marina-cloud/cli",
1719
- version: "0.0.1",
1752
+ version: "0.0.2",
1720
1753
  description: "Command-line client for Marina Cloud",
1721
1754
  homepage: "https://github.com/marina-hq/marina#readme",
1722
1755
  bugs: {
@@ -2061,27 +2094,6 @@ async function main() {
2061
2094
  return;
2062
2095
  }
2063
2096
  shouldCheckForUpdates = true;
2064
- let skillBootstrap = null;
2065
- if (command !== "skills") {
2066
- try {
2067
- skillBootstrap = autoInstallSkills();
2068
- for (const installed of skillBootstrap.installed) {
2069
- if (installed.status === "installed") {
2070
- say(dim(`installed Marina skill for ${installed.agent}`));
2071
- }
2072
- }
2073
- if (skillBootstrap.updatesAvailable.length > 0) {
2074
- const agent = skillBootstrap.updatesAvailable.length > 1 ? "all" : skillBootstrap.updatesAvailable[0];
2075
- say(
2076
- dim(
2077
- `Marina skill update available \u2014 run marina skills install --agent ${agent ?? "all"}`
2078
- )
2079
- );
2080
- }
2081
- } catch {
2082
- say(dim("could not install the Marina agent skill; run marina skills install later"));
2083
- }
2084
- }
2085
2097
  switch (command) {
2086
2098
  case "setup":
2087
2099
  case "login": {
@@ -2089,24 +2101,41 @@ async function main() {
2089
2101
  if (token) {
2090
2102
  saveToken(token);
2091
2103
  } else {
2092
- const signedIn = await loginWithBrowser((url) => {
2093
- say("Opening your browser to sign in\u2026");
2094
- say(dim(url));
2095
- });
2104
+ let lastReportedRemaining = -1;
2105
+ const signedIn = await loginWithBrowser(
2106
+ (url) => {
2107
+ say("Opening your browser to sign in\u2026");
2108
+ say(dim(url));
2109
+ },
2110
+ (progress) => {
2111
+ if (progress.phase === "waiting") {
2112
+ const remainingSeconds = Math.ceil(progress.remainingMs / 1e3);
2113
+ if (lastReportedRemaining < 0) {
2114
+ say(
2115
+ dim(
2116
+ `Waiting for browser authorization (${String(Math.ceil(remainingSeconds / 60))} minute timeout)\u2026`
2117
+ )
2118
+ );
2119
+ lastReportedRemaining = remainingSeconds;
2120
+ } else if (remainingSeconds <= lastReportedRemaining - 15) {
2121
+ const minutes = Math.floor(remainingSeconds / 60);
2122
+ const seconds = String(remainingSeconds % 60).padStart(2, "0");
2123
+ say(dim(`Still waiting\u2026 ${String(minutes)}:${seconds} remaining`));
2124
+ lastReportedRemaining = remainingSeconds;
2125
+ }
2126
+ } else if (progress.phase === "received") {
2127
+ say(dim("Authorization received. Completing sign-in\u2026"));
2128
+ }
2129
+ }
2130
+ );
2096
2131
  saveToken(signedIn.token);
2097
2132
  }
2098
2133
  say(`${green("ok")} signed in ${dim(`(${apiUrl()})`)}`);
2099
- if (skillBootstrap?.detected.length === 0) {
2100
- say(
2101
- dim("no Codex or Claude installation detected \u2014 use marina skills install --agent later")
2102
- );
2103
- }
2104
2134
  result({
2105
2135
  command: "setup",
2106
2136
  signed_in: true,
2107
2137
  api: apiUrl(),
2108
- profile: profilePath(),
2109
- skills: skillBootstrap?.installed ?? []
2138
+ profile: profilePath()
2110
2139
  });
2111
2140
  return;
2112
2141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marina-cloud/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Command-line client for Marina Cloud",
5
5
  "homepage": "https://github.com/marina-hq/marina#readme",
6
6
  "bugs": {