@eide/foir-cli 0.14.1 → 0.15.2

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/cli.js +141 -12
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -2099,8 +2099,10 @@ import {
2099
2099
  ForceAssignExperimentRequestSchema,
2100
2100
  RemoveExperimentAssignmentRequestSchema,
2101
2101
  ApplyExperimentWinnerRequestSchema,
2102
- GetCustomerAssignmentsRequestSchema,
2103
- ExperimentVariantSchema
2102
+ GetAssignmentsRequestSchema,
2103
+ ListExperimentDeclarationsRequestSchema,
2104
+ ExperimentVariantSchema,
2105
+ ExperimentGoalSchema
2104
2106
  } from "@eide/foir-proto-ts/experiments/v1/experiments_pb";
2105
2107
  import { ExperimentStatus as ExperimentStatus2 } from "@eide/foir-proto-ts/experiments/v1/experiments_pb";
2106
2108
  var STATUS_TO_PROTO = {
@@ -2156,6 +2158,14 @@ function createExperimentsMethods(client) {
2156
2158
  name: v.name,
2157
2159
  percent: v.percent
2158
2160
  })
2161
+ ) ?? [],
2162
+ assignmentPersistence: params.assignmentPersistence,
2163
+ goals: params.goals?.map(
2164
+ (g) => create7(ExperimentGoalSchema, {
2165
+ key: g.key,
2166
+ name: g.name,
2167
+ description: g.description
2168
+ })
2159
2169
  ) ?? []
2160
2170
  })
2161
2171
  );
@@ -2175,7 +2185,16 @@ function createExperimentsMethods(client) {
2175
2185
  name: v.name,
2176
2186
  percent: v.percent
2177
2187
  })
2178
- )
2188
+ ),
2189
+ assignmentPersistence: params.assignmentPersistence,
2190
+ goals: params.goals?.map(
2191
+ (g) => create7(ExperimentGoalSchema, {
2192
+ key: g.key,
2193
+ name: g.name,
2194
+ description: g.description
2195
+ })
2196
+ ) ?? [],
2197
+ goalsClear: params.goalsClear ?? false
2179
2198
  })
2180
2199
  );
2181
2200
  return resp.experiment ?? null;
@@ -2221,30 +2240,41 @@ function createExperimentsMethods(client) {
2221
2240
  return resp.experiment ?? null;
2222
2241
  },
2223
2242
  // ── Assignments ──────────────────────────────────────────
2224
- async forceAssignExperiment(customerId, experimentId, variantKey) {
2243
+ async forceAssignExperiment(identity, experimentId, variantKey) {
2225
2244
  const resp = await client.forceAssignExperiment(
2226
2245
  create7(ForceAssignExperimentRequestSchema, {
2227
- customerId,
2246
+ customerId: identity.customerId,
2247
+ viewerKey: identity.viewerKey,
2228
2248
  experimentId,
2229
2249
  variantKey
2230
2250
  })
2231
2251
  );
2232
2252
  return resp.assignment ?? null;
2233
2253
  },
2234
- async removeExperimentAssignment(customerId, experimentId) {
2254
+ async removeExperimentAssignment(identity, experimentId) {
2235
2255
  const resp = await client.removeExperimentAssignment(
2236
2256
  create7(RemoveExperimentAssignmentRequestSchema, {
2237
- customerId,
2257
+ customerId: identity.customerId,
2258
+ viewerKey: identity.viewerKey,
2238
2259
  experimentId
2239
2260
  })
2240
2261
  );
2241
2262
  return resp.success;
2242
2263
  },
2243
- async getCustomerAssignments(customerId) {
2244
- const resp = await client.getCustomerAssignments(
2245
- create7(GetCustomerAssignmentsRequestSchema, { customerId })
2264
+ async getAssignments(identity) {
2265
+ const resp = await client.getAssignments(
2266
+ create7(GetAssignmentsRequestSchema, {
2267
+ customerId: identity.customerId,
2268
+ viewerKey: identity.viewerKey
2269
+ })
2246
2270
  );
2247
2271
  return resp.assignments ?? [];
2272
+ },
2273
+ async listExperimentDeclarations() {
2274
+ const resp = await client.listExperimentDeclarations(
2275
+ create7(ListExperimentDeclarationsRequestSchema, {})
2276
+ );
2277
+ return resp.declarations ?? [];
2248
2278
  }
2249
2279
  };
2250
2280
  }
@@ -3747,7 +3777,7 @@ async function parseInputData(opts) {
3747
3777
  function isUUID(value) {
3748
3778
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
3749
3779
  value
3750
- ) || /^c[a-z0-9]{24,}$/.test(value);
3780
+ ) || /^[a-z][a-z0-9]{23,31}$/.test(value);
3751
3781
  }
3752
3782
  function parseFilters(filterStr) {
3753
3783
  if (!filterStr) return [];
@@ -7188,7 +7218,8 @@ function registerSegmentsCommands(program2, globalOpts) {
7188
7218
  // src/commands/experiments.ts
7189
7219
  import {
7190
7220
  ExperimentSchema,
7191
- ExperimentStatsSchema
7221
+ ExperimentStatsSchema,
7222
+ ExperimentAssignmentSchema
7192
7223
  } from "@eide/foir-proto-ts/experiments/v1/experiments_pb";
7193
7224
  function registerExperimentsCommands(program2, globalOpts) {
7194
7225
  const experiments = program2.command("experiments").description("Manage experiments");
@@ -7336,6 +7367,104 @@ function registerExperimentsCommands(program2, globalOpts) {
7336
7367
  formatOutputProto(ExperimentStatsSchema, result, opts);
7337
7368
  })
7338
7369
  );
7370
+ experiments.command("force-assign <experimentId>").description(
7371
+ "Force a known identity into a specific variant (admin override)."
7372
+ ).requiredOption("--variant <key>", "Variant key to assign").option("--customer-id <id>", "Customer identity to bucket").option(
7373
+ "--viewer-key <key>",
7374
+ "Anonymous viewer identity (cookie/localStorage value) to bucket"
7375
+ ).action(
7376
+ withErrorHandler(
7377
+ globalOpts,
7378
+ async (experimentId, cmdOpts) => {
7379
+ const opts = globalOpts();
7380
+ if (!cmdOpts.customerId && !cmdOpts.viewerKey) {
7381
+ throw new Error(
7382
+ "Provide exactly one of --customer-id or --viewer-key."
7383
+ );
7384
+ }
7385
+ if (cmdOpts.customerId && cmdOpts.viewerKey) {
7386
+ throw new Error(
7387
+ "Provide exactly one of --customer-id or --viewer-key, not both."
7388
+ );
7389
+ }
7390
+ const client = await createPlatformClient(opts);
7391
+ const result = await client.experiments.forceAssignExperiment(
7392
+ { customerId: cmdOpts.customerId, viewerKey: cmdOpts.viewerKey },
7393
+ experimentId,
7394
+ cmdOpts.variant
7395
+ );
7396
+ if (!result) throw new Error("Force-assign returned no assignment.");
7397
+ formatOutputProto(ExperimentAssignmentSchema, result, opts);
7398
+ if (!(opts.json || opts.jsonl || opts.quiet))
7399
+ success(
7400
+ `Assigned ${cmdOpts.customerId ? "customer" : "viewer"} ${cmdOpts.customerId ?? cmdOpts.viewerKey} \u2192 ${cmdOpts.variant}`
7401
+ );
7402
+ }
7403
+ )
7404
+ );
7405
+ experiments.command("remove-assignment <experimentId>").description("Remove the assignment for a known identity.").option("--customer-id <id>", "Customer identity to clear").option("--viewer-key <key>", "Anonymous viewer identity to clear").action(
7406
+ withErrorHandler(
7407
+ globalOpts,
7408
+ async (experimentId, cmdOpts) => {
7409
+ const opts = globalOpts();
7410
+ if (!cmdOpts.customerId && !cmdOpts.viewerKey) {
7411
+ throw new Error(
7412
+ "Provide exactly one of --customer-id or --viewer-key."
7413
+ );
7414
+ }
7415
+ if (cmdOpts.customerId && cmdOpts.viewerKey) {
7416
+ throw new Error(
7417
+ "Provide exactly one of --customer-id or --viewer-key, not both."
7418
+ );
7419
+ }
7420
+ const client = await createPlatformClient(opts);
7421
+ const ok = await client.experiments.removeExperimentAssignment(
7422
+ { customerId: cmdOpts.customerId, viewerKey: cmdOpts.viewerKey },
7423
+ experimentId
7424
+ );
7425
+ if (opts.json || opts.jsonl)
7426
+ formatOutput({ removed: ok, experimentId }, opts);
7427
+ else if (ok) success("Assignment removed");
7428
+ else success("No matching assignment found");
7429
+ }
7430
+ )
7431
+ );
7432
+ experiments.command("assignments").description("List assignments for a single identity across experiments.").option("--customer-id <id>", "Customer identity to look up").option("--viewer-key <key>", "Anonymous viewer identity to look up").action(
7433
+ withErrorHandler(
7434
+ globalOpts,
7435
+ async (cmdOpts) => {
7436
+ const opts = globalOpts();
7437
+ if (!cmdOpts.customerId && !cmdOpts.viewerKey) {
7438
+ throw new Error(
7439
+ "Provide exactly one of --customer-id or --viewer-key."
7440
+ );
7441
+ }
7442
+ if (cmdOpts.customerId && cmdOpts.viewerKey) {
7443
+ throw new Error(
7444
+ "Provide exactly one of --customer-id or --viewer-key, not both."
7445
+ );
7446
+ }
7447
+ const client = await createPlatformClient(opts);
7448
+ const data = await client.experiments.getAssignments({
7449
+ customerId: cmdOpts.customerId,
7450
+ viewerKey: cmdOpts.viewerKey
7451
+ });
7452
+ formatListProto(ExperimentAssignmentSchema, data, opts, {
7453
+ columns: [
7454
+ { key: "experimentKey", header: "Experiment", width: 24 },
7455
+ { key: "variantKey", header: "Variant", width: 16 },
7456
+ { key: "assignmentMethod", header: "Method", width: 10 },
7457
+ {
7458
+ key: "assignedAt",
7459
+ header: "Assigned",
7460
+ width: 12,
7461
+ format: (v) => timeAgo(v)
7462
+ }
7463
+ ]
7464
+ });
7465
+ }
7466
+ )
7467
+ );
7339
7468
  }
7340
7469
 
7341
7470
  // src/commands/schedules.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eide/foir-cli",
3
- "version": "0.14.1",
3
+ "version": "0.15.2",
4
4
  "description": "Universal platform CLI for Foir platform",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -50,7 +50,7 @@
50
50
  "@bufbuild/protovalidate": "^1.1.1",
51
51
  "@connectrpc/connect": "^2.0.0",
52
52
  "@connectrpc/connect-node": "^2.0.0",
53
- "@eide/foir-proto-ts": "^0.40.0",
53
+ "@eide/foir-proto-ts": "^0.43.0",
54
54
  "chalk": "^5.3.0",
55
55
  "commander": "^12.1.0",
56
56
  "dotenv": "^16.4.5",