@go-labs-sg/gosh 1.2.0 → 1.4.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 CHANGED
@@ -246,16 +246,29 @@ and missions stay editable after play starts: each edit re-syncs every started
246
246
  team's saved progress in the same transaction. Played stations keep their
247
247
  progress and order, while unplayed stations follow the new route. An edit that
248
248
  would remove the station a team is in the middle of fails with a conflict and
249
- changes nothing. A started team's group assignment, team creation, play mode
250
- and progression settings stay locked. A group shares its ordered station route,
251
- while each team keeps independent progress through that route.
249
+ changes nothing. A started team's group assignment, play mode and progression
250
+ settings stay locked. A group shares its ordered station route, while each team
251
+ keeps independent progress through that route.
252
+
253
+ Late teams can join a live group-routed event. `team create` must then include
254
+ a `groupId`, so the team starts on that group's route behind its release gate
255
+ (a held or scheduled group stays closed to it). Restoring an archived team
256
+ while group routing is live works only if the team never played and still has
257
+ a group. Without group routing, team creation still locks once play starts.
258
+
259
+ ```bash
260
+ gosh team create \
261
+ --input '{"eventId":"event-id","name":"Walk-in team","groupId":"group-id"}' \
262
+ --target event-id \
263
+ --allow-state-change
264
+ ```
252
265
 
253
266
  Configure gameplay, then manage the event's routing groups and their complete
254
267
  station lists:
255
268
 
256
269
  ```bash
257
270
  gosh event set-gameplay \
258
- --input '{"eventId":"event-id","progression":"SEQUENTIAL","groupRoutingEnabled":true,"allowMissionSkipping":true}' \
271
+ --input '{"eventId":"event-id","progression":"SEQUENTIAL","groupRoutingEnabled":true,"allowMissionSkipping":true,"allowStationSkipping":false}' \
259
272
  --target event-id \
260
273
  --allow-state-change
261
274
 
@@ -368,6 +381,46 @@ finish time are kept, and a finished station accepts the reopened mission
368
381
  until it is resolved. Pending submissions must be reviewed first, and a
369
382
  reopened mission that still holds earlier points cannot be skipped.
370
383
 
384
+ #### Skipping a station
385
+
386
+ When the event's `allowStationSkipping` gameplay setting is on, a player who
387
+ can submit for their team can skip their current station. Check-in is not
388
+ required, so a team that cannot pass geolock can still move on:
389
+
390
+ ```bash
391
+ gosh player station skip \
392
+ --input '{"eventId":"event-id","stationId":"station-2"}' \
393
+ --target station-2 \
394
+ --allow-state-change
395
+ ```
396
+
397
+ A host with `reviewSubmissions` can skip a team's (`teamId`) or individual
398
+ player's (`eventPlayerId`) current station, or one open mission on it, while
399
+ the event is live. This works whatever the player setting is. Check the route
400
+ first, then pass an optional reason:
401
+
402
+ ```bash
403
+ gosh route progress --input '{"eventId":"event-id","teamId":"team-1"}'
404
+
405
+ gosh route skip-station \
406
+ --input '{"eventId":"event-id","teamId":"team-1","stationId":"station-2","reason":"Location closed"}' \
407
+ --target team-1 \
408
+ --allow-state-change
409
+
410
+ gosh route skip-mission \
411
+ --input '{"eventId":"event-id","teamId":"team-1","missionId":"mission-3","reason":"Prop missing"}' \
412
+ --target team-1 \
413
+ --allow-state-change
414
+ ```
415
+
416
+ Only the current station can be skipped, so route order stays intact. Its
417
+ open missions score zero, the station completes (a stop nobody started records
418
+ zero time), and the next stop unlocks immediately; skipping the last stop
419
+ finishes the route. A station with a reopened mission that still holds earlier
420
+ points cannot be skipped until it is resubmitted or reviewed. Who skipped and
421
+ why appears in `route progress`, the activity log, and the results export's
422
+ `skips.csv`.
423
+
371
424
  When reviewing, pass the submission's `updatedAt` from the review queue as
372
425
  `expectedUpdatedAt`. If the team resubmitted after you read it, the review is
373
426
  refused with `CONFLICT` instead of scoring the new answer blind:
@@ -379,6 +432,44 @@ gosh submission review \
379
432
  --allow-state-change
380
433
  ```
381
434
 
435
+ ### RSVP check-in teams
436
+
437
+ An RSVP form can create a GOSH team on its first successful check-in. RSVP
438
+ signs the request; GOSH creates the team from the registration's emails (the
439
+ first email is the captain, the rest are members), picks the least-loaded
440
+ route group that still has room, and returns the route. This also works
441
+ while the event is live, so a newly checked-in team can start playing.
442
+
443
+ The binding is off by default. Set the signing secret in the GOSH environment
444
+ as `GOSH_RSVP_CHECK_IN_WEBHOOK_SECRETS=<keyId>:whsec_...` (comma separated
445
+ during rotation). The secret is never stored in the binding.
446
+
447
+ ```bash
448
+ gosh rsvp-check-in binding set \
449
+ --input '{"eventId":"event-id","sourceOrganizationId":"rsvp-org-id","sourceEventId":"rsvp-event-id","sourceFormId":"rsvp-form-id","keyId":"farm-2026","enabled":true,"groups":[{"groupId":"group-a","teamCapacity":125},{"groupId":"group-b","teamCapacity":125},{"groupId":"group-c","teamCapacity":125},{"groupId":"group-d","teamCapacity":125}],"totalTeamCapacity":500,"maxRosterSize":2}' \
450
+ --target event-id \
451
+ --allow-state-change
452
+ gosh rsvp-check-in binding get --input '{"eventId":"event-id"}'
453
+ gosh rsvp-check-in links list --input '{"eventId":"event-id","filter":"REVIEW"}'
454
+ ```
455
+
456
+ - Group load counts every non-archived team in the group, including teams
457
+ assigned by hand. While the binding is enabled, `routing-group assign-team`
458
+ and restoring an archived team are refused if they would exceed a group's
459
+ capacity or the total limit.
460
+ - Groups with equal load are filled in the order listed in `groups`.
461
+ - When every route is full, GOSH creates no team and RSVP shows
462
+ `CAPACITY_FULL`. The attendee stays checked in.
463
+ - Email addresses are matched case-insensitively. Repeated addresses are
464
+ counted once. An invalid address, a suspended account, a player removed
465
+ from the event, or a player already on another team rejects the whole
466
+ team, and nothing is created.
467
+ - New accounts are not marked verified, and GOSH sends no email. Players sign
468
+ in later with the normal email code.
469
+ - Undoing the last RSVP check-in keeps the team and route but flags the team
470
+ for review. Clear the flag with `gosh rsvp-check-in review resolve`. A later
471
+ check-in reuses the same team.
472
+
382
473
  ### Artwork requirements (no login required)
383
474
 
384
475
  Read the bundled upload policy before preparing station, mission, or branding artwork:
@@ -462,6 +553,14 @@ gosh submission watch --input '{"organizationId":"acme","eventId":"event-id"}'
462
553
 
463
554
  Global admins can list users, change global roles, and ban or unban accounts under `gosh admin users …`. Banning an account atomically revokes its active browser and CLI sessions.
464
555
 
556
+ Global admins can read every post-event experience review across organizations under `gosh admin reviews …`. `list` returns newest-first `{ items, pageInfo }` cursor pages (`limit` 1 to 100, default 25; pass `pageInfo.nextCursor` back as `cursor`), `count` returns the total, average rating and 5 to 1 star distribution, `filter-options` lists the organizations and events that have reviews, and `export` returns every matching row (up to 10,000). All four accept the same optional filters: `rating` (1 to 5), `organizationId`, `eventId`, `comment` (`with` or `without`) and `q` (comment, participant or event search):
557
+
558
+ ```bash
559
+ gosh admin reviews list --input '{"rating":1,"limit":50}'
560
+ gosh admin reviews count --input '{"comment":"with"}'
561
+ gosh admin reviews export --input '{"organizationId":"org-id"}'
562
+ ```
563
+
465
564
  ## Mutation safety
466
565
 
467
566
  Mutations require `--target` to exactly match an identifier in `--input` (such as an `eventId`, `stationId`, slug, or name), binding the confirmation to the payload. In non-interactive automation, every applicable effect flag is also required:
@@ -921,6 +921,15 @@
921
921
  "requiresAuth": true,
922
922
  "summary": "List teams and their current members."
923
923
  },
924
+ {
925
+ "effects": [],
926
+ "input": "json",
927
+ "operation": "query",
928
+ "path": ["route", "progress"],
929
+ "procedure": "team.getRouteProgress",
930
+ "requiresAuth": true,
931
+ "summary": "Show a team's (teamId) or individual player's (eventPlayerId) sequential route: stops, mission statuses, and who skipped what and why."
932
+ },
924
933
  {
925
934
  "effects": [],
926
935
  "input": "json",
@@ -1239,9 +1248,47 @@
1239
1248
  "path": ["routing-group", "assign-team"],
1240
1249
  "procedure": "team.assignTeamRoutingGroup",
1241
1250
  "requiresAuth": true,
1242
- "summary": "Assign a team to a routing group, or clear its assignment with groupId null, before play starts.",
1251
+ "summary": "Assign a team to a routing group, or clear its assignment with groupId null, before play starts. Groups covered by an enabled RSVP check-in binding reject moves that exceed their team capacity.",
1243
1252
  "targetKeys": ["teamId"]
1244
1253
  },
1254
+ {
1255
+ "effects": [],
1256
+ "input": "json",
1257
+ "operation": "query",
1258
+ "path": ["rsvp-check-in", "binding", "get"],
1259
+ "procedure": "team.getRsvpCheckInBinding",
1260
+ "requiresAuth": true,
1261
+ "summary": "Show the RSVP check-in team binding for an event: source RSVP organization/event/form, key ID, enabled flag, ordered routing groups with team capacity and current load, total limit and roster size limit. Secrets are never stored or shown. Requires editSetup."
1262
+ },
1263
+ {
1264
+ "effects": ["state-change"],
1265
+ "input": "json",
1266
+ "operation": "mutation",
1267
+ "path": ["rsvp-check-in", "binding", "set"],
1268
+ "procedure": "team.upsertRsvpCheckInBinding",
1269
+ "requiresAuth": true,
1270
+ "summary": "Create or replace the RSVP check-in team binding: {eventId, sourceOrganizationId, sourceEventId, sourceFormId, keyId, enabled, groups:[{groupId, teamCapacity}] in tie-break order, totalTeamCapacity, maxRosterSize}. Disabled by default; the signing secret is set in the GOSH_RSVP_CHECK_IN_WEBHOOK_SECRETS environment variable under keyId. Requires launchEvent.",
1271
+ "targetKeys": ["eventId", "sourceFormId"]
1272
+ },
1273
+ {
1274
+ "effects": [],
1275
+ "input": "json",
1276
+ "operation": "query",
1277
+ "path": ["rsvp-check-in", "links", "list"],
1278
+ "procedure": "team.listRsvpCheckInLinks",
1279
+ "requiresAuth": true,
1280
+ "summary": "List RSVP registrations linked to check-in teams with route, applied revision, review flag and rejection code. filter ALL, REVIEW (undone check-ins) or REJECTED. Requires editSetup."
1281
+ },
1282
+ {
1283
+ "effects": ["state-change"],
1284
+ "input": "json",
1285
+ "operation": "mutation",
1286
+ "path": ["rsvp-check-in", "review", "resolve"],
1287
+ "procedure": "team.resolveRsvpCheckInReview",
1288
+ "requiresAuth": true,
1289
+ "summary": "Clear the review flag on an RSVP check-in team after crew follow-up. The team and its route are unchanged. Requires editSetup.",
1290
+ "targetKeys": ["linkId"]
1291
+ },
1245
1292
  {
1246
1293
  "effects": [],
1247
1294
  "input": "json",
@@ -1622,6 +1669,26 @@
1622
1669
  "name"
1623
1670
  ]
1624
1671
  },
1672
+ {
1673
+ "effects": ["state-change"],
1674
+ "input": "json",
1675
+ "operation": "mutation",
1676
+ "path": ["route", "skip-station"],
1677
+ "procedure": "team.skipStation",
1678
+ "requiresAuth": true,
1679
+ "summary": "Skip a team's (teamId) or individual player's (eventPlayerId) current station on a fixed-order route while the event is live, with an optional reason. Its open missions score zero and the next stop unlocks; works whatever the player skipping setting is.",
1680
+ "targetKeys": ["teamId", "eventPlayerId", "stationId"]
1681
+ },
1682
+ {
1683
+ "effects": ["state-change"],
1684
+ "input": "json",
1685
+ "operation": "mutation",
1686
+ "path": ["route", "skip-mission"],
1687
+ "procedure": "team.skipMission",
1688
+ "requiresAuth": true,
1689
+ "summary": "Skip one open mission at a team's (teamId) or individual player's (eventPlayerId) current station for zero points, with an optional reason. Completes the station when it was the last open mission.",
1690
+ "targetKeys": ["teamId", "eventPlayerId", "missionId"]
1691
+ },
1625
1692
  {
1626
1693
  "effects": ["state-change"],
1627
1694
  "input": "json",
@@ -1868,6 +1935,16 @@
1868
1935
  "name"
1869
1936
  ]
1870
1937
  },
1938
+ {
1939
+ "effects": ["state-change"],
1940
+ "input": "json",
1941
+ "operation": "mutation",
1942
+ "path": ["event", "update-survey"],
1943
+ "procedure": "event.updateSurveySettings",
1944
+ "requiresAuth": true,
1945
+ "summary": "Set the player survey: built-in, external link, or off, plus its copy and Google review prompt.",
1946
+ "targetKeys": ["eventId"]
1947
+ },
1871
1948
  {
1872
1949
  "effects": ["state-change"],
1873
1950
  "input": "json",
@@ -1964,6 +2041,79 @@
1964
2041
  "name"
1965
2042
  ]
1966
2043
  },
2044
+ {
2045
+ "effects": ["state-change"],
2046
+ "input": "json",
2047
+ "operation": "mutation",
2048
+ "path": ["station", "update-check-in"],
2049
+ "procedure": "event.updateStationCheckIn",
2050
+ "requiresAuth": true,
2051
+ "summary": "Set how players unlock a station: open, location, QR, or both.",
2052
+ "targetKeys": [
2053
+ "id",
2054
+ "memberId",
2055
+ "invitationId",
2056
+ "broadcastId",
2057
+ "rewardId",
2058
+ "submissionId",
2059
+ "participantId",
2060
+ "participantIds",
2061
+ "missionId",
2062
+ "stationId",
2063
+ "teamId",
2064
+ "templateId",
2065
+ "eventId",
2066
+ "organizationId",
2067
+ "userId",
2068
+ "key",
2069
+ "uploadId",
2070
+ "email",
2071
+ "joinCode",
2072
+ "slug",
2073
+ "name"
2074
+ ]
2075
+ },
2076
+ {
2077
+ "effects": [],
2078
+ "input": "json",
2079
+ "operation": "query",
2080
+ "path": ["station", "qr-codes"],
2081
+ "procedure": "event.getStationQrCodes",
2082
+ "requiresAuth": true,
2083
+ "summary": "List printable QR codes and manual codes for QR-enabled stations."
2084
+ },
2085
+ {
2086
+ "effects": ["state-change"],
2087
+ "input": "json",
2088
+ "operation": "mutation",
2089
+ "path": ["station", "regenerate-qr"],
2090
+ "procedure": "event.regenerateStationQr",
2091
+ "requiresAuth": true,
2092
+ "summary": "Regenerate a station QR. Earlier prints stop working.",
2093
+ "targetKeys": [
2094
+ "id",
2095
+ "memberId",
2096
+ "invitationId",
2097
+ "broadcastId",
2098
+ "rewardId",
2099
+ "submissionId",
2100
+ "participantId",
2101
+ "participantIds",
2102
+ "missionId",
2103
+ "stationId",
2104
+ "teamId",
2105
+ "templateId",
2106
+ "eventId",
2107
+ "organizationId",
2108
+ "userId",
2109
+ "key",
2110
+ "uploadId",
2111
+ "email",
2112
+ "joinCode",
2113
+ "slug",
2114
+ "name"
2115
+ ]
2116
+ },
1967
2117
  {
1968
2118
  "effects": ["state-change"],
1969
2119
  "input": "json",
@@ -2288,6 +2438,38 @@
2288
2438
  "name"
2289
2439
  ]
2290
2440
  },
2441
+ {
2442
+ "effects": ["state-change"],
2443
+ "input": "json",
2444
+ "operation": "mutation",
2445
+ "path": ["player", "station-qr-check-in"],
2446
+ "procedure": "event.createStationQrCheckInProof",
2447
+ "requiresAuth": true,
2448
+ "summary": "Check in to a station with its scanned QR or manual code.",
2449
+ "targetKeys": [
2450
+ "id",
2451
+ "memberId",
2452
+ "invitationId",
2453
+ "broadcastId",
2454
+ "rewardId",
2455
+ "submissionId",
2456
+ "participantId",
2457
+ "participantIds",
2458
+ "missionId",
2459
+ "stationId",
2460
+ "teamId",
2461
+ "templateId",
2462
+ "eventId",
2463
+ "organizationId",
2464
+ "userId",
2465
+ "key",
2466
+ "uploadId",
2467
+ "email",
2468
+ "joinCode",
2469
+ "slug",
2470
+ "name"
2471
+ ]
2472
+ },
2291
2473
  {
2292
2474
  "effects": ["state-change"],
2293
2475
  "input": "json",
@@ -2308,6 +2490,16 @@
2308
2490
  "summary": "Permanently skip one unresolved mission for zero points when enabled; retain other points and elapsed station time.",
2309
2491
  "targetKeys": ["missionId"]
2310
2492
  },
2493
+ {
2494
+ "effects": ["state-change"],
2495
+ "input": "json",
2496
+ "operation": "mutation",
2497
+ "path": ["player", "station", "skip"],
2498
+ "procedure": "event.skipStation",
2499
+ "requiresAuth": true,
2500
+ "summary": "Skip your current sequential station for zero points when the event allows station skipping. No check-in is needed; its missions cannot be played later.",
2501
+ "targetKeys": ["stationId"]
2502
+ },
2311
2503
  {
2312
2504
  "effects": ["state-change"],
2313
2505
  "input": "json",
@@ -2977,6 +3169,42 @@
2977
3169
  "name"
2978
3170
  ]
2979
3171
  },
3172
+ {
3173
+ "effects": [],
3174
+ "input": "json",
3175
+ "operation": "query",
3176
+ "path": ["admin", "reviews", "list"],
3177
+ "procedure": "review.listAdmin",
3178
+ "requiresAuth": true,
3179
+ "summary": "List experience reviews across all events as a global admin. Input: optional rating, organizationId, eventId, comment (with|without), q, plus limit, cursor and direction; returns { items, pageInfo }."
3180
+ },
3181
+ {
3182
+ "effects": [],
3183
+ "input": "json",
3184
+ "operation": "query",
3185
+ "path": ["admin", "reviews", "count"],
3186
+ "procedure": "review.getAdminSummary",
3187
+ "requiresAuth": true,
3188
+ "summary": "Count experience reviews for the given filters, with average rating and 5 to 1 star distribution."
3189
+ },
3190
+ {
3191
+ "effects": [],
3192
+ "input": "none",
3193
+ "operation": "query",
3194
+ "path": ["admin", "reviews", "filter-options"],
3195
+ "procedure": "review.listAdminFilterOptions",
3196
+ "requiresAuth": true,
3197
+ "summary": "List organizations and events that have experience reviews."
3198
+ },
3199
+ {
3200
+ "effects": [],
3201
+ "input": "json",
3202
+ "operation": "query",
3203
+ "path": ["admin", "reviews", "export"],
3204
+ "procedure": "review.exportAdmin",
3205
+ "requiresAuth": true,
3206
+ "summary": "Export every experience review matching the filters as rows (capped at 10,000)."
3207
+ },
2980
3208
  {
2981
3209
  "path": ["assets", "requirements"],
2982
3210
  "operation": "local",
@@ -370,6 +370,14 @@ List teams and their current members.
370
370
  - Input: `--input '<json>'` required
371
371
  - Effects: read-only
372
372
 
373
+ ## `gosh route progress`
374
+
375
+ Show a team's (teamId) or individual player's (eventPlayerId) sequential route: stops, mission statuses, and who skipped what and why.
376
+
377
+ - Authentication: browser-authenticated CLI session required
378
+ - Input: `--input '<json>'` required
379
+ - Effects: read-only
380
+
373
381
  ## `gosh team station-routes`
374
382
 
375
383
  List each team's linked stations and team-specific route order.
@@ -500,7 +508,39 @@ Replace a group's complete station route in the supplied order before play start
500
508
 
501
509
  ## `gosh routing-group assign-team`
502
510
 
503
- Assign a team to a routing group, or clear its assignment with groupId null, before play starts.
511
+ Assign a team to a routing group, or clear its assignment with groupId null, before play starts. Groups covered by an enabled RSVP check-in binding reject moves that exceed their team capacity.
512
+
513
+ - Authentication: browser-authenticated CLI session required
514
+ - Input: `--input '<json>'` required
515
+ - Effects: state-change
516
+
517
+ ## `gosh rsvp-check-in binding get`
518
+
519
+ Show the RSVP check-in team binding for an event: source RSVP organization/event/form, key ID, enabled flag, ordered routing groups with team capacity and current load, total limit and roster size limit. Secrets are never stored or shown. Requires editSetup.
520
+
521
+ - Authentication: browser-authenticated CLI session required
522
+ - Input: `--input '<json>'` required
523
+ - Effects: read-only
524
+
525
+ ## `gosh rsvp-check-in binding set`
526
+
527
+ Create or replace the RSVP check-in team binding: {eventId, sourceOrganizationId, sourceEventId, sourceFormId, keyId, enabled, groups:[{groupId, teamCapacity}] in tie-break order, totalTeamCapacity, maxRosterSize}. Disabled by default; the signing secret is set in the GOSH_RSVP_CHECK_IN_WEBHOOK_SECRETS environment variable under keyId. Requires launchEvent.
528
+
529
+ - Authentication: browser-authenticated CLI session required
530
+ - Input: `--input '<json>'` required
531
+ - Effects: state-change
532
+
533
+ ## `gosh rsvp-check-in links list`
534
+
535
+ List RSVP registrations linked to check-in teams with route, applied revision, review flag and rejection code. filter ALL, REVIEW (undone check-ins) or REJECTED. Requires editSetup.
536
+
537
+ - Authentication: browser-authenticated CLI session required
538
+ - Input: `--input '<json>'` required
539
+ - Effects: read-only
540
+
541
+ ## `gosh rsvp-check-in review resolve`
542
+
543
+ Clear the review flag on an RSVP check-in team after crew follow-up. The team and its route are unchanged. Requires editSetup.
504
544
 
505
545
  - Authentication: browser-authenticated CLI session required
506
546
  - Input: `--input '<json>'` required
@@ -618,6 +658,22 @@ Review a mission submission.
618
658
  - Input: `--input '<json>'` required
619
659
  - Effects: state-change
620
660
 
661
+ ## `gosh route skip-station`
662
+
663
+ Skip a team's (teamId) or individual player's (eventPlayerId) current station on a fixed-order route while the event is live, with an optional reason. Its open missions score zero and the next stop unlocks; works whatever the player skipping setting is.
664
+
665
+ - Authentication: browser-authenticated CLI session required
666
+ - Input: `--input '<json>'` required
667
+ - Effects: state-change
668
+
669
+ ## `gosh route skip-mission`
670
+
671
+ Skip one open mission at a team's (teamId) or individual player's (eventPlayerId) current station for zero points, with an optional reason. Completes the station when it was the last open mission.
672
+
673
+ - Authentication: browser-authenticated CLI session required
674
+ - Input: `--input '<json>'` required
675
+ - Effects: state-change
676
+
621
677
  ## `gosh mission reopen`
622
678
 
623
679
  Reopen a submitted or skipped mission for one team (teamId) or player (eventPlayerId) on a fixed-order route. Earlier points count until they resubmit; then only the new submission counts. Finish times are kept.
@@ -754,6 +810,14 @@ Update event branding.
754
810
  - Input: `--input '<json>'` required
755
811
  - Effects: state-change
756
812
 
813
+ ## `gosh event update-survey`
814
+
815
+ Set the player survey: built-in, external link, or off, plus its copy and Google review prompt.
816
+
817
+ - Authentication: browser-authenticated CLI session required
818
+ - Input: `--input '<json>'` required
819
+ - Effects: state-change
820
+
757
821
  ## `gosh station update-availability`
758
822
 
759
823
  Update station availability.
@@ -778,6 +842,30 @@ Update station location and geofence settings.
778
842
  - Input: `--input '<json>'` required
779
843
  - Effects: state-change
780
844
 
845
+ ## `gosh station update-check-in`
846
+
847
+ Set how players unlock a station: open, location, QR, or both.
848
+
849
+ - Authentication: browser-authenticated CLI session required
850
+ - Input: `--input '<json>'` required
851
+ - Effects: state-change
852
+
853
+ ## `gosh station qr-codes`
854
+
855
+ List printable QR codes and manual codes for QR-enabled stations.
856
+
857
+ - Authentication: browser-authenticated CLI session required
858
+ - Input: `--input '<json>'` required
859
+ - Effects: read-only
860
+
861
+ ## `gosh station regenerate-qr`
862
+
863
+ Regenerate a station QR. Earlier prints stop working.
864
+
865
+ - Authentication: browser-authenticated CLI session required
866
+ - Input: `--input '<json>'` required
867
+ - Effects: state-change
868
+
781
869
  ## `gosh event save-as-template`
782
870
 
783
871
  Save an event as a template.
@@ -922,6 +1010,14 @@ Create an expiring station check-in proof.
922
1010
  - Input: `--input '<json>'` required
923
1011
  - Effects: state-change
924
1012
 
1013
+ ## `gosh player station-qr-check-in`
1014
+
1015
+ Check in to a station with its scanned QR or manual code.
1016
+
1017
+ - Authentication: browser-authenticated CLI session required
1018
+ - Input: `--input '<json>'` required
1019
+ - Effects: state-change
1020
+
925
1021
  ## `gosh player station start`
926
1022
 
927
1023
  Start the current sequential station's shared mission timer and reveal its missions; required check-in still applies.
@@ -938,6 +1034,14 @@ Permanently skip one unresolved mission for zero points when enabled; retain oth
938
1034
  - Input: `--input '<json>'` required
939
1035
  - Effects: state-change
940
1036
 
1037
+ ## `gosh player station skip`
1038
+
1039
+ Skip your current sequential station for zero points when the event allows station skipping. No check-in is needed; its missions cannot be played later.
1040
+
1041
+ - Authentication: browser-authenticated CLI session required
1042
+ - Input: `--input '<json>'` required
1043
+ - Effects: state-change
1044
+
941
1045
  ## `gosh player mission submit`
942
1046
 
943
1047
  Submit a mission response and return revealMessage, including already-completed submissions.
@@ -1180,6 +1284,38 @@ Unban a user.
1180
1284
  - Input: `--input '<json>'` required
1181
1285
  - Effects: state-change
1182
1286
 
1287
+ ## `gosh admin reviews list`
1288
+
1289
+ List experience reviews across all events as a global admin. Input: optional rating, organizationId, eventId, comment (with|without), q, plus limit, cursor and direction; returns { items, pageInfo }.
1290
+
1291
+ - Authentication: browser-authenticated CLI session required
1292
+ - Input: `--input '<json>'` required
1293
+ - Effects: read-only
1294
+
1295
+ ## `gosh admin reviews count`
1296
+
1297
+ Count experience reviews for the given filters, with average rating and 5 to 1 star distribution.
1298
+
1299
+ - Authentication: browser-authenticated CLI session required
1300
+ - Input: `--input '<json>'` required
1301
+ - Effects: read-only
1302
+
1303
+ ## `gosh admin reviews filter-options`
1304
+
1305
+ List organizations and events that have experience reviews.
1306
+
1307
+ - Authentication: browser-authenticated CLI session required
1308
+ - Input: none
1309
+ - Effects: read-only
1310
+
1311
+ ## `gosh admin reviews export`
1312
+
1313
+ Export every experience review matching the filters as rows (capped at 10,000).
1314
+
1315
+ - Authentication: browser-authenticated CLI session required
1316
+ - Input: `--input '<json>'` required
1317
+ - Effects: read-only
1318
+
1183
1319
  ## `gosh assets requirements`
1184
1320
 
1185
1321
  Show upload limits and separate artwork recommendations without login or network access.
package/dist/index.js CHANGED
@@ -7452,6 +7452,10 @@ var typedOperations = {
7452
7452
  kind: "query",
7453
7453
  invoke: (client, input) => client.team.list.query(input)
7454
7454
  },
7455
+ "team.getRouteProgress": {
7456
+ kind: "query",
7457
+ invoke: (client, input) => client.team.getRouteProgress.query(input)
7458
+ },
7455
7459
  "team.getStationRoutes": {
7456
7460
  kind: "query",
7457
7461
  invoke: (client, input) => client.team.getStationRoutes.query(input)
@@ -7520,6 +7524,22 @@ var typedOperations = {
7520
7524
  kind: "mutation",
7521
7525
  invoke: (client, input) => client.team.assignTeamRoutingGroup.mutate(input)
7522
7526
  },
7527
+ "team.getRsvpCheckInBinding": {
7528
+ kind: "query",
7529
+ invoke: (client, input) => client.team.getRsvpCheckInBinding.query(input)
7530
+ },
7531
+ "team.upsertRsvpCheckInBinding": {
7532
+ kind: "mutation",
7533
+ invoke: (client, input) => client.team.upsertRsvpCheckInBinding.mutate(input)
7534
+ },
7535
+ "team.listRsvpCheckInLinks": {
7536
+ kind: "query",
7537
+ invoke: (client, input) => client.team.listRsvpCheckInLinks.query(input)
7538
+ },
7539
+ "team.resolveRsvpCheckInReview": {
7540
+ kind: "mutation",
7541
+ invoke: (client, input) => client.team.resolveRsvpCheckInReview.mutate(input)
7542
+ },
7523
7543
  "template.list": {
7524
7544
  kind: "query",
7525
7545
  invoke: (client, input) => client.template.list.query(input)
@@ -7576,6 +7596,14 @@ var typedOperations = {
7576
7596
  kind: "mutation",
7577
7597
  invoke: (client, input) => client.event.reviewSubmission.mutate(input)
7578
7598
  },
7599
+ "team.skipStation": {
7600
+ kind: "mutation",
7601
+ invoke: (client, input) => client.team.skipStation.mutate(input)
7602
+ },
7603
+ "team.skipMission": {
7604
+ kind: "mutation",
7605
+ invoke: (client, input) => client.team.skipMission.mutate(input)
7606
+ },
7579
7607
  "event.reopenMission": {
7580
7608
  kind: "mutation",
7581
7609
  invoke: (client, input) => client.event.reopenMission.mutate(input)
@@ -7644,6 +7672,10 @@ var typedOperations = {
7644
7672
  kind: "mutation",
7645
7673
  invoke: (client, input) => client.event.updateBranding.mutate(input)
7646
7674
  },
7675
+ "event.updateSurveySettings": {
7676
+ kind: "mutation",
7677
+ invoke: (client, input) => client.event.updateSurveySettings.mutate(input)
7678
+ },
7647
7679
  "event.updateStationAvailability": {
7648
7680
  kind: "mutation",
7649
7681
  invoke: (client, input) => client.event.updateStationAvailability.mutate(input)
@@ -7656,6 +7688,18 @@ var typedOperations = {
7656
7688
  kind: "mutation",
7657
7689
  invoke: (client, input) => client.event.updateStationLocation.mutate(input)
7658
7690
  },
7691
+ "event.updateStationCheckIn": {
7692
+ kind: "mutation",
7693
+ invoke: (client, input) => client.event.updateStationCheckIn.mutate(input)
7694
+ },
7695
+ "event.getStationQrCodes": {
7696
+ kind: "query",
7697
+ invoke: (client, input) => client.event.getStationQrCodes.query(input)
7698
+ },
7699
+ "event.regenerateStationQr": {
7700
+ kind: "mutation",
7701
+ invoke: (client, input) => client.event.regenerateStationQr.mutate(input)
7702
+ },
7659
7703
  "event.saveAsTemplate": {
7660
7704
  kind: "mutation",
7661
7705
  invoke: (client, input) => client.event.saveAsTemplate.mutate(input)
@@ -7728,6 +7772,10 @@ var typedOperations = {
7728
7772
  kind: "mutation",
7729
7773
  invoke: (client, input) => client.event.createStationCheckInProof.mutate(input)
7730
7774
  },
7775
+ "event.createStationQrCheckInProof": {
7776
+ kind: "mutation",
7777
+ invoke: (client, input) => client.event.createStationQrCheckInProof.mutate(input)
7778
+ },
7731
7779
  "event.startStation": {
7732
7780
  kind: "mutation",
7733
7781
  invoke: (client, input) => client.event.startStation.mutate(input)
@@ -7736,6 +7784,10 @@ var typedOperations = {
7736
7784
  kind: "mutation",
7737
7785
  invoke: (client, input) => client.event.skipMission.mutate(input)
7738
7786
  },
7787
+ "event.skipStation": {
7788
+ kind: "mutation",
7789
+ invoke: (client, input) => client.event.skipStation.mutate(input)
7790
+ },
7739
7791
  "event.submitMission": {
7740
7792
  kind: "mutation",
7741
7793
  invoke: (client, input) => client.event.submitMission.mutate(input)
@@ -7863,6 +7915,22 @@ var typedOperations = {
7863
7915
  "user.unban": {
7864
7916
  kind: "mutation",
7865
7917
  invoke: (client, input) => client.user.unban.mutate(input)
7918
+ },
7919
+ "review.listAdmin": {
7920
+ kind: "query",
7921
+ invoke: (client, input) => client.review.listAdmin.query(input)
7922
+ },
7923
+ "review.getAdminSummary": {
7924
+ kind: "query",
7925
+ invoke: (client, input) => client.review.getAdminSummary.query(input)
7926
+ },
7927
+ "review.listAdminFilterOptions": {
7928
+ kind: "query",
7929
+ invoke: (client, input) => client.review.listAdminFilterOptions.query(input)
7930
+ },
7931
+ "review.exportAdmin": {
7932
+ kind: "query",
7933
+ invoke: (client, input) => client.review.exportAdmin.query(input)
7866
7934
  }
7867
7935
  };
7868
7936
 
@@ -7918,13 +7986,19 @@ var defaultMutationTargetKeys = [
7918
7986
  var mutationTargetKeys = {
7919
7987
  "team.updateRoutingGroupRelease": ["groupId", "eventId"],
7920
7988
  "team.setGameplaySettings": ["eventId"],
7989
+ "event.updateSurveySettings": ["eventId"],
7921
7990
  "team.createRoutingGroup": ["eventId"],
7922
7991
  "team.renameRoutingGroup": ["groupId"],
7923
7992
  "team.deleteRoutingGroup": ["groupId"],
7924
7993
  "team.setRoutingGroupStations": ["groupId"],
7925
7994
  "team.assignTeamRoutingGroup": ["teamId"],
7995
+ "team.upsertRsvpCheckInBinding": ["eventId", "sourceFormId"],
7996
+ "team.resolveRsvpCheckInReview": ["linkId"],
7926
7997
  "event.startStation": ["stationId"],
7927
7998
  "event.skipMission": ["missionId"],
7999
+ "event.skipStation": ["stationId"],
8000
+ "team.skipStation": ["teamId", "eventPlayerId", "stationId"],
8001
+ "team.skipMission": ["teamId", "eventPlayerId", "missionId"],
7928
8002
  "event.reopenMission": ["teamId", "eventPlayerId", "missionId"],
7929
8003
  "event.create": ["organizationId", "name"],
7930
8004
  "event.createFromTemplate": ["organizationId", "templateId"],
@@ -8022,6 +8096,7 @@ var commands = [
8022
8096
  mutation(["mission", "archive"], "event.archiveMission", "Archive a mission.", ["state-change", "delete"]),
8023
8097
  mutation(["mission", "reorder"], "event.reorderMissions", "Reorder missions within a station.", ["state-change"]),
8024
8098
  query(["team", "list"], "team.list", "List teams and their current members."),
8099
+ query(["route", "progress"], "team.getRouteProgress", "Show a team's (teamId) or individual player's (eventPlayerId) sequential route: stops, mission statuses, and who skipped what and why."),
8025
8100
  query(["team", "station-routes"], "team.getStationRoutes", "List each team's linked stations and team-specific route order."),
8026
8101
  mutation(["team", "create"], "team.create", "Create a team.", [
8027
8102
  "state-change"
@@ -8042,7 +8117,11 @@ var commands = [
8042
8117
  mutation(["routing-group", "rename"], "team.renameRoutingGroup", "Rename an event's routing group before play starts.", ["state-change"]),
8043
8118
  mutation(["routing-group", "delete"], "team.deleteRoutingGroup", "Delete an unassigned routing group before play starts.", ["state-change", "delete"]),
8044
8119
  mutation(["routing-group", "set-stations"], "team.setRoutingGroupStations", "Replace a group's complete station route in the supplied order before play starts.", ["state-change"]),
8045
- mutation(["routing-group", "assign-team"], "team.assignTeamRoutingGroup", "Assign a team to a routing group, or clear its assignment with groupId null, before play starts.", ["state-change"]),
8120
+ mutation(["routing-group", "assign-team"], "team.assignTeamRoutingGroup", "Assign a team to a routing group, or clear its assignment with groupId null, before play starts. Groups covered by an enabled RSVP check-in binding reject moves that exceed their team capacity.", ["state-change"]),
8121
+ query(["rsvp-check-in", "binding", "get"], "team.getRsvpCheckInBinding", "Show the RSVP check-in team binding for an event: source RSVP organization/event/form, key ID, enabled flag, ordered routing groups with team capacity and current load, total limit and roster size limit. Secrets are never stored or shown. Requires editSetup."),
8122
+ mutation(["rsvp-check-in", "binding", "set"], "team.upsertRsvpCheckInBinding", "Create or replace the RSVP check-in team binding: {eventId, sourceOrganizationId, sourceEventId, sourceFormId, keyId, enabled, groups:[{groupId, teamCapacity}] in tie-break order, totalTeamCapacity, maxRosterSize}. Disabled by default; the signing secret is set in the GOSH_RSVP_CHECK_IN_WEBHOOK_SECRETS environment variable under keyId. Requires launchEvent.", ["state-change"]),
8123
+ query(["rsvp-check-in", "links", "list"], "team.listRsvpCheckInLinks", "List RSVP registrations linked to check-in teams with route, applied revision, review flag and rejection code. filter ALL, REVIEW (undone check-ins) or REJECTED. Requires editSetup."),
8124
+ mutation(["rsvp-check-in", "review", "resolve"], "team.resolveRsvpCheckInReview", "Clear the review flag on an RSVP check-in team after crew follow-up. The team and its route are unchanged. Requires editSetup.", ["state-change"]),
8046
8125
  query(["template", "list"], "template.list", "List templates visible in an organization; global admins can include every organization."),
8047
8126
  query(["template", "get"], "template.getById", "Get a template visible in the requested organization; global admins can get any template."),
8048
8127
  mutation(["template", "create"], "template.create", "Create an organization template, including shared stations with optional per-team route order.", ["state-change"]),
@@ -8060,6 +8139,8 @@ var commands = [
8060
8139
  mutation(["reward", "update"], "event.updateReward", "Update an event reward.", ["state-change"]),
8061
8140
  mutation(["score", "adjust"], "event.createScoreAdjustment", "Create a manual score adjustment.", ["state-change"]),
8062
8141
  mutation(["submission", "review"], "event.reviewSubmission", "Review a mission submission.", ["state-change"]),
8142
+ mutation(["route", "skip-station"], "team.skipStation", "Skip a team's (teamId) or individual player's (eventPlayerId) current station on a fixed-order route while the event is live, with an optional reason. Its open missions score zero and the next stop unlocks; works whatever the player skipping setting is.", ["state-change"]),
8143
+ mutation(["route", "skip-mission"], "team.skipMission", "Skip one open mission at a team's (teamId) or individual player's (eventPlayerId) current station for zero points, with an optional reason. Completes the station when it was the last open mission.", ["state-change"]),
8063
8144
  mutation(["mission", "reopen"], "event.reopenMission", "Reopen a submitted or skipped mission for one team (teamId) or player (eventPlayerId) on a fixed-order route. Earlier points count until they resubmit; then only the new submission counts. Finish times are kept.", ["state-change"]),
8064
8145
  mutation(["results-share", "regenerate"], "event.regenerateResultsPortalShare", "Create a new results portal share token.", ["state-change"]),
8065
8146
  mutation(["results-share", "revoke"], "event.revokeResultsPortalShare", "Revoke the current results portal share token.", ["state-change", "delete"]),
@@ -8077,9 +8158,13 @@ var commands = [
8077
8158
  query(["results-share", "get"], "event.getResultsPortalShare", "Get the current results portal share."),
8078
8159
  mutation(["event", "update-access"], "event.updateAccess", "Update event access rules.", ["state-change"]),
8079
8160
  mutation(["event", "update-branding"], "event.updateBranding", "Update event branding.", ["state-change"]),
8161
+ mutation(["event", "update-survey"], "event.updateSurveySettings", "Set the player survey: built-in, external link, or off, plus its copy and Google review prompt.", ["state-change"]),
8080
8162
  mutation(["station", "update-availability"], "event.updateStationAvailability", "Update station availability.", ["state-change"]),
8081
8163
  mutation(["station", "update-image"], "event.updateStationImage", "Update a station image.", ["state-change"]),
8082
8164
  mutation(["station", "update-location"], "event.updateStationLocation", "Update station location and geofence settings.", ["state-change"]),
8165
+ mutation(["station", "update-check-in"], "event.updateStationCheckIn", "Set how players unlock a station: open, location, QR, or both.", ["state-change"]),
8166
+ query(["station", "qr-codes"], "event.getStationQrCodes", "List printable QR codes and manual codes for QR-enabled stations."),
8167
+ mutation(["station", "regenerate-qr"], "event.regenerateStationQr", "Regenerate a station QR. Earlier prints stop working.", ["state-change"]),
8083
8168
  mutation(["event", "save-as-template"], "event.saveAsTemplate", "Save an event as a template.", ["state-change"]),
8084
8169
  query(["player", "event"], "event.getJoinDetails", "Get the current player's event details."),
8085
8170
  query(["player", "leaderboard", "page"], "event.getPaginatedPlayerLeaderboard", "Get a page of the player leaderboard."),
@@ -8100,8 +8185,10 @@ var commands = [
8100
8185
  mutation(["player", "display-name", "update"], "event.updatePlayerDisplayName", "Update the player's display name.", ["state-change"]),
8101
8186
  mutation(["player", "resolve-join-code"], "event.resolveJoinCode", "Resolve an event or team join code.", []),
8102
8187
  mutation(["player", "station-check-in"], "event.createStationCheckInProof", "Create an expiring station check-in proof.", ["state-change"]),
8188
+ mutation(["player", "station-qr-check-in"], "event.createStationQrCheckInProof", "Check in to a station with its scanned QR or manual code.", ["state-change"]),
8103
8189
  mutation(["player", "station", "start"], "event.startStation", "Start the current sequential station's shared mission timer and reveal its missions; required check-in still applies.", ["state-change"]),
8104
8190
  mutation(["player", "mission", "skip"], "event.skipMission", "Permanently skip one unresolved mission for zero points when enabled; retain other points and elapsed station time.", ["state-change"]),
8191
+ mutation(["player", "station", "skip"], "event.skipStation", "Skip your current sequential station for zero points when the event allows station skipping. No check-in is needed; its missions cannot be played later.", ["state-change"]),
8105
8192
  mutation(["player", "mission", "submit"], "event.submitMission", "Submit a mission response and return revealMessage, including already-completed submissions.", ["state-change"]),
8106
8193
  query(["public", "event-login"], "event.getPublicLoginDetails", "Get public event login details.", { requiresAuth: false }),
8107
8194
  query(["public", "results"], "event.getResultsPortal", "Get a shared public results portal.", { requiresAuth: false }),
@@ -8133,7 +8220,11 @@ var commands = [
8133
8220
  mutation(["admin", "users", "ban"], "user.ban", "Ban a user and revoke their sessions and API keys.", ["state-change", "delete"]),
8134
8221
  mutation(["admin", "users", "unban"], "user.unban", "Unban a user.", [
8135
8222
  "state-change"
8136
- ])
8223
+ ]),
8224
+ query(["admin", "reviews", "list"], "review.listAdmin", "List experience reviews across all events as a global admin. Input: optional rating, organizationId, eventId, comment (with|without), q, plus limit, cursor and direction; returns { items, pageInfo }."),
8225
+ query(["admin", "reviews", "count"], "review.getAdminSummary", "Count experience reviews for the given filters, with average rating and 5 to 1 star distribution."),
8226
+ query(["admin", "reviews", "filter-options"], "review.listAdminFilterOptions", "List organizations and events that have experience reviews.", { input: "none" }),
8227
+ query(["admin", "reviews", "export"], "review.exportAdmin", "Export every experience review matching the filters as rows (capped at 10,000).")
8137
8228
  ];
8138
8229
  var commandManifest = commands.map((command) => ({
8139
8230
  effects: command.effects,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/gosh",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "GOSH CLI for organizers and AI agents — manage organizations, events, teams, stations, missions, templates, and live operations through the GOSH API.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",