pairit 0.1.2 → 0.1.4

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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +73 -63
  3. package/package.json +41 -41
package/README.md CHANGED
@@ -38,6 +38,7 @@ This makes `pairit` available globally from the `bin` field in package.json.
38
38
 
39
39
  ```bash
40
40
  pairit login # Authenticate (required first)
41
+ # On a remote/headless server, open the printed URL locally and paste back the one-time code
41
42
 
42
43
  pairit config lint path/to/config.yaml
43
44
  pairit config compile path/to/config.yaml
package/dist/index.js CHANGED
@@ -9955,73 +9955,74 @@ async function exchangeCodeForToken(code) {
9955
9955
  async function login() {
9956
9956
  console.log("Initiating login...");
9957
9957
  const loginUrl = `${BASE_URL}/login`;
9958
- try {
9959
- const { createServer } = await import("node:http");
9960
- const { default: open2 } = await Promise.resolve().then(() => (init_open(), exports_open));
9961
- const server = createServer();
9962
- const port = await new Promise((resolve, reject) => {
9963
- server.listen(0, "127.0.0.1", () => {
9964
- const address = server.address();
9965
- if (address && typeof address === "object") {
9966
- resolve(address.port);
9967
- } else {
9968
- reject(new Error("Could not determine port"));
9969
- }
9970
- });
9971
- });
9972
- const redirectUri = `http://127.0.0.1:${port}`;
9973
- const targetUrl = `${loginUrl}?cli_redirect_uri=${encodeURIComponent(redirectUri)}`;
9974
- console.log("Opening browser for authentication...");
9975
- await open2(targetUrl);
9976
- console.log(`Waiting for authentication at ${redirectUri}...`);
9977
- const authCode = await new Promise((resolve, reject) => {
9978
- const timeout = setTimeout(() => {
9979
- server.close();
9980
- reject(new Error("Login timed out"));
9981
- }, 120000);
9982
- server.on("request", (req, res) => {
9983
- try {
9984
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
9985
- const code = url.searchParams.get("code");
9986
- if (code) {
9987
- res.writeHead(200, { "Content-Type": "text/html" });
9988
- res.end(`<!DOCTYPE html><html><head><title>Login Successful</title></head><body style="font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><div style="text-align:center"><h1>Authenticated!</h1><p>You can close this window and return to your CLI.</p></div><script>setTimeout(()=>window.close(),3000)</script></body></html>`);
9989
- clearTimeout(timeout);
9990
- server.close();
9991
- resolve(code);
9958
+ const manualLoginUrl = `${loginUrl}?manual=1`;
9959
+ if (canAttemptBrowserLogin()) {
9960
+ try {
9961
+ const { createServer } = await import("node:http");
9962
+ const { default: open2 } = await Promise.resolve().then(() => (init_open(), exports_open));
9963
+ const server = createServer();
9964
+ const port = await new Promise((resolve, reject) => {
9965
+ server.listen(0, "127.0.0.1", () => {
9966
+ const address = server.address();
9967
+ if (address && typeof address === "object") {
9968
+ resolve(address.port);
9992
9969
  } else {
9993
- res.writeHead(400);
9994
- res.end("Missing authorization code");
9970
+ reject(new Error("Could not determine port"));
9995
9971
  }
9996
- } catch (_e) {
9997
- res.writeHead(500);
9998
- res.end("Error processing request");
9999
- }
9972
+ });
10000
9973
  });
10001
- });
10002
- console.log("Exchanging authorization code for token...");
10003
- const token2 = await exchangeCodeForToken(authCode);
10004
- await saveCredentials({ token: token2 });
10005
- console.log("Successfully logged in!");
10006
- return;
10007
- } catch (error) {
10008
- console.warn("Automated login failed, falling back to manual entry:", error instanceof Error ? error.message : String(error));
9974
+ const redirectUri = `http://127.0.0.1:${port}`;
9975
+ const targetUrl = `${loginUrl}?cli_redirect_uri=${encodeURIComponent(redirectUri)}`;
9976
+ console.log("Opening browser for authentication...");
9977
+ await open2(targetUrl);
9978
+ console.log(`Waiting for authentication at ${redirectUri}...`);
9979
+ const authCode = await new Promise((resolve, reject) => {
9980
+ const timeout = setTimeout(() => {
9981
+ server.close();
9982
+ reject(new Error("Login timed out"));
9983
+ }, 120000);
9984
+ server.on("request", (req, res) => {
9985
+ try {
9986
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
9987
+ const code2 = url.searchParams.get("code");
9988
+ if (code2) {
9989
+ res.writeHead(200, { "Content-Type": "text/html" });
9990
+ res.end(`<!DOCTYPE html><html><head><title>Login Successful</title></head><body style="font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><div style="text-align:center"><h1>Authenticated!</h1><p>You can close this window and return to your CLI.</p></div><script>setTimeout(()=>window.close(),3000)</script></body></html>`);
9991
+ clearTimeout(timeout);
9992
+ server.close();
9993
+ resolve(code2);
9994
+ } else {
9995
+ res.writeHead(400);
9996
+ res.end("Missing authorization code");
9997
+ }
9998
+ } catch (_e) {
9999
+ res.writeHead(500);
10000
+ res.end("Error processing request");
10001
+ }
10002
+ });
10003
+ });
10004
+ console.log("Exchanging authorization code for token...");
10005
+ const token2 = await exchangeCodeForToken(authCode);
10006
+ await saveCredentials({ token: token2 });
10007
+ console.log("Successfully logged in!");
10008
+ return;
10009
+ } catch (error) {
10010
+ console.warn("Automated login failed, falling back to manual entry:", error instanceof Error ? error.message : String(error));
10011
+ }
10009
10012
  }
10010
10013
  console.log(`
10011
- Please visit: ${loginUrl}
10014
+ Open this URL in a browser on any machine:
10015
+ ${manualLoginUrl}
10012
10016
 
10013
- After logging in, you will see a success page. The CLI will handle authentication automatically.
10014
- If automatic login fails, you may need to check your network connection or try again.
10017
+ After signing in, the page will show a one-time authorization code.
10018
+ Paste that code here to complete CLI login.
10015
10019
  `);
10016
- try {
10017
- const { default: open2 } = await Promise.resolve().then(() => (init_open(), exports_open));
10018
- await open2(loginUrl);
10019
- } catch (_e) {}
10020
- const token = await prompt("Enter session token (from browser): ");
10021
- if (!token) {
10022
- console.error("No token provided");
10020
+ const code = await prompt("Enter authorization code: ");
10021
+ if (!code) {
10022
+ console.error("No authorization code provided");
10023
10023
  process.exit(1);
10024
10024
  }
10025
+ const token = await exchangeCodeForToken(code);
10025
10026
  await saveCredentials({ token });
10026
10027
  console.log("Successfully logged in!");
10027
10028
  }
@@ -10086,6 +10087,15 @@ async function prompt(question) {
10086
10087
  });
10087
10088
  });
10088
10089
  }
10090
+ function canAttemptBrowserLogin() {
10091
+ if (!process.stdout.isTTY || !process.stdin.isTTY) {
10092
+ return false;
10093
+ }
10094
+ if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY) {
10095
+ return false;
10096
+ }
10097
+ return process.platform === "darwin" || process.platform === "win32" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
10098
+ }
10089
10099
  async function loadKeytar() {
10090
10100
  try {
10091
10101
  const runtimeImport = new Function("s", "return import(s)");
@@ -10101,7 +10111,7 @@ async function loadKeytar() {
10101
10111
 
10102
10112
  // src/index.ts
10103
10113
  var program2 = new Command;
10104
- program2.name("pairit").description("CLI for Pairit experiment configs").version("0.1.0");
10114
+ program2.name("pairit").description("CLI for Pairit experiment configs").version("0.1.3");
10105
10115
  program2.command("login").description("Login to Pairit Manager").action(async () => {
10106
10116
  try {
10107
10117
  await login();
@@ -10560,11 +10570,11 @@ async function exportData(configId, options) {
10560
10570
  Files written to: ${outDir}`);
10561
10571
  }
10562
10572
  function flattenSession(session) {
10563
- const { user_state, prolific, ...rest } = session;
10573
+ const { session_state, prolific, ...rest } = session;
10564
10574
  const flattened = { ...rest };
10565
- if (user_state && typeof user_state === "object") {
10566
- for (const [key, value] of Object.entries(user_state)) {
10567
- flattened[`user_state.${key}`] = serializeValue(value);
10575
+ if (session_state && typeof session_state === "object") {
10576
+ for (const [key, value] of Object.entries(session_state)) {
10577
+ flattened[`session_state.${key}`] = serializeValue(value);
10568
10578
  }
10569
10579
  }
10570
10580
  if (prolific) {
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
1
  {
2
- "name": "pairit",
3
- "version": "0.1.2",
4
- "description": "CLI for the Pairit behavioral science experiment platform",
5
- "type": "module",
6
- "bin": {
7
- "pairit": "dist/index.js"
8
- },
9
- "files": [
10
- "dist"
11
- ],
12
- "scripts": {
13
- "build": "bun build src/index.ts --outdir dist --target node",
14
- "lint": "tsc --noEmit",
15
- "prepublishOnly": "bun run build"
16
- },
17
- "keywords": [
18
- "experiment",
19
- "behavioral-science",
20
- "research",
21
- "cli"
22
- ],
23
- "repository": {
24
- "type": "git",
25
- "url": "https://github.com/pairium/pairit.git",
26
- "directory": "apps/manager/cli"
27
- },
28
- "homepage": "https://pairium.github.io/pairit/",
29
- "license": "MIT",
30
- "optionalDependencies": {
31
- "keytar": "^7.9.0"
32
- },
33
- "devDependencies": {
34
- "@types/bun": "^1.1.13",
35
- "@types/open": "^6.2.1",
36
- "better-auth": "^1.4.7",
37
- "commander": "^12.1.0",
38
- "dotenv": "^17.2.3",
39
- "open": "^11.0.0",
40
- "typescript": "^5.9.3",
41
- "yaml": "^2.6.0"
42
- }
2
+ "name": "pairit",
3
+ "version": "0.1.4",
4
+ "description": "CLI for the Pairit behavioral science experiment platform",
5
+ "type": "module",
6
+ "bin": {
7
+ "pairit": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "bun build src/index.ts --outdir dist --target node",
14
+ "lint": "tsc --noEmit",
15
+ "prepublishOnly": "bun run build"
16
+ },
17
+ "keywords": [
18
+ "experiment",
19
+ "behavioral-science",
20
+ "research",
21
+ "cli"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/pairium/pairit.git",
26
+ "directory": "apps/manager/cli"
27
+ },
28
+ "homepage": "https://pairium.github.io/pairit/",
29
+ "license": "MIT",
30
+ "optionalDependencies": {
31
+ "keytar": "^7.9.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/bun": "^1.1.13",
35
+ "@types/open": "^6.2.1",
36
+ "better-auth": "^1.4.7",
37
+ "commander": "^12.1.0",
38
+ "dotenv": "^17.2.3",
39
+ "open": "^11.0.0",
40
+ "typescript": "^5.9.3",
41
+ "yaml": "^2.6.0"
42
+ }
43
43
  }