@aisa-one/cli 0.6.5 → 0.6.6

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/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.6] — 2026-09-30
11
+
12
+ ### Fixed
13
+
14
+ - Open the browser on Windows. `start` is built into cmd.exe, so running it
15
+ directly failed with ENOENT and the error was swallowed: `aisa login`,
16
+ `aisa connect` and `aisa topup` never opened a page, and sign-in waited
17
+ for an approval that could not arrive. The browser now opens through
18
+ cmd.exe, with `&` and other special characters escaped so the whole URL
19
+ gets through.
20
+
10
21
  ## [0.6.5] — 2026-09-30
11
22
 
12
23
  ### Added
@@ -1,4 +1,4 @@
1
- import { run } from "../utils/exec.js";
1
+ import { openBrowser } from "../utils/browser.js";
2
2
  import chalk from "chalk";
3
3
  import { requireAccessToken } from "../config.js";
4
4
  import { apiRequest } from "../api.js";
@@ -92,8 +92,7 @@ export function topupAction(amount, options = {}) {
92
92
  console.log(` ${chalk.cyan(url)}`);
93
93
  if (options.open === false)
94
94
  return;
95
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
96
- void run(cmd, [url], { timeout: 30_000 }).catch(() => { });
95
+ openBrowser(url);
97
96
  hint("Credit lands in your account as soon as the payment completes");
98
97
  }
99
98
  /** Gateway caps GET /v1/usage at 31 days; buckets are fixed at one day. */
@@ -14,7 +14,7 @@ import { INSTALLERS, installAgent, isInstalled, supported } from "./install.js";
14
14
  import { writeCodexLLM, writeClaudeCodeLLM, writeOpencodeLLM, writeCodexAisaProfile, writeOpencodeAisaBackup, defaultModelsFor, patchCodexMCPAuth, DEFAULT_MODELS, } from "./llm-config.js";
15
15
  import { writeClaudeAisaSettings, installWrappers } from "./wrappers.js";
16
16
  import { signInAndStoreTokens } from "./oauth-login.js";
17
- import { canOpenBrowser } from "../utils/browser.js";
17
+ import { canOpenBrowser, openBrowser } from "../utils/browser.js";
18
18
  import { vscodeDetected, vscodeUserDir, writeVSCodeLLM, writeVSCodeMCP, installVSCodeExtension, launchVSCode, VSCODE_MODELS } from "./vscode.js";
19
19
  import { formatMicrosUSD } from "./account.js";
20
20
  import { apiRequest } from "../api.js";
@@ -1812,10 +1812,6 @@ function launchCursor(dir) {
1812
1812
  function withBrowserEarly() {
1813
1813
  return canOpenBrowser();
1814
1814
  }
1815
- function openBrowser(url) {
1816
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1817
- void run(cmd, [url], { timeout: 30_000 }).catch(() => { });
1818
- }
1819
1815
  /**
1820
1816
  * Where a live run leaves its address, so a second `aisa connect` can offer
1821
1817
  * the first one's page instead of starting a rival server. Ephemeral by
@@ -1,5 +1,4 @@
1
1
  import chalk from "chalk";
2
- import { run } from "../utils/exec.js";
3
2
  import { createHash, randomBytes } from "node:crypto";
4
3
  import { createServer } from "node:http";
5
4
  import { createInterface } from "node:readline/promises";
@@ -7,7 +6,7 @@ import { error, hint, info, success } from "../utils/display.js";
7
6
  import { replaceTokens, tokenExpiresAt } from "../config.js";
8
7
  import { maskKey } from "../config.js";
9
8
  import { httpFetch } from "../utils/http.js";
10
- import { canOpenBrowser } from "../utils/browser.js";
9
+ import { canOpenBrowser, openBrowser } from "../utils/browser.js";
11
10
  import { SIGNIN, t } from "./flow.js";
12
11
  import { renderSignInPage } from "./signin-page.js";
13
12
  import { handOverSignInPage, SIGNIN_PAGE_TTL_MS } from "./serve-signin.js";
@@ -220,10 +219,6 @@ async function waitForPaste(expectedState, lang) {
220
219
  /* c8 ignore next */
221
220
  throw new Error(t(SIGNIN.badPaste, lang));
222
221
  }
223
- function openBrowser(url) {
224
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
225
- void run(cmd, [url], { timeout: 30_000 }).catch(() => { });
226
- }
227
222
  export async function signInAndStoreTokens(options = {}) {
228
223
  const lang = options.lang ?? "en";
229
224
  // Unset means "work it out": a server has no browser to open and waiting
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.6.5";
1
+ export declare const VERSION = "0.6.6";
2
2
  /** Default for chat when no model is configured or supplied explicitly. */
3
3
  export declare const DEFAULT_CHAT_MODEL = "deepseek-v4.1-flash";
4
4
  /** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "0.6.5";
1
+ export const VERSION = "0.6.6";
2
2
  /** Default for chat when no model is configured or supplied explicitly. */
3
3
  export const DEFAULT_CHAT_MODEL = "deepseek-v4.1-flash";
4
4
  /** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
@@ -17,3 +17,29 @@
17
17
  * for a click nobody will ever make.
18
18
  */
19
19
  export declare function canOpenBrowser(): boolean;
20
+ /**
21
+ * The command that hands a URL to the default browser on this platform.
22
+ *
23
+ * Windows is the one that needs care. Its opener, `start`, is not a program
24
+ * but a command built into cmd.exe, so running it directly fails with
25
+ * ENOENT, and the failure used to be swallowed: the browser never opened,
26
+ * and `aisa login` sat waiting for an approval that could not arrive.
27
+ * Going through cmd.exe fixes that, with two details:
28
+ *
29
+ * · `start` reads its first quoted argument as a window title, so an empty
30
+ * title goes first; otherwise a quoted URL would become the title.
31
+ * · cmd.exe reads `&` in a query string as "run the next command", which
32
+ * would cut the URL at its second parameter. Its special characters are
33
+ * escaped with `^` so the whole URL reaches the browser.
34
+ */
35
+ export declare function browserCommand(url: string, platform?: NodeJS.Platform): {
36
+ file: string;
37
+ args: string[];
38
+ };
39
+ /**
40
+ * Open a URL in the default browser, without waiting and without failing.
41
+ *
42
+ * Every caller prints the URL as well, so a browser that does not open
43
+ * costs the user a click, not the command.
44
+ */
45
+ export declare function openBrowser(url: string): void;
@@ -1,3 +1,4 @@
1
+ import { run } from "./exec.js";
1
2
  /**
2
3
  * Can this machine put a page in front of the person using it?
3
4
  *
@@ -29,3 +30,36 @@ export function canOpenBrowser() {
29
30
  return true;
30
31
  return Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
31
32
  }
33
+ /**
34
+ * The command that hands a URL to the default browser on this platform.
35
+ *
36
+ * Windows is the one that needs care. Its opener, `start`, is not a program
37
+ * but a command built into cmd.exe, so running it directly fails with
38
+ * ENOENT, and the failure used to be swallowed: the browser never opened,
39
+ * and `aisa login` sat waiting for an approval that could not arrive.
40
+ * Going through cmd.exe fixes that, with two details:
41
+ *
42
+ * · `start` reads its first quoted argument as a window title, so an empty
43
+ * title goes first; otherwise a quoted URL would become the title.
44
+ * · cmd.exe reads `&` in a query string as "run the next command", which
45
+ * would cut the URL at its second parameter. Its special characters are
46
+ * escaped with `^` so the whole URL reaches the browser.
47
+ */
48
+ export function browserCommand(url, platform = process.platform) {
49
+ if (platform === "darwin")
50
+ return { file: "open", args: [url] };
51
+ if (platform === "win32") {
52
+ return { file: "cmd", args: ["/c", "start", "", url.replace(/[\^&|<>()]/g, "^$&")] };
53
+ }
54
+ return { file: "xdg-open", args: [url] };
55
+ }
56
+ /**
57
+ * Open a URL in the default browser, without waiting and without failing.
58
+ *
59
+ * Every caller prints the URL as well, so a browser that does not open
60
+ * costs the user a click, not the command.
61
+ */
62
+ export function openBrowser(url) {
63
+ const { file, args } = browserCommand(url);
64
+ void run(file, args, { timeout: 30_000 }).catch(() => { });
65
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aisa-one/cli",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "CLI for the AIsa unified AI infrastructure platform - one API key for 80+ LLMs and 900+ endpoints across finance, search, social, and video APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",