pairit 0.2.0 → 0.3.1
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 +7 -7
- package/dist/index.js +65 -8
- package/package.json +40 -40
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
|
|
|
@@ -66,8 +66,8 @@ Add `--private` if you need to keep an object private. Use `--bucket <name>` onl
|
|
|
66
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:
|
package/dist/index.js
CHANGED
|
@@ -9513,9 +9513,13 @@ function getEnvName() {
|
|
|
9513
9513
|
return fromEnv ? parseEnvName(fromEnv) : "production";
|
|
9514
9514
|
}
|
|
9515
9515
|
function getApiUrl() {
|
|
9516
|
+
if (selected)
|
|
9517
|
+
return ENVIRONMENTS[selected].api;
|
|
9516
9518
|
return process2.env.PAIRIT_API_URL || ENVIRONMENTS[getEnvName()].api;
|
|
9517
9519
|
}
|
|
9518
9520
|
function getLabUrl() {
|
|
9521
|
+
if (selected)
|
|
9522
|
+
return ENVIRONMENTS[selected].lab;
|
|
9519
9523
|
return process2.env.PAIRIT_LAB_URL || ENVIRONMENTS[getEnvName()].lab;
|
|
9520
9524
|
}
|
|
9521
9525
|
function getCredentialKey() {
|
|
@@ -9805,7 +9809,7 @@ async function attachHtmlToCompiledConfig(configPath, compiled) {
|
|
|
9805
9809
|
|
|
9806
9810
|
// src/index.ts
|
|
9807
9811
|
var program2 = new Command;
|
|
9808
|
-
program2.name("pairit").description("CLI for Pairit experiment configs").version("0.
|
|
9812
|
+
program2.name("pairit").description("CLI for Pairit experiment configs").version("0.3.1").option("--env <name>", "Target environment: staging or production", (value) => {
|
|
9809
9813
|
try {
|
|
9810
9814
|
return parseEnvName(value);
|
|
9811
9815
|
} catch (error) {
|
|
@@ -9853,7 +9857,7 @@ configCommand.command("compile").argument("<config>", "Path to YAML config").des
|
|
|
9853
9857
|
process11.exitCode = 1;
|
|
9854
9858
|
}
|
|
9855
9859
|
});
|
|
9856
|
-
configCommand.command("upload").argument("<config>", "Path to YAML config").option("--config-id <configId>", "
|
|
9860
|
+
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) => {
|
|
9857
9861
|
try {
|
|
9858
9862
|
const { payload, checksum, attachments } = await buildUploadPayload(configPath, options);
|
|
9859
9863
|
const response = await callFunctions("/configs/upload", {
|
|
@@ -9864,10 +9868,23 @@ configCommand.command("upload").argument("<config>", "Path to YAML config").opti
|
|
|
9864
9868
|
for (const file of attachments) {
|
|
9865
9869
|
console.log(`✓ Attached ${path3.basename(file.src)} (${formatBytes(file.bytes)})`);
|
|
9866
9870
|
}
|
|
9867
|
-
|
|
9871
|
+
const result = response;
|
|
9872
|
+
const label = result.name ? `${result.name} (${result.configId})` : result.configId;
|
|
9873
|
+
if (result.created) {
|
|
9874
|
+
console.log(`✓ Created ${label}, revision ${result.revision}`);
|
|
9875
|
+
} else if (result.previousRevision != null) {
|
|
9876
|
+
console.log(`✓ Uploaded ${label}, revision ${result.previousRevision} → ${result.revision}`);
|
|
9877
|
+
} else {
|
|
9878
|
+
console.log(`✓ Uploaded ${label}, revision ${result.revision} (config unchanged)`);
|
|
9879
|
+
}
|
|
9880
|
+
console.log(` checksum ${checksum}`);
|
|
9881
|
+
if (result.previousRevision != null && result.previousRevisionSessionCount > 0) {
|
|
9882
|
+
const n = result.previousRevisionSessionCount;
|
|
9883
|
+
console.warn(`⚠ ${n} participant${n === 1 ? "" : "s"} ran revision ${result.previousRevision}. New participants will get revision ${result.revision}.`);
|
|
9884
|
+
console.warn(" Exports record each session's configRevision. For a different study, upload under a new --name instead.");
|
|
9885
|
+
}
|
|
9868
9886
|
const labUrl = getLabUrl();
|
|
9869
|
-
console.log(`Survey Link: ${labUrl}/${
|
|
9870
|
-
console.log(JSON.stringify(response, null, 2));
|
|
9887
|
+
console.log(`Survey Link: ${labUrl}/${result.configId}`);
|
|
9871
9888
|
} catch (error) {
|
|
9872
9889
|
reportCliError("Upload failed", error);
|
|
9873
9890
|
}
|
|
@@ -9883,12 +9900,41 @@ configCommand.command("list").description("List configs from Pairit Functions").
|
|
|
9883
9900
|
configs.forEach((config) => {
|
|
9884
9901
|
const metadata = config.metadata;
|
|
9885
9902
|
const filename = metadata?.originalFilename ? ` | file=${metadata.originalFilename}` : "";
|
|
9886
|
-
|
|
9903
|
+
const name = config.name ? `name=${config.name} | ` : "";
|
|
9904
|
+
const revision = config.revision != null ? ` | revision=${config.revision}` : "";
|
|
9905
|
+
console.log(`${name}configId=${config.configId}${revision} | owner=${config.owner} | checksum=${config.checksum}${filename} | updated=${config.updatedAt ?? "n/a"}`);
|
|
9887
9906
|
});
|
|
9888
9907
|
} catch (error) {
|
|
9889
9908
|
reportCliError("List failed", error);
|
|
9890
9909
|
}
|
|
9891
9910
|
});
|
|
9911
|
+
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) => {
|
|
9912
|
+
try {
|
|
9913
|
+
const base = `/configs/${encodeURIComponent(configId)}/revisions`;
|
|
9914
|
+
if (options.revision) {
|
|
9915
|
+
const revision = await callFunctions(`${base}/${encodeURIComponent(options.revision)}`, { method: "GET" });
|
|
9916
|
+
const json = JSON.stringify(revision.config, null, 2);
|
|
9917
|
+
if (options.out) {
|
|
9918
|
+
await writeFile2(options.out, json, "utf8");
|
|
9919
|
+
console.log(`✓ Wrote revision ${options.revision} to ${options.out}`);
|
|
9920
|
+
} else {
|
|
9921
|
+
console.log(json);
|
|
9922
|
+
}
|
|
9923
|
+
return;
|
|
9924
|
+
}
|
|
9925
|
+
const history = await callFunctions(base, { method: "GET" });
|
|
9926
|
+
if (!history.revisions.length) {
|
|
9927
|
+
console.log("No revisions recorded yet. They start with the next upload.");
|
|
9928
|
+
return;
|
|
9929
|
+
}
|
|
9930
|
+
for (const r of history.revisions) {
|
|
9931
|
+
const current = r.revision === history.currentRevision ? " (current)" : "";
|
|
9932
|
+
console.log(`revision=${r.revision}${current} | sessions=${r.sessionCount} | uploaded=${r.createdAt ?? "n/a"} | checksum=${r.checksum ?? "n/a"}`);
|
|
9933
|
+
}
|
|
9934
|
+
} catch (error) {
|
|
9935
|
+
reportCliError("History failed", error);
|
|
9936
|
+
}
|
|
9937
|
+
});
|
|
9892
9938
|
configCommand.command("delete").argument("<configId>", "Config id to delete").option("--force", "Skip confirmation").description("Delete config via Pairit Functions").action(async (configId, options) => {
|
|
9893
9939
|
try {
|
|
9894
9940
|
if (!options.force) {
|
|
@@ -10033,6 +10079,9 @@ async function lintConfig(configPath) {
|
|
|
10033
10079
|
if (!config.schema_version) {
|
|
10034
10080
|
errors.push("missing schema_version");
|
|
10035
10081
|
}
|
|
10082
|
+
if (config.name !== undefined && (typeof config.name !== "string" || !config.name.trim())) {
|
|
10083
|
+
errors.push("name must be a non-empty string");
|
|
10084
|
+
}
|
|
10036
10085
|
if (!config.initialPageId) {
|
|
10037
10086
|
errors.push("missing initialPageId");
|
|
10038
10087
|
}
|
|
@@ -10044,6 +10093,12 @@ async function lintConfig(configPath) {
|
|
|
10044
10093
|
if (!page.id) {
|
|
10045
10094
|
errors.push(`pages[${index}] is missing id`);
|
|
10046
10095
|
}
|
|
10096
|
+
page.components?.forEach((component, componentIndex) => {
|
|
10097
|
+
const column = component?.column;
|
|
10098
|
+
if (column !== undefined && column !== "left" && column !== "right") {
|
|
10099
|
+
errors.push(`pages[${index}].components[${componentIndex}].column must be "left" or "right"`);
|
|
10100
|
+
}
|
|
10101
|
+
});
|
|
10047
10102
|
});
|
|
10048
10103
|
}
|
|
10049
10104
|
if (errors.length) {
|
|
@@ -10115,7 +10170,8 @@ async function buildUploadPayload(configPath, options) {
|
|
|
10115
10170
|
const attachments = await attachHtmlToCompiledConfig(configPath, parsed);
|
|
10116
10171
|
const hashBuffer = createHash("sha256").update(JSON.stringify(parsed)).digest();
|
|
10117
10172
|
const checksum = hashBuffer.toString("hex");
|
|
10118
|
-
const
|
|
10173
|
+
const explicitName = options.name ?? sourceConfig.name;
|
|
10174
|
+
const name = options.configId ? explicitName : explicitName ?? path3.basename(configPath, path3.extname(configPath));
|
|
10119
10175
|
const metadata = options.metadata ? JSON.parse(options.metadata) : {};
|
|
10120
10176
|
if (!metadata.originalFilename) {
|
|
10121
10177
|
metadata.originalFilename = path3.basename(configPath);
|
|
@@ -10128,7 +10184,8 @@ async function buildUploadPayload(configPath, options) {
|
|
|
10128
10184
|
};
|
|
10129
10185
|
return {
|
|
10130
10186
|
payload: {
|
|
10131
|
-
|
|
10187
|
+
...name && { name },
|
|
10188
|
+
...options.configId && { configId: options.configId },
|
|
10132
10189
|
checksum,
|
|
10133
10190
|
metadata: metadata ?? null,
|
|
10134
10191
|
config: parsed,
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
2
|
+
"name": "pairit",
|
|
3
|
+
"version": "0.3.1",
|
|
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": "git+https://github.com/pairium/pairit.git",
|
|
26
|
+
"directory": "apps/manager/cli"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://docs.pairium.ai/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
|
+
"open": "^11.0.0",
|
|
39
|
+
"typescript": "^5.9.3",
|
|
40
|
+
"yaml": "^2.6.0"
|
|
41
|
+
}
|
|
42
42
|
}
|