pairit 0.1.9 → 0.3.0
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/README.md +17 -10
- package/dist/index.js +278 -85
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -43,8 +43,8 @@ pairit login # Authenticate (required firs
|
|
|
43
43
|
|
|
44
44
|
pairit config lint path/to/config.yaml
|
|
45
45
|
pairit config compile path/to/config.yaml
|
|
46
|
-
pairit config upload configs/simple-survey-basic.yaml --
|
|
47
|
-
pairit config upload configs/agent-study.yaml --
|
|
46
|
+
pairit config upload configs/simple-survey-basic.yaml --name simple-survey-basic
|
|
47
|
+
pairit config upload configs/agent-study.yaml --name agent-study --openai-api-key sk-...
|
|
48
48
|
pairit config list
|
|
49
49
|
pairit config delete 2f3c4d5e...
|
|
50
50
|
|
|
@@ -63,11 +63,11 @@ Add `--private` if you need to keep an object private. Use `--bucket <name>` onl
|
|
|
63
63
|
### Example
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
# Point
|
|
66
|
+
# Point at local manager (published CLI defaults to production)
|
|
67
67
|
export PAIRIT_API_URL=http://localhost:3002
|
|
68
68
|
|
|
69
|
-
bun run apps/manager/cli/src/index.ts config upload configs/simple-survey-basic.yaml --
|
|
70
|
-
bun run apps/manager/cli/src/index.ts config upload configs/agent-study.yaml --
|
|
69
|
+
bun run apps/manager/cli/src/index.ts config upload configs/simple-survey-basic.yaml --name simple-survey-basic
|
|
70
|
+
bun run apps/manager/cli/src/index.ts config upload configs/agent-study.yaml --name agent-study --openai-api-key sk-...
|
|
71
71
|
bun run apps/manager/cli/src/index.ts config list
|
|
72
72
|
bun run apps/manager/cli/src/index.ts config delete 2f3c4d5e... --force
|
|
73
73
|
|
|
@@ -76,7 +76,7 @@ bun run apps/manager/cli/src/index.ts media list
|
|
|
76
76
|
bun run apps/manager/cli/src/index.ts media delete onboarding/logo.png --force
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
`config compile` writes `configs/simple-survey-basic.json` next to the source YAML. `config upload`
|
|
79
|
+
`config compile` writes `configs/simple-survey-basic.json` next to the source YAML. `config upload` creates or updates a config by name (`--name`, else the YAML `name:` field, else the file name). The server assigns the config id (`<name>-<10 random chars>`) on the first upload, and later uploads under the same name add revisions. `--config-id` updates an existing config by id. See `config history` for past revisions.
|
|
80
80
|
|
|
81
81
|
## Manager allowlist (admin)
|
|
82
82
|
|
|
@@ -101,8 +101,8 @@ Notes:
|
|
|
101
101
|
If your config uses AI agents, attach provider credentials when uploading:
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
|
-
pairit config upload path/to/config.yaml --
|
|
105
|
-
pairit config upload path/to/config.yaml --
|
|
104
|
+
pairit config upload path/to/config.yaml --name my-exp --openai-api-key sk-...
|
|
105
|
+
pairit config upload path/to/config.yaml --name my-exp --anthropic-api-key sk-ant-...
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Notes:
|
|
@@ -118,9 +118,16 @@ Notes:
|
|
|
118
118
|
- `{configId}-events.{format}` — session ID, event type, component info, payload, timestamps
|
|
119
119
|
- `{configId}-chat-messages.{format}` — group ID, session ID, sender, content, timestamps
|
|
120
120
|
|
|
121
|
-
All hosted commands require the manager service to be reachable
|
|
121
|
+
All hosted commands require the manager service to be reachable. By default the CLI talks to production (`https://pairit-api.pairium.ai`). Add `--env staging` to use staging; each environment keeps its own saved login.
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
```bash
|
|
124
|
+
pairit --env staging login
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Override via shell environment variables when needed:
|
|
128
|
+
|
|
129
|
+
- `PAIRIT_ENV` — `production` (default) or `staging`, same as `--env`
|
|
130
|
+
- `PAIRIT_API_URL` — manager base URL. Use `http://localhost:3002` for local dev.
|
|
124
131
|
- `PAIRIT_LAB_URL` — lab base URL used to print the survey link after `config upload`
|
|
125
132
|
- `PAIRIT_CREDENTIALS_BACKEND` — `keychain` (default when available) or `file`
|
|
126
133
|
- `PAIRIT_MAX_INLINE_MEDIA_BYTES` — switch to signed-URL upload above this size (default 5 MiB)
|
package/dist/index.js
CHANGED
|
@@ -8866,11 +8866,11 @@ var init_is_inside_container = __esm(() => {
|
|
|
8866
8866
|
});
|
|
8867
8867
|
|
|
8868
8868
|
// ../../../node_modules/.bun/is-wsl@3.1.0/node_modules/is-wsl/index.js
|
|
8869
|
-
import
|
|
8869
|
+
import process3 from "node:process";
|
|
8870
8870
|
import os from "node:os";
|
|
8871
8871
|
import fs3 from "node:fs";
|
|
8872
8872
|
var isWsl = () => {
|
|
8873
|
-
if (
|
|
8873
|
+
if (process3.platform !== "linux") {
|
|
8874
8874
|
return false;
|
|
8875
8875
|
}
|
|
8876
8876
|
if (os.release().toLowerCase().includes("microsoft")) {
|
|
@@ -8887,15 +8887,15 @@ var isWsl = () => {
|
|
|
8887
8887
|
}, is_wsl_default;
|
|
8888
8888
|
var init_is_wsl = __esm(() => {
|
|
8889
8889
|
init_is_inside_container();
|
|
8890
|
-
is_wsl_default =
|
|
8890
|
+
is_wsl_default = process3.env.__IS_WSL_TEST__ ? isWsl : isWsl();
|
|
8891
8891
|
});
|
|
8892
8892
|
|
|
8893
8893
|
// ../../../node_modules/.bun/powershell-utils@0.1.0/node_modules/powershell-utils/index.js
|
|
8894
|
-
import
|
|
8894
|
+
import process4 from "node:process";
|
|
8895
8895
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
8896
8896
|
import { promisify } from "node:util";
|
|
8897
8897
|
import childProcess from "node:child_process";
|
|
8898
|
-
var execFile, powerShellPath = () => `${
|
|
8898
|
+
var execFile, powerShellPath = () => `${process4.env.SYSTEMROOT || process4.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, executePowerShell = async (command, options = {}) => {
|
|
8899
8899
|
const {
|
|
8900
8900
|
powerShellPath: psPath,
|
|
8901
8901
|
...execFileOptions
|
|
@@ -9010,10 +9010,10 @@ function defineLazyProperty(object, propertyName, valueGetter) {
|
|
|
9010
9010
|
|
|
9011
9011
|
// ../../../node_modules/.bun/default-browser-id@5.0.1/node_modules/default-browser-id/index.js
|
|
9012
9012
|
import { promisify as promisify3 } from "node:util";
|
|
9013
|
-
import
|
|
9013
|
+
import process5 from "node:process";
|
|
9014
9014
|
import { execFile as execFile3 } from "node:child_process";
|
|
9015
9015
|
async function defaultBrowserId() {
|
|
9016
|
-
if (
|
|
9016
|
+
if (process5.platform !== "darwin") {
|
|
9017
9017
|
throw new Error("macOS only");
|
|
9018
9018
|
}
|
|
9019
9019
|
const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
|
|
@@ -9030,11 +9030,11 @@ var init_default_browser_id = __esm(() => {
|
|
|
9030
9030
|
});
|
|
9031
9031
|
|
|
9032
9032
|
// ../../../node_modules/.bun/run-applescript@7.1.0/node_modules/run-applescript/index.js
|
|
9033
|
-
import
|
|
9033
|
+
import process6 from "node:process";
|
|
9034
9034
|
import { promisify as promisify4 } from "node:util";
|
|
9035
9035
|
import { execFile as execFile4, execFileSync } from "node:child_process";
|
|
9036
9036
|
async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
|
|
9037
|
-
if (
|
|
9037
|
+
if (process6.platform !== "darwin") {
|
|
9038
9038
|
throw new Error("macOS only");
|
|
9039
9039
|
}
|
|
9040
9040
|
const outputArguments = humanReadableOutput ? [] : ["-ss"];
|
|
@@ -9108,21 +9108,21 @@ var init_windows = __esm(() => {
|
|
|
9108
9108
|
|
|
9109
9109
|
// ../../../node_modules/.bun/default-browser@5.4.0/node_modules/default-browser/index.js
|
|
9110
9110
|
import { promisify as promisify6 } from "node:util";
|
|
9111
|
-
import
|
|
9111
|
+
import process7 from "node:process";
|
|
9112
9112
|
import { execFile as execFile6 } from "node:child_process";
|
|
9113
9113
|
async function defaultBrowser2() {
|
|
9114
|
-
if (
|
|
9114
|
+
if (process7.platform === "darwin") {
|
|
9115
9115
|
const id = await defaultBrowserId();
|
|
9116
9116
|
const name = await bundleName(id);
|
|
9117
9117
|
return { name, id };
|
|
9118
9118
|
}
|
|
9119
|
-
if (
|
|
9119
|
+
if (process7.platform === "linux") {
|
|
9120
9120
|
const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
|
|
9121
9121
|
const id = stdout.trim();
|
|
9122
9122
|
const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
|
|
9123
9123
|
return { name, id };
|
|
9124
9124
|
}
|
|
9125
|
-
if (
|
|
9125
|
+
if (process7.platform === "win32") {
|
|
9126
9126
|
return defaultBrowser();
|
|
9127
9127
|
}
|
|
9128
9128
|
throw new Error("Only macOS, Linux, and Windows are supported");
|
|
@@ -9137,10 +9137,10 @@ var init_default_browser = __esm(() => {
|
|
|
9137
9137
|
});
|
|
9138
9138
|
|
|
9139
9139
|
// ../../../node_modules/.bun/is-in-ssh@1.0.0/node_modules/is-in-ssh/index.js
|
|
9140
|
-
import
|
|
9140
|
+
import process8 from "node:process";
|
|
9141
9141
|
var isInSsh, is_in_ssh_default;
|
|
9142
9142
|
var init_is_in_ssh = __esm(() => {
|
|
9143
|
-
isInSsh = Boolean(
|
|
9143
|
+
isInSsh = Boolean(process8.env.SSH_CONNECTION || process8.env.SSH_CLIENT || process8.env.SSH_TTY);
|
|
9144
9144
|
is_in_ssh_default = isInSsh;
|
|
9145
9145
|
});
|
|
9146
9146
|
|
|
@@ -9151,7 +9151,7 @@ __export(exports_open, {
|
|
|
9151
9151
|
default: () => open_default,
|
|
9152
9152
|
apps: () => apps
|
|
9153
9153
|
});
|
|
9154
|
-
import
|
|
9154
|
+
import process9 from "node:process";
|
|
9155
9155
|
import path from "node:path";
|
|
9156
9156
|
import { fileURLToPath } from "node:url";
|
|
9157
9157
|
import childProcess3 from "node:child_process";
|
|
@@ -9322,7 +9322,7 @@ var fallbackAttemptSymbol, __dirname2, localXdgOpenPath, platform, arch, tryEach
|
|
|
9322
9322
|
await fs5.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
9323
9323
|
exeLocalXdgOpen = true;
|
|
9324
9324
|
} catch {}
|
|
9325
|
-
const useSystemXdgOpen =
|
|
9325
|
+
const useSystemXdgOpen = process9.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
|
|
9326
9326
|
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
9327
9327
|
}
|
|
9328
9328
|
if (appArguments.length > 0) {
|
|
@@ -9409,7 +9409,7 @@ var init_open = __esm(() => {
|
|
|
9409
9409
|
fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
9410
9410
|
__dirname2 = import.meta.url ? path.dirname(fileURLToPath(import.meta.url)) : "";
|
|
9411
9411
|
localXdgOpenPath = path.join(__dirname2, "xdg-open");
|
|
9412
|
-
({ platform, arch } =
|
|
9412
|
+
({ platform, arch } = process9);
|
|
9413
9413
|
apps = {
|
|
9414
9414
|
browser: "browser",
|
|
9415
9415
|
browserPrivate: "browserPrivate"
|
|
@@ -9456,9 +9456,9 @@ var init_open = __esm(() => {
|
|
|
9456
9456
|
|
|
9457
9457
|
// src/index.ts
|
|
9458
9458
|
import { createHash } from "node:crypto";
|
|
9459
|
-
import { mkdir as mkdir2, readFile as
|
|
9460
|
-
import
|
|
9461
|
-
import
|
|
9459
|
+
import { mkdir as mkdir2, readFile as readFile3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
9460
|
+
import path3 from "node:path";
|
|
9461
|
+
import process11 from "node:process";
|
|
9462
9462
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
9463
9463
|
|
|
9464
9464
|
// ../../../node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
|
|
@@ -9484,15 +9484,58 @@ var import_yaml = __toESM(require_dist(), 1);
|
|
|
9484
9484
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
9485
9485
|
import { homedir } from "node:os";
|
|
9486
9486
|
import { join } from "node:path";
|
|
9487
|
+
|
|
9488
|
+
// src/env.ts
|
|
9489
|
+
import process2 from "node:process";
|
|
9490
|
+
var ENVIRONMENTS = {
|
|
9491
|
+
production: {
|
|
9492
|
+
api: "https://pairit-api.pairium.ai",
|
|
9493
|
+
lab: "https://pairit.pairium.ai"
|
|
9494
|
+
},
|
|
9495
|
+
staging: {
|
|
9496
|
+
api: "https://pairit-api-staging.pairium.ai",
|
|
9497
|
+
lab: "https://pairit-staging.pairium.ai"
|
|
9498
|
+
}
|
|
9499
|
+
};
|
|
9500
|
+
var selected;
|
|
9501
|
+
function parseEnvName(value) {
|
|
9502
|
+
if (value === "production" || value === "staging")
|
|
9503
|
+
return value;
|
|
9504
|
+
throw new Error(`Unknown environment "${value}". Use staging or production.`);
|
|
9505
|
+
}
|
|
9506
|
+
function setEnv(name) {
|
|
9507
|
+
selected = name;
|
|
9508
|
+
}
|
|
9509
|
+
function getEnvName() {
|
|
9510
|
+
if (selected)
|
|
9511
|
+
return selected;
|
|
9512
|
+
const fromEnv = process2.env.PAIRIT_ENV;
|
|
9513
|
+
return fromEnv ? parseEnvName(fromEnv) : "production";
|
|
9514
|
+
}
|
|
9515
|
+
function getApiUrl() {
|
|
9516
|
+
return process2.env.PAIRIT_API_URL || ENVIRONMENTS[getEnvName()].api;
|
|
9517
|
+
}
|
|
9518
|
+
function getLabUrl() {
|
|
9519
|
+
return process2.env.PAIRIT_LAB_URL || ENVIRONMENTS[getEnvName()].lab;
|
|
9520
|
+
}
|
|
9521
|
+
function getCredentialKey() {
|
|
9522
|
+
const api = getApiUrl();
|
|
9523
|
+
if (api === ENVIRONMENTS.production.api)
|
|
9524
|
+
return "default";
|
|
9525
|
+
return new URL(api).host;
|
|
9526
|
+
}
|
|
9527
|
+
|
|
9528
|
+
// src/auth.ts
|
|
9487
9529
|
var CONFIG_DIR = join(homedir(), ".pairit");
|
|
9488
|
-
|
|
9530
|
+
function getCredentialsFile() {
|
|
9531
|
+
const key = getCredentialKey();
|
|
9532
|
+
return join(CONFIG_DIR, key === "default" ? "credentials.json" : `credentials-${key.replace(/[^a-zA-Z0-9.-]/g, "_")}.json`);
|
|
9533
|
+
}
|
|
9489
9534
|
var CREDENTIALS_BACKEND = process.env.PAIRIT_CREDENTIALS_BACKEND || "keychain";
|
|
9490
9535
|
var KEYCHAIN_SERVICE = "pairit-cli";
|
|
9491
|
-
var KEYCHAIN_ACCOUNT = "default";
|
|
9492
9536
|
var KEYTAR_MODULE_SPECIFIER = "keytar";
|
|
9493
|
-
var BASE_URL = process.env.PAIRIT_API_URL || "https://manager-432501290611.us-central1.run.app";
|
|
9494
9537
|
async function exchangeCodeForToken(code) {
|
|
9495
|
-
const response = await fetch(`${
|
|
9538
|
+
const response = await fetch(`${getApiUrl()}/api/cli/exchange`, {
|
|
9496
9539
|
method: "POST",
|
|
9497
9540
|
headers: { "Content-Type": "application/json" },
|
|
9498
9541
|
body: JSON.stringify({ code })
|
|
@@ -9508,8 +9551,8 @@ async function exchangeCodeForToken(code) {
|
|
|
9508
9551
|
return data.token;
|
|
9509
9552
|
}
|
|
9510
9553
|
async function login() {
|
|
9511
|
-
console.log(
|
|
9512
|
-
const loginUrl = `${
|
|
9554
|
+
console.log(`Initiating login to ${getApiUrl()}...`);
|
|
9555
|
+
const loginUrl = `${getApiUrl()}/login`;
|
|
9513
9556
|
const manualLoginUrl = `${loginUrl}?manual=1`;
|
|
9514
9557
|
if (canAttemptBrowserLogin()) {
|
|
9515
9558
|
try {
|
|
@@ -9599,12 +9642,12 @@ async function saveCredentials(creds) {
|
|
|
9599
9642
|
if (CREDENTIALS_BACKEND !== "file") {
|
|
9600
9643
|
const keytar = await loadKeytar();
|
|
9601
9644
|
if (keytar) {
|
|
9602
|
-
await keytar.setPassword(KEYCHAIN_SERVICE,
|
|
9645
|
+
await keytar.setPassword(KEYCHAIN_SERVICE, getCredentialKey(), JSON.stringify(creds));
|
|
9603
9646
|
return;
|
|
9604
9647
|
}
|
|
9605
9648
|
}
|
|
9606
9649
|
await mkdir(CONFIG_DIR, { recursive: true });
|
|
9607
|
-
await writeFile(
|
|
9650
|
+
await writeFile(getCredentialsFile(), JSON.stringify(creds, null, 2), {
|
|
9608
9651
|
mode: 384
|
|
9609
9652
|
});
|
|
9610
9653
|
}
|
|
@@ -9612,7 +9655,7 @@ async function getCredentials() {
|
|
|
9612
9655
|
if (CREDENTIALS_BACKEND !== "file") {
|
|
9613
9656
|
const keytar = await loadKeytar();
|
|
9614
9657
|
if (keytar) {
|
|
9615
|
-
const stored = await keytar.getPassword(KEYCHAIN_SERVICE,
|
|
9658
|
+
const stored = await keytar.getPassword(KEYCHAIN_SERVICE, getCredentialKey());
|
|
9616
9659
|
if (stored) {
|
|
9617
9660
|
try {
|
|
9618
9661
|
return JSON.parse(stored);
|
|
@@ -9623,7 +9666,7 @@ async function getCredentials() {
|
|
|
9623
9666
|
}
|
|
9624
9667
|
}
|
|
9625
9668
|
try {
|
|
9626
|
-
const data = await readFile(
|
|
9669
|
+
const data = await readFile(getCredentialsFile(), "utf8");
|
|
9627
9670
|
return JSON.parse(data);
|
|
9628
9671
|
} catch {
|
|
9629
9672
|
return null;
|
|
@@ -9664,9 +9707,115 @@ async function loadKeytar() {
|
|
|
9664
9707
|
}
|
|
9665
9708
|
}
|
|
9666
9709
|
|
|
9710
|
+
// src/html-assets.ts
|
|
9711
|
+
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
9712
|
+
import path2 from "node:path";
|
|
9713
|
+
import process10 from "node:process";
|
|
9714
|
+
var HTML_MAX_BYTES = 1024 * 1024;
|
|
9715
|
+
function isRecord(value) {
|
|
9716
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9717
|
+
}
|
|
9718
|
+
function formatBytes(bytes) {
|
|
9719
|
+
if (bytes < 1024)
|
|
9720
|
+
return `${bytes} B`;
|
|
9721
|
+
if (bytes < 1024 * 1024)
|
|
9722
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
9723
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
9724
|
+
}
|
|
9725
|
+
function listHtmlComponents(config) {
|
|
9726
|
+
const pages = config.pages;
|
|
9727
|
+
const pageList = Array.isArray(pages) ? pages.filter(isRecord) : isRecord(pages) ? Object.values(pages).filter(isRecord) : [];
|
|
9728
|
+
const result = [];
|
|
9729
|
+
for (const page of pageList) {
|
|
9730
|
+
const components = page.components;
|
|
9731
|
+
if (!Array.isArray(components))
|
|
9732
|
+
continue;
|
|
9733
|
+
for (const comp of components) {
|
|
9734
|
+
if (!isRecord(comp) || comp.type !== "html")
|
|
9735
|
+
continue;
|
|
9736
|
+
const props = isRecord(comp.props) ? comp.props : {};
|
|
9737
|
+
const id = typeof comp.id === "string" ? comp.id : "html";
|
|
9738
|
+
result.push({ id, props });
|
|
9739
|
+
}
|
|
9740
|
+
}
|
|
9741
|
+
return result;
|
|
9742
|
+
}
|
|
9743
|
+
async function resolveHtmlSource(src, componentId, configDir) {
|
|
9744
|
+
if (typeof src !== "string" || src.trim() === "") {
|
|
9745
|
+
throw new Error(`html ${componentId}: missing src`);
|
|
9746
|
+
}
|
|
9747
|
+
const trimmed = src.trim();
|
|
9748
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
9749
|
+
throw new Error(`html ${componentId}: remote URLs are not allowed`);
|
|
9750
|
+
}
|
|
9751
|
+
if (path2.extname(trimmed).toLowerCase() !== ".html") {
|
|
9752
|
+
throw new Error(`html ${componentId}: src must be a .html file`);
|
|
9753
|
+
}
|
|
9754
|
+
const absolutePath = path2.resolve(configDir, trimmed);
|
|
9755
|
+
const rel = path2.relative(configDir, absolutePath);
|
|
9756
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
9757
|
+
throw new Error(`html ${componentId}: src must stay inside the config directory`);
|
|
9758
|
+
}
|
|
9759
|
+
let info;
|
|
9760
|
+
try {
|
|
9761
|
+
info = await stat(absolutePath);
|
|
9762
|
+
} catch {
|
|
9763
|
+
throw new Error(`html ${componentId}: file not found: ${trimmed}`);
|
|
9764
|
+
}
|
|
9765
|
+
if (!info.isFile()) {
|
|
9766
|
+
throw new Error(`html ${componentId}: file not found: ${trimmed}`);
|
|
9767
|
+
}
|
|
9768
|
+
if (info.size > HTML_MAX_BYTES) {
|
|
9769
|
+
throw new Error(`html ${componentId}: file too large (${formatBytes(info.size)}); max is 1 MB`);
|
|
9770
|
+
}
|
|
9771
|
+
return { src: trimmed, absolutePath, bytes: info.size };
|
|
9772
|
+
}
|
|
9773
|
+
async function resolveConfigDir(configPath) {
|
|
9774
|
+
const absolute = path2.isAbsolute(configPath) ? configPath : path2.resolve(process10.cwd(), configPath);
|
|
9775
|
+
return path2.dirname(absolute);
|
|
9776
|
+
}
|
|
9777
|
+
async function inspectHtmlSources(configPath, config) {
|
|
9778
|
+
const configDir = await resolveConfigDir(configPath);
|
|
9779
|
+
const errors = [];
|
|
9780
|
+
const results = [];
|
|
9781
|
+
for (const comp of listHtmlComponents(config)) {
|
|
9782
|
+
try {
|
|
9783
|
+
const resolved = await resolveHtmlSource(comp.props.src, comp.id, configDir);
|
|
9784
|
+
results.push({ componentId: comp.id, ...resolved });
|
|
9785
|
+
} catch (error) {
|
|
9786
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
if (errors.length) {
|
|
9790
|
+
throw new Error(errors.join(`
|
|
9791
|
+
`));
|
|
9792
|
+
}
|
|
9793
|
+
return results;
|
|
9794
|
+
}
|
|
9795
|
+
async function attachHtmlToCompiledConfig(configPath, compiled) {
|
|
9796
|
+
const configDir = await resolveConfigDir(configPath);
|
|
9797
|
+
const sources = [];
|
|
9798
|
+
for (const comp of listHtmlComponents(compiled)) {
|
|
9799
|
+
const resolved = await resolveHtmlSource(comp.props.src, comp.id, configDir);
|
|
9800
|
+
comp.props.html = await readFile2(resolved.absolutePath, "utf8");
|
|
9801
|
+
sources.push({ componentId: comp.id, ...resolved });
|
|
9802
|
+
}
|
|
9803
|
+
return sources;
|
|
9804
|
+
}
|
|
9805
|
+
|
|
9667
9806
|
// src/index.ts
|
|
9668
9807
|
var program2 = new Command;
|
|
9669
|
-
program2.name("pairit").description("CLI for Pairit experiment configs").version("0.
|
|
9808
|
+
program2.name("pairit").description("CLI for Pairit experiment configs").version("0.3.0").option("--env <name>", "Target environment: staging or production", (value) => {
|
|
9809
|
+
try {
|
|
9810
|
+
return parseEnvName(value);
|
|
9811
|
+
} catch (error) {
|
|
9812
|
+
throw new InvalidArgumentError(error.message);
|
|
9813
|
+
}
|
|
9814
|
+
}).hook("preAction", (command) => {
|
|
9815
|
+
const name = command.optsWithGlobals().env;
|
|
9816
|
+
if (name)
|
|
9817
|
+
setEnv(name);
|
|
9818
|
+
});
|
|
9670
9819
|
program2.command("login").description("Login to Pairit Manager").action(async () => {
|
|
9671
9820
|
try {
|
|
9672
9821
|
await login();
|
|
@@ -9677,15 +9826,18 @@ program2.command("login").description("Login to Pairit Manager").action(async ()
|
|
|
9677
9826
|
var configCommand = program2.command("config").description("Manage experiment configs");
|
|
9678
9827
|
configCommand.command("lint").argument("<config>", "Path to YAML config").description("Run minimal validation on a config file").action(async (configPath) => {
|
|
9679
9828
|
try {
|
|
9680
|
-
await lintConfig(configPath);
|
|
9829
|
+
const htmlFiles = await lintConfig(configPath);
|
|
9681
9830
|
console.log(`✓ ${configPath} passed lint checks`);
|
|
9831
|
+
for (const file of htmlFiles) {
|
|
9832
|
+
console.log(` html ${file.componentId} ← ${file.src} (${formatBytes(file.bytes)})`);
|
|
9833
|
+
}
|
|
9682
9834
|
} catch (error) {
|
|
9683
9835
|
if (error instanceof Error) {
|
|
9684
9836
|
console.error(`Lint failed: ${error.message}`);
|
|
9685
9837
|
} else {
|
|
9686
9838
|
console.error("Lint failed with unknown error");
|
|
9687
9839
|
}
|
|
9688
|
-
|
|
9840
|
+
process11.exitCode = 1;
|
|
9689
9841
|
}
|
|
9690
9842
|
});
|
|
9691
9843
|
configCommand.command("compile").argument("<config>", "Path to YAML config").description("Compile YAML config to canonical JSON next to source").action(async (configPath) => {
|
|
@@ -9698,21 +9850,37 @@ configCommand.command("compile").argument("<config>", "Path to YAML config").des
|
|
|
9698
9850
|
} else {
|
|
9699
9851
|
console.error("Compile failed with unknown error");
|
|
9700
9852
|
}
|
|
9701
|
-
|
|
9853
|
+
process11.exitCode = 1;
|
|
9702
9854
|
}
|
|
9703
9855
|
});
|
|
9704
|
-
configCommand.command("upload").argument("<config>", "Path to YAML config").option("--config-id <configId>", "
|
|
9856
|
+
configCommand.command("upload").argument("<config>", "Path to YAML config").option("--name <name>", "Config name (defaults to the YAML name field, then the file name)").option("--config-id <configId>", "Update an existing config by id (configs created before names)").option("--metadata <json>", "Optional metadata JSON string").option("--openai-api-key <key>", "OpenAI API key for this experiment").option("--anthropic-api-key <key>", "Anthropic API key for this experiment").description("Compile and upload config via Pairit Functions").action(async (configPath, options) => {
|
|
9705
9857
|
try {
|
|
9706
|
-
const { payload, checksum } = await buildUploadPayload(configPath, options);
|
|
9858
|
+
const { payload, checksum, attachments } = await buildUploadPayload(configPath, options);
|
|
9707
9859
|
const response = await callFunctions("/configs/upload", {
|
|
9708
9860
|
method: "POST",
|
|
9709
9861
|
body: JSON.stringify(payload),
|
|
9710
9862
|
headers: { "Content-Type": "application/json" }
|
|
9711
9863
|
});
|
|
9712
|
-
|
|
9864
|
+
for (const file of attachments) {
|
|
9865
|
+
console.log(`✓ Attached ${path3.basename(file.src)} (${formatBytes(file.bytes)})`);
|
|
9866
|
+
}
|
|
9867
|
+
const result = response;
|
|
9868
|
+
const label = result.name ? `${result.name} (${result.configId})` : result.configId;
|
|
9869
|
+
if (result.created) {
|
|
9870
|
+
console.log(`✓ Created ${label}, revision ${result.revision}`);
|
|
9871
|
+
} else if (result.previousRevision != null) {
|
|
9872
|
+
console.log(`✓ Uploaded ${label}, revision ${result.previousRevision} → ${result.revision}`);
|
|
9873
|
+
} else {
|
|
9874
|
+
console.log(`✓ Uploaded ${label}, revision ${result.revision} (config unchanged)`);
|
|
9875
|
+
}
|
|
9876
|
+
console.log(` checksum ${checksum}`);
|
|
9877
|
+
if (result.previousRevision != null && result.previousRevisionSessionCount > 0) {
|
|
9878
|
+
const n = result.previousRevisionSessionCount;
|
|
9879
|
+
console.warn(`⚠ ${n} participant${n === 1 ? "" : "s"} ran revision ${result.previousRevision}. New participants will get revision ${result.revision}.`);
|
|
9880
|
+
console.warn(" Exports record each session's configRevision. For a different study, upload under a new --name instead.");
|
|
9881
|
+
}
|
|
9713
9882
|
const labUrl = getLabUrl();
|
|
9714
|
-
console.log(`Survey Link: ${labUrl}/${
|
|
9715
|
-
console.log(JSON.stringify(response, null, 2));
|
|
9883
|
+
console.log(`Survey Link: ${labUrl}/${result.configId}`);
|
|
9716
9884
|
} catch (error) {
|
|
9717
9885
|
reportCliError("Upload failed", error);
|
|
9718
9886
|
}
|
|
@@ -9728,12 +9896,41 @@ configCommand.command("list").description("List configs from Pairit Functions").
|
|
|
9728
9896
|
configs.forEach((config) => {
|
|
9729
9897
|
const metadata = config.metadata;
|
|
9730
9898
|
const filename = metadata?.originalFilename ? ` | file=${metadata.originalFilename}` : "";
|
|
9731
|
-
|
|
9899
|
+
const name = config.name ? `name=${config.name} | ` : "";
|
|
9900
|
+
const revision = config.revision != null ? ` | revision=${config.revision}` : "";
|
|
9901
|
+
console.log(`${name}configId=${config.configId}${revision} | owner=${config.owner} | checksum=${config.checksum}${filename} | updated=${config.updatedAt ?? "n/a"}`);
|
|
9732
9902
|
});
|
|
9733
9903
|
} catch (error) {
|
|
9734
9904
|
reportCliError("List failed", error);
|
|
9735
9905
|
}
|
|
9736
9906
|
});
|
|
9907
|
+
configCommand.command("history").argument("<configId>", "Config id").option("--revision <n>", "Download one revision's compiled config").option("--out <file>", "With --revision, write the config to this file").description("List a config's revisions, or download one").action(async (configId, options) => {
|
|
9908
|
+
try {
|
|
9909
|
+
const base = `/configs/${encodeURIComponent(configId)}/revisions`;
|
|
9910
|
+
if (options.revision) {
|
|
9911
|
+
const revision = await callFunctions(`${base}/${encodeURIComponent(options.revision)}`, { method: "GET" });
|
|
9912
|
+
const json = JSON.stringify(revision.config, null, 2);
|
|
9913
|
+
if (options.out) {
|
|
9914
|
+
await writeFile2(options.out, json, "utf8");
|
|
9915
|
+
console.log(`✓ Wrote revision ${options.revision} to ${options.out}`);
|
|
9916
|
+
} else {
|
|
9917
|
+
console.log(json);
|
|
9918
|
+
}
|
|
9919
|
+
return;
|
|
9920
|
+
}
|
|
9921
|
+
const history = await callFunctions(base, { method: "GET" });
|
|
9922
|
+
if (!history.revisions.length) {
|
|
9923
|
+
console.log("No revisions recorded yet. They start with the next upload.");
|
|
9924
|
+
return;
|
|
9925
|
+
}
|
|
9926
|
+
for (const r of history.revisions) {
|
|
9927
|
+
const current = r.revision === history.currentRevision ? " (current)" : "";
|
|
9928
|
+
console.log(`revision=${r.revision}${current} | sessions=${r.sessionCount} | uploaded=${r.createdAt ?? "n/a"} | checksum=${r.checksum ?? "n/a"}`);
|
|
9929
|
+
}
|
|
9930
|
+
} catch (error) {
|
|
9931
|
+
reportCliError("History failed", error);
|
|
9932
|
+
}
|
|
9933
|
+
});
|
|
9737
9934
|
configCommand.command("delete").argument("<configId>", "Config id to delete").option("--force", "Skip confirmation").description("Delete config via Pairit Functions").action(async (configId, options) => {
|
|
9738
9935
|
try {
|
|
9739
9936
|
if (!options.force) {
|
|
@@ -9868,9 +10065,9 @@ adminCommand.command("list-users").description("List users on the manager allowl
|
|
|
9868
10065
|
reportCliError("List users failed", error);
|
|
9869
10066
|
}
|
|
9870
10067
|
});
|
|
9871
|
-
program2.parseAsync(
|
|
10068
|
+
program2.parseAsync(process11.argv).catch((err) => {
|
|
9872
10069
|
console.error(err);
|
|
9873
|
-
|
|
10070
|
+
process11.exit(1);
|
|
9874
10071
|
});
|
|
9875
10072
|
async function lintConfig(configPath) {
|
|
9876
10073
|
const config = await loadConfig(configPath);
|
|
@@ -9878,6 +10075,9 @@ async function lintConfig(configPath) {
|
|
|
9878
10075
|
if (!config.schema_version) {
|
|
9879
10076
|
errors.push("missing schema_version");
|
|
9880
10077
|
}
|
|
10078
|
+
if (config.name !== undefined && (typeof config.name !== "string" || !config.name.trim())) {
|
|
10079
|
+
errors.push("name must be a non-empty string");
|
|
10080
|
+
}
|
|
9881
10081
|
if (!config.initialPageId) {
|
|
9882
10082
|
errors.push("missing initialPageId");
|
|
9883
10083
|
}
|
|
@@ -9894,6 +10094,7 @@ async function lintConfig(configPath) {
|
|
|
9894
10094
|
if (errors.length) {
|
|
9895
10095
|
throw new Error(errors.join(", "));
|
|
9896
10096
|
}
|
|
10097
|
+
return inspectHtmlSources(configPath, config);
|
|
9897
10098
|
}
|
|
9898
10099
|
async function compileConfig(configPath) {
|
|
9899
10100
|
const config = await loadConfig(configPath);
|
|
@@ -9945,7 +10146,7 @@ async function compileConfig(configPath) {
|
|
|
9945
10146
|
output.matchmaking = config.matchmaking;
|
|
9946
10147
|
}
|
|
9947
10148
|
const resolvedPath = await resolvePath(configPath);
|
|
9948
|
-
const outPath =
|
|
10149
|
+
const outPath = path3.join(path3.dirname(resolvedPath), `${path3.basename(resolvedPath, path3.extname(resolvedPath))}.json`);
|
|
9949
10150
|
await writeFile2(outPath, JSON.stringify(output, null, 2), "utf8");
|
|
9950
10151
|
return outPath;
|
|
9951
10152
|
}
|
|
@@ -9954,14 +10155,16 @@ async function buildUploadPayload(configPath, options) {
|
|
|
9954
10155
|
const allowRetake = sourceConfig.allowRetake === true;
|
|
9955
10156
|
const requireAuth = sourceConfig.requireAuth;
|
|
9956
10157
|
const compiledPath = await compileConfig(configPath);
|
|
9957
|
-
const compiledContent = await
|
|
9958
|
-
const hashBuffer = createHash("sha256").update(compiledContent).digest();
|
|
9959
|
-
const checksum = hashBuffer.toString("hex");
|
|
10158
|
+
const compiledContent = await readFile3(compiledPath, "utf8");
|
|
9960
10159
|
const parsed = JSON.parse(compiledContent);
|
|
9961
|
-
const
|
|
10160
|
+
const attachments = await attachHtmlToCompiledConfig(configPath, parsed);
|
|
10161
|
+
const hashBuffer = createHash("sha256").update(JSON.stringify(parsed)).digest();
|
|
10162
|
+
const checksum = hashBuffer.toString("hex");
|
|
10163
|
+
const explicitName = options.name ?? sourceConfig.name;
|
|
10164
|
+
const name = options.configId ? explicitName : explicitName ?? path3.basename(configPath, path3.extname(configPath));
|
|
9962
10165
|
const metadata = options.metadata ? JSON.parse(options.metadata) : {};
|
|
9963
10166
|
if (!metadata.originalFilename) {
|
|
9964
|
-
metadata.originalFilename =
|
|
10167
|
+
metadata.originalFilename = path3.basename(configPath);
|
|
9965
10168
|
}
|
|
9966
10169
|
const llmCredentials = {
|
|
9967
10170
|
...options.openaiApiKey && { openaiApiKey: options.openaiApiKey },
|
|
@@ -9971,7 +10174,8 @@ async function buildUploadPayload(configPath, options) {
|
|
|
9971
10174
|
};
|
|
9972
10175
|
return {
|
|
9973
10176
|
payload: {
|
|
9974
|
-
|
|
10177
|
+
...name && { name },
|
|
10178
|
+
...options.configId && { configId: options.configId },
|
|
9975
10179
|
checksum,
|
|
9976
10180
|
metadata: metadata ?? null,
|
|
9977
10181
|
config: parsed,
|
|
@@ -9979,15 +10183,16 @@ async function buildUploadPayload(configPath, options) {
|
|
|
9979
10183
|
allowRetake,
|
|
9980
10184
|
...requireAuth !== undefined && { requireAuth }
|
|
9981
10185
|
},
|
|
9982
|
-
checksum
|
|
10186
|
+
checksum,
|
|
10187
|
+
attachments
|
|
9983
10188
|
};
|
|
9984
10189
|
}
|
|
9985
10190
|
async function uploadMedia(filePath, options) {
|
|
9986
10191
|
const resolved = await resolvePath(filePath);
|
|
9987
|
-
const fileBuffer = await
|
|
10192
|
+
const fileBuffer = await readFile3(resolved);
|
|
9988
10193
|
const hashBuffer = createHash("sha256").update(fileBuffer).digest();
|
|
9989
10194
|
const checksum = hashBuffer.toString("hex");
|
|
9990
|
-
const defaultObject = `${toBase64Url(hashBuffer.subarray(0, 12))}${
|
|
10195
|
+
const defaultObject = `${toBase64Url(hashBuffer.subarray(0, 12))}${path3.extname(resolved)}`;
|
|
9991
10196
|
const object = options.object ?? defaultObject;
|
|
9992
10197
|
const shouldBePublic = typeof options.public === "boolean" ? options.public : true;
|
|
9993
10198
|
const maxInlineBytes = getInlineMediaLimit();
|
|
@@ -10056,7 +10261,7 @@ function toBase64Url(buffer) {
|
|
|
10056
10261
|
}
|
|
10057
10262
|
async function loadConfig(configPath) {
|
|
10058
10263
|
const resolvedPath = await resolvePath(configPath);
|
|
10059
|
-
const content = await
|
|
10264
|
+
const content = await readFile3(resolvedPath, "utf8");
|
|
10060
10265
|
const parsed = import_yaml.default.parse(content);
|
|
10061
10266
|
if (!parsed || typeof parsed !== "object") {
|
|
10062
10267
|
throw new Error("config must be an object");
|
|
@@ -10064,9 +10269,9 @@ async function loadConfig(configPath) {
|
|
|
10064
10269
|
return parsed;
|
|
10065
10270
|
}
|
|
10066
10271
|
async function resolvePath(configPath) {
|
|
10067
|
-
const cwd =
|
|
10068
|
-
const absolute =
|
|
10069
|
-
await
|
|
10272
|
+
const cwd = process11.cwd();
|
|
10273
|
+
const absolute = path3.isAbsolute(configPath) ? configPath : path3.resolve(cwd, configPath);
|
|
10274
|
+
await stat2(absolute);
|
|
10070
10275
|
return absolute;
|
|
10071
10276
|
}
|
|
10072
10277
|
async function fetchAllPages(pathname, extract) {
|
|
@@ -10083,7 +10288,7 @@ async function fetchAllPages(pathname, extract) {
|
|
|
10083
10288
|
return all;
|
|
10084
10289
|
}
|
|
10085
10290
|
async function callFunctions(pathname, init = {}) {
|
|
10086
|
-
const baseUrl =
|
|
10291
|
+
const baseUrl = getApiUrl();
|
|
10087
10292
|
const url = new URL(pathname, baseUrl).toString();
|
|
10088
10293
|
const authHeaders = await getAuthHeaders();
|
|
10089
10294
|
const headers = {
|
|
@@ -10104,33 +10309,21 @@ async function callFunctions(pathname, init = {}) {
|
|
|
10104
10309
|
throw new Error(`Invalid JSON response: ${text}`);
|
|
10105
10310
|
}
|
|
10106
10311
|
}
|
|
10107
|
-
function getFunctionsBaseUrl() {
|
|
10108
|
-
if (process9.env.PAIRIT_API_URL) {
|
|
10109
|
-
return process9.env.PAIRIT_API_URL;
|
|
10110
|
-
}
|
|
10111
|
-
return "https://manager-432501290611.us-central1.run.app";
|
|
10112
|
-
}
|
|
10113
|
-
function getLabUrl() {
|
|
10114
|
-
if (process9.env.PAIRIT_LAB_URL) {
|
|
10115
|
-
return process9.env.PAIRIT_LAB_URL;
|
|
10116
|
-
}
|
|
10117
|
-
return "https://lab-432501290611.us-central1.run.app";
|
|
10118
|
-
}
|
|
10119
10312
|
function getInlineMediaLimit() {
|
|
10120
|
-
const fromEnv = Number(
|
|
10313
|
+
const fromEnv = Number(process11.env.PAIRIT_MAX_INLINE_MEDIA_BYTES);
|
|
10121
10314
|
if (Number.isFinite(fromEnv) && fromEnv > 0)
|
|
10122
10315
|
return fromEnv;
|
|
10123
10316
|
return 5 * 1024 * 1024;
|
|
10124
10317
|
}
|
|
10125
10318
|
async function promptConfirm(prompt2) {
|
|
10126
|
-
|
|
10319
|
+
process11.stdout.write(`${prompt2} `);
|
|
10127
10320
|
return new Promise((resolve) => {
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10321
|
+
process11.stdin.setEncoding("utf8");
|
|
10322
|
+
process11.stdin.resume();
|
|
10323
|
+
process11.stdin.once("data", (data) => {
|
|
10131
10324
|
const input = data.toString().trim().toLowerCase();
|
|
10132
10325
|
resolve(input === "y" || input === "yes");
|
|
10133
|
-
|
|
10326
|
+
process11.stdin.pause();
|
|
10134
10327
|
});
|
|
10135
10328
|
});
|
|
10136
10329
|
}
|
|
@@ -10140,20 +10333,20 @@ function reportCliError(prefix, error) {
|
|
|
10140
10333
|
} else {
|
|
10141
10334
|
console.error(`${prefix}: unknown error`);
|
|
10142
10335
|
}
|
|
10143
|
-
|
|
10336
|
+
process11.exitCode = 1;
|
|
10144
10337
|
}
|
|
10145
10338
|
async function exportData(configId, options) {
|
|
10146
10339
|
const format = options.format;
|
|
10147
|
-
const outDir =
|
|
10340
|
+
const outDir = path3.resolve(process11.cwd(), options.out);
|
|
10148
10341
|
await mkdir2(outDir, { recursive: true });
|
|
10149
10342
|
console.log(`Exporting data for ${configId} in ${format} format...`);
|
|
10150
10343
|
const cfg = encodeURIComponent(configId);
|
|
10151
|
-
await fetchAndWriteExport(`/data/${cfg}/sessions`,
|
|
10152
|
-
await fetchAndWriteExport(`/data/${cfg}/events`,
|
|
10153
|
-
await fetchAndWriteExport(`/data/${cfg}/chat-messages`,
|
|
10154
|
-
await fetchAndWriteExport(`/data/${cfg}/groups`,
|
|
10155
|
-
await fetchAndWriteExport(`/data/${cfg}/survey-responses`,
|
|
10156
|
-
await fetchAndWriteExport(`/data/${cfg}/workspace-documents`,
|
|
10344
|
+
await fetchAndWriteExport(`/data/${cfg}/sessions`, path3.join(outDir, `${configId}-sessions`), (r) => r.sessions ?? [], format, flattenSession, "sessions");
|
|
10345
|
+
await fetchAndWriteExport(`/data/${cfg}/events`, path3.join(outDir, `${configId}-events`), (r) => r.events ?? [], format, flattenEvent, "events");
|
|
10346
|
+
await fetchAndWriteExport(`/data/${cfg}/chat-messages`, path3.join(outDir, `${configId}-chat-messages`), (r) => r.messages ?? [], format, (msg) => msg, "chat messages");
|
|
10347
|
+
await fetchAndWriteExport(`/data/${cfg}/groups`, path3.join(outDir, `${configId}-groups`), (r) => r.groups ?? [], format, (group) => group, "groups");
|
|
10348
|
+
await fetchAndWriteExport(`/data/${cfg}/survey-responses`, path3.join(outDir, `${configId}-survey-responses`), (r) => r.surveyResponses ?? [], format, flattenSurveyResponse, "survey responses");
|
|
10349
|
+
await fetchAndWriteExport(`/data/${cfg}/workspace-documents`, path3.join(outDir, `${configId}-workspace-documents`), (r) => r.workspaceDocuments ?? [], format, flattenWorkspaceDocument, "workspace documents");
|
|
10157
10350
|
console.log(`
|
|
10158
10351
|
Files written to: ${outDir}`);
|
|
10159
10352
|
}
|
|
@@ -10275,7 +10468,7 @@ function escapeCsvValue(value) {
|
|
|
10275
10468
|
return str;
|
|
10276
10469
|
}
|
|
10277
10470
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
10278
|
-
var __dirname3 =
|
|
10471
|
+
var __dirname3 = path3.dirname(__filename2);
|
|
10279
10472
|
export {
|
|
10280
10473
|
__filename2 as __filename,
|
|
10281
10474
|
__dirname3 as __dirname
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pairit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for the Pairit behavioral science experiment platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"url": "https://github.com/pairium/pairit.git",
|
|
26
26
|
"directory": "apps/manager/cli"
|
|
27
27
|
},
|
|
28
|
-
"homepage": "https://pairium.
|
|
28
|
+
"homepage": "https://docs.pairium.ai/pairit/",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"optionalDependencies": {
|
|
31
31
|
"keytar": "^7.9.0"
|