@malloydata/malloyyo 0.2.39 → 0.2.40

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/index.js +64 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2881,7 +2881,7 @@ function clearCreds(url6) {
2881
2881
  }
2882
2882
 
2883
2883
  // package.json
2884
- var version = "0.2.39";
2884
+ var version = "0.2.40";
2885
2885
 
2886
2886
  // src/http.ts
2887
2887
  var USER_AGENT = `malloyyo/${version}`;
@@ -2928,15 +2928,34 @@ async function registerClient(registrationEndpoint, redirectUri) {
2928
2928
  if (!res.ok) throw new Error(`client registration failed: ${res.status} ${await res.text()}`);
2929
2929
  return (await res.json()).client_id;
2930
2930
  }
2931
+ function browserless(platform = process.platform, env = process.env) {
2932
+ if (platform === "darwin" || platform === "win32") return false;
2933
+ return !env.DISPLAY && !env.WAYLAND_DISPLAY;
2934
+ }
2931
2935
  function openBrowser(url6) {
2932
2936
  const [cmd, args] = process.platform === "darwin" ? ["open", [url6]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url6]] : ["xdg-open", [url6]];
2933
2937
  try {
2934
- spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
2938
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
2939
+ child.on("error", () => {
2940
+ });
2941
+ child.unref();
2935
2942
  } catch {
2936
2943
  }
2937
2944
  }
2945
+ function listenTarget(env = process.env) {
2946
+ const raw = env.MALLOYYO_OAUTH_PORT;
2947
+ let port = 0;
2948
+ if (raw !== void 0 && raw !== "") {
2949
+ port = Number(raw);
2950
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
2951
+ throw new Error(`MALLOYYO_OAUTH_PORT must be a port number between 1 and 65535, got "${raw}"`);
2952
+ }
2953
+ }
2954
+ return { host: env.MALLOYYO_OAUTH_HOST || "127.0.0.1", port };
2955
+ }
2938
2956
  function awaitRedirect(state) {
2939
- return new Promise((resolveServer) => {
2957
+ return new Promise((resolveServer, rejectServer) => {
2958
+ const { host, port: wanted } = listenTarget();
2940
2959
  let resolveCode;
2941
2960
  let rejectCode;
2942
2961
  const code = new Promise((res, rej) => {
@@ -2961,13 +2980,33 @@ function awaitRedirect(state) {
2961
2980
  if (ok) resolveCode(got);
2962
2981
  else rejectCode(new Error(err ?? "state mismatch or missing code"));
2963
2982
  });
2964
- server.listen(0, "127.0.0.1", () => {
2983
+ let listening = false;
2984
+ server.on("error", (err) => {
2985
+ clearTimeout(timer);
2986
+ const detail = err.code === "EADDRINUSE" ? `${host}:${wanted} is already in use \u2014 set MALLOYYO_OAUTH_PORT to a free port` : err.message;
2987
+ const failure = new Error(`could not start the sign-in listener: ${detail}`);
2988
+ rejectCode(failure);
2989
+ if (!listening) {
2990
+ void code.catch(() => {
2991
+ });
2992
+ rejectServer(failure);
2993
+ }
2994
+ });
2995
+ server.listen(wanted, host, () => {
2996
+ listening = true;
2965
2997
  const port = server.address().port;
2966
- resolveServer({ port, code, close: () => server.close() });
2998
+ resolveServer({
2999
+ port,
3000
+ code,
3001
+ close: () => {
3002
+ clearTimeout(timer);
3003
+ server.close();
3004
+ }
3005
+ });
2967
3006
  });
2968
3007
  });
2969
3008
  }
2970
- async function login(baseUrl) {
3009
+ async function login(baseUrl, opts = {}) {
2971
3010
  const ep = await discover(baseUrl);
2972
3011
  const { verifier, challenge } = pkce();
2973
3012
  const state = crypto.randomBytes(16).toString("base64url");
@@ -2985,11 +3024,24 @@ async function login(baseUrl) {
2985
3024
  scope: "mcp",
2986
3025
  state
2987
3026
  }).toString();
2988
- console.log("Opening your browser to sign in\u2026");
2989
- console.log(`If it doesn't open, visit:
3027
+ if (opts.noBrowser || browserless()) {
3028
+ console.log(`Visit this URL to sign in:
3029
+
2990
3030
  ${authUrl.toString()}
2991
3031
  `);
2992
- openBrowser(authUrl.toString());
3032
+ if (!process.env.MALLOYYO_OAUTH_PORT) {
3033
+ console.log(
3034
+ "Note: sign-in redirects back to this machine on a random port.\n In a container, set MALLOYYO_OAUTH_PORT and MALLOYYO_OAUTH_HOST=0.0.0.0,\n and publish that port, so the browser can reach the redirect.\n"
3035
+ );
3036
+ }
3037
+ console.log("Waiting for sign-in to complete\u2026");
3038
+ } else {
3039
+ console.log("Opening your browser to sign in\u2026");
3040
+ console.log(`If it doesn't open, visit:
3041
+ ${authUrl.toString()}
3042
+ `);
3043
+ openBrowser(authUrl.toString());
3044
+ }
2993
3045
  const authCode = await code;
2994
3046
  const res = await apiFetch(ep.token_endpoint, {
2995
3047
  method: "POST",
@@ -5389,9 +5441,9 @@ async function status(target, opts) {
5389
5441
  console.log(` version ${s.version ?? "?"}` + (git?.sha ? ` ${git.branch}@${shortSha(git.sha)}` : ""));
5390
5442
  console.log(` ${s.compileError ? `\u2717 ${s.compileError}` : `\u2713 compiled ${s.compiledAt ?? ""}`}`);
5391
5443
  }
5392
- async function loginCmd(target) {
5444
+ async function loginCmd(target, opts) {
5393
5445
  const inst = resolveInstance(resolve3("."), target);
5394
- await login(inst.url);
5446
+ await login(inst.url, { noBrowser: opts.browser === false });
5395
5447
  console.log(`\u2713 logged in to ${inst.name} (${inst.url})`);
5396
5448
  }
5397
5449
  async function logoutCmd(target) {
@@ -5400,7 +5452,7 @@ async function logoutCmd(target) {
5400
5452
  }
5401
5453
  var program = new Command();
5402
5454
  program.name("malloyyo").description("Publish Malloy models to a Malloyyo instance").version(version);
5403
- program.command("login").argument("[target]", "target name or instance URL (optional if the config has one target)").description("sign in to an instance in your browser (stores a token)").action(loginCmd);
5455
+ program.command("login").argument("[target]", "target name or instance URL (optional if the config has one target)").option("--no-browser", "print the sign-in URL instead of launching a browser").description("sign in to an instance in your browser (stores a token)").action(loginCmd);
5404
5456
  program.command("logout").argument("[target]", "target name or instance URL (optional if the config has one target)").description("forget the stored token for an instance").action(logoutCmd);
5405
5457
  program.command("publish").argument(
5406
5458
  "[target]",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloyyo",
3
- "version": "0.2.39",
3
+ "version": "0.2.40",
4
4
  "description": "Publish Malloy models to a Malloyyo instance",
5
5
  "license": "MIT",
6
6
  "repository": {