@go-labs-sg/gosh 0.12.0 → 0.14.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
@@ -97,7 +97,7 @@ Commands use `--input '<json>'` for the exact validated tRPC input object. This
97
97
 
98
98
  `gosh event list-page` returns `{ items, page, pageSize, pageCount, totalCount }`. It defaults to page 1 with 25 results, accepts page sizes from 1 to 100, and supports server-side `search` and `status` filters. Archived events are excluded unless an explicit status is supplied. Request subsequent pages explicitly; the CLI does not automatically fetch every page. The existing `gosh event list` command and its array response remain unchanged.
99
99
 
100
- The command registry exposes the supported GOSH tRPC procedures across organizer and global-admin operations, organization invitations and membership, player workflows, public results/login procedures, uploads, and realtime subscriptions. Update the registry alongside API changes, then run `bun run generate:commands` to regenerate the manifest, reference, and typed operations.
100
+ The command registry exposes the supported GOSH tRPC procedures across organizer and global-admin operations, organization invitations and membership, player workflows, public results/login procedures, uploads, and realtime subscriptions. Update the registry alongside API changes, then run `bun --filter @go-labs-sg/gosh generate:commands` from the repository root to regenerate the manifest, reference, and typed operations.
101
101
 
102
102
  ### Templates and organization scope
103
103
 
@@ -183,8 +183,9 @@ value in that template team's `order` field:
183
183
  Events default to `OPEN` progression, which keeps the existing open-play flow.
184
184
  Sequential progression starts each team or individual on an ordered station
185
185
  route. Group routing is available only for `TEAM` play mode. Create teams and
186
- assign them to routing groups before launching the event; setup freezes when
187
- play starts. A group shares its ordered station route, while each team keeps
186
+ assign them to routing groups before launching the event; sequential setup
187
+ freezes when play starts. Grouped OPEN events lock only a started team's group
188
+ assignment, a started group's route and the gameplay settings. A group shares its ordered station route, while each team keeps
188
189
  independent progress through that route.
189
190
 
190
191
  Configure gameplay, then manage the event's routing groups and their complete
@@ -225,6 +226,43 @@ the desired order. Use `groupId: null` with `assign-team` to unassign a team.
225
226
  Group names are trimmed and limited to 80 characters. The delete command
226
227
  requires the additional `--allow-delete` effect flag.
227
228
 
229
+ ### Staggered group release
230
+
231
+ `routing-group release` changes flag-off without changing routes. It requires
232
+ launch permission and a draft, ready or live TEAM event with group routing. A
233
+ different group already playing does not block it. Once a team in the target
234
+ group has started, only HOLD (emergency hold) and RELEASE_SCHEDULED_HOLD (resume)
235
+ are accepted.
236
+
237
+ ```bash
238
+ gosh routing-group release \
239
+ --input '{"eventId":"event-id","groupId":"group-d","action":"SCHEDULE","releaseAt":"2030-01-01T09:20:00+08:00"}' \
240
+ --target group-d \
241
+ --allow-state-change
242
+ gosh routing-group release \
243
+ --input '{"eventId":"event-id","groupId":"group-d","action":"HOLD"}' \
244
+ --target group-d \
245
+ --allow-state-change
246
+ ```
247
+
248
+ Replace example IDs/time with your intended target and a future instant.
249
+ SCHEDULE requires an ISO timestamp with `Z` or a numeric timezone offset;
250
+ offsetless, invalid and past times are rejected. CLEAR sets the schedule to null
251
+ but preserves a hold. HOLD overrides the clock. RELEASE_SCHEDULED_HOLD removes
252
+ the hold and retains the schedule; an elapsed/no schedule releases immediately.
253
+ RELEASE_NOW clears both schedule and hold. Omit `releaseAt` for non-schedule
254
+ actions. Repeated no-op controls do not create duplicate audits. List routing
255
+ groups to read back release values and editability, and use
256
+ `gosh routing-group release-history --input '{"eventId":"event-id","groupId":"group-d"}'`
257
+ for the five most recent actor/before/after changes; the existing event activity
258
+ timeline provides the full audit history.
259
+
260
+ The server evaluates deadlines without cron. Release never starts a timer or
261
+ bypasses LIVE status, availability, check-in or captain permissions. Any team in
262
+ a group beginning play locks that group's schedule; emergency HOLD/resume stays
263
+ available and does not pause running station clocks. There is no
264
+ untimed practice-mission exemption. See [the design notes](../../../docs/design/gosh-staggered-routing-group-release.md).
265
+
228
266
  For sequential player actions, complete any required location check-in with
229
267
  `gosh player station-check-in` first. The server checks the signed-in player’s
230
268
  current check-in record; a successful check-in also starts the station timer.
@@ -358,6 +396,20 @@ Without the allow flags, an interactive terminal prints a workflow preview and r
358
396
 
359
397
  One-shot commands write one JSON envelope to stdout. Watch commands write newline-delimited JSON envelopes. Failures and opt-in `--debug` diagnostics go to stderr, and known secrets, payloads, join codes, share tokens, and signed URLs are redacted. `--quiet` or `-q` suppresses diagnostics.
360
398
 
399
+ ## Monorepo development
400
+
401
+ Follow the [development workflow](../../../docs/development-workflow.md#required-cli-coverage). App/API changes affecting this CLI must update its commands, types/input-output contracts, help/docs and verification in the same change. Use the existing authenticated product API rather than CLI-only business logic. Presentation-only changes do not require CLI edits; record why when no CLI update was needed.
402
+
403
+ From the repository root after isolated-worktree initialization:
404
+
405
+ ```bash
406
+ bun --filter @go-labs-sg/gosh typecheck
407
+ bun --filter @go-labs-sg/gosh build
408
+ bun run packages/gosh/cli/src/index.ts help
409
+ ```
410
+
411
+ Building is not publication or permission to mutate live data. Use focused temporary business-behavior tests and the final pre-push gate from the shared workflow. Its existing build regenerates command artifacts; review the manifest/reference and typed operations after registry changes.
412
+
361
413
  ## Release verification
362
414
 
363
415
  The `gosh-cli-v*` release workflow runs
@@ -1173,6 +1173,25 @@
1173
1173
  "requiresAuth": true,
1174
1174
  "summary": "List an event's routing groups, ordered stations, and assigned teams."
1175
1175
  },
1176
+ {
1177
+ "effects": [],
1178
+ "input": "json",
1179
+ "operation": "query",
1180
+ "path": ["routing-group", "release-history"],
1181
+ "procedure": "team.listRoutingGroupReleaseHistory",
1182
+ "requiresAuth": true,
1183
+ "summary": "Show a routing group's five most recent release changes with actor, action and before/after UTC values."
1184
+ },
1185
+ {
1186
+ "effects": ["state-change"],
1187
+ "input": "json",
1188
+ "operation": "mutation",
1189
+ "path": ["routing-group", "release"],
1190
+ "procedure": "team.updateRoutingGroupRelease",
1191
+ "requiresAuth": true,
1192
+ "summary": "Schedule (future releaseAt ISO timestamp with offset), CLEAR schedule, HOLD, RELEASE_SCHEDULED_HOLD (keep schedule), or RELEASE_NOW for a routing group. After a group starts, only HOLD and RELEASE_SCHEDULED_HOLD are accepted. Requires launchEvent permission.",
1193
+ "targetKeys": ["groupId", "eventId"]
1194
+ },
1176
1195
  {
1177
1196
  "effects": ["state-change"],
1178
1197
  "input": "json",
@@ -450,6 +450,22 @@ List an event's routing groups, ordered stations, and assigned teams.
450
450
  - Input: `--input '<json>'` required
451
451
  - Effects: read-only
452
452
 
453
+ ## `gosh routing-group release-history`
454
+
455
+ Show a routing group's five most recent release changes with actor, action and before/after UTC values.
456
+
457
+ - Authentication: browser-authenticated CLI session required
458
+ - Input: `--input '<json>'` required
459
+ - Effects: read-only
460
+
461
+ ## `gosh routing-group release`
462
+
463
+ Schedule (future releaseAt ISO timestamp with offset), CLEAR schedule, HOLD, RELEASE_SCHEDULED_HOLD (keep schedule), or RELEASE_NOW for a routing group. After a group starts, only HOLD and RELEASE_SCHEDULED_HOLD are accepted. Requires launchEvent permission.
464
+
465
+ - Authentication: browser-authenticated CLI session required
466
+ - Input: `--input '<json>'` required
467
+ - Effects: state-change
468
+
453
469
  ## `gosh routing-group create`
454
470
 
455
471
  Create a routing group within an event before play starts.
package/dist/index.js CHANGED
@@ -35,16 +35,16 @@ var __toESM = (mod, isNodeMode, target) => {
35
35
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
36
36
  var __require = import.meta.require;
37
37
 
38
- // ../../../node_modules/.bun/dotenv@18.0.5/node_modules/dotenv/dist/index.cjs
38
+ // ../../../node_modules/.bun/dotenv@18.0.6/node_modules/dotenv/dist/index.cjs
39
39
  var require_dist = __commonJS(function(exports, module) {
40
- var I = (e, o) => () => {
40
+ var S = (e, o) => () => {
41
41
  try {
42
42
  return o || e((o = { exports: {} }).exports, o), o.exports;
43
43
  } catch (t) {
44
44
  throw o = 0, t;
45
45
  }
46
46
  };
47
- var S = I((xe, U) => {
47
+ var j = S((xe, U) => {
48
48
  function G(e) {
49
49
  return typeof e == "string" ? !["false", "0", "no", "off", ""].includes(e.toLowerCase()) : !!e;
50
50
  }
@@ -58,8 +58,8 @@ var require_dist = __commonJS(function(exports, module) {
58
58
  }
59
59
  U.exports = { parseBoolean: G, optionsFromEnv: X };
60
60
  });
61
- var N = I((ye, x) => {
62
- var Y = __require("fs"), j = __require("path"), z = __require("os"), { URL: Z, fileURLToPath: ee } = __require("url"), { parseBoolean: k, optionsFromEnv: B } = S(), te = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg, b = new Uint8Array(256);
61
+ var N = S((ye, x) => {
62
+ var Y = __require("fs"), k = __require("path"), z = __require("os"), { URL: Z, fileURLToPath: ee } = __require("url"), { parseBoolean: $, optionsFromEnv: B } = j(), te = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg, b = new Uint8Array(256);
63
63
  for (let e = 48;e <= 57; e++)
64
64
  b[e] = 1;
65
65
  for (let e = 65;e <= 90; e++)
@@ -185,7 +185,7 @@ var require_dist = __commonJS(function(exports, module) {
185
185
  return o;
186
186
  }
187
187
  function ne(e, o) {
188
- return o && k(o.fast) ? oe(e) : re(e);
188
+ return o && $(o.fast) ? oe(e) : re(e);
189
189
  }
190
190
  function T(e) {
191
191
  console.log(`\u2506 ${e}`);
@@ -194,16 +194,16 @@ var require_dist = __commonJS(function(exports, module) {
194
194
  console.error(`\u25C7 ${e}`);
195
195
  }
196
196
  function V(e) {
197
- return e[0] === "~" ? j.join(z.homedir(), e.slice(1)) : e;
197
+ return e[0] === "~" ? k.join(z.homedir(), e.slice(1)) : e;
198
198
  }
199
199
  function ie(e = {}) {
200
200
  return { ...B(), ...e };
201
201
  }
202
202
  function ce(e) {
203
203
  e = ie(e);
204
- let o = j.resolve(process.cwd(), ".env"), t = "utf8", n = process.env;
204
+ let o = k.resolve(process.cwd(), ".env"), t = "utf8", n = process.env;
205
205
  e && e.processEnv != null && (n = e.processEnv);
206
- let r = k(e && e.debug);
206
+ let r = $(e && e.debug);
207
207
  e && e.encoding ? t = e.encoding : r && T("no encoding is specified (UTF-8 is used by default)");
208
208
  let s = [o];
209
209
  if (e && e.path)
@@ -222,12 +222,12 @@ var require_dist = __commonJS(function(exports, module) {
222
222
  } catch (a) {
223
223
  r && T(`failed to load ${c} ${a.message}`), i = a;
224
224
  }
225
- let p = A.populate(n, l, e), f = k(Object.prototype.hasOwnProperty.call(e, "quiet") ? e.quiet : B(n).quiet);
225
+ let p = A.populate(n, l, e), f = $(Object.prototype.hasOwnProperty.call(e, "quiet") ? e.quiet : B(n).quiet);
226
226
  if (r || !f) {
227
227
  let c = Object.keys(p).length, a = [];
228
228
  for (let g of s)
229
229
  try {
230
- let h = j.relative(process.cwd(), g instanceof Z ? ee(g) : g);
230
+ let h = k.relative(process.cwd(), g instanceof Z ? ee(g) : g);
231
231
  a.push(h);
232
232
  } catch (h) {
233
233
  r && T(`failed to load ${g} ${h.message}`), i = h;
@@ -240,7 +240,7 @@ var require_dist = __commonJS(function(exports, module) {
240
240
  return A.configDotenv(e);
241
241
  }
242
242
  function le(e, o, t = {}) {
243
- let n = !!(t && t.debug), r = !!(t && t.override), s = {};
243
+ let n = $(t && t.debug), r = $(t && t.override), s = {};
244
244
  if (e === null || typeof e != "object" || o === null || typeof o != "object") {
245
245
  let i = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
246
246
  throw i.code = "OBJECT_REQUIRED", i;
@@ -256,7 +256,7 @@ var require_dist = __commonJS(function(exports, module) {
256
256
  x.exports.populate = A.populate;
257
257
  x.exports = A;
258
258
  });
259
- var M = I((Te, W) => {
259
+ var M = S((Te, W) => {
260
260
  var _ = __require("child_process"), fe = __require("fs"), L = __require("path");
261
261
  function ue(e) {
262
262
  let o = ['"'], t = 0;
@@ -309,9 +309,9 @@ var require_dist = __commonJS(function(exports, module) {
309
309
  }
310
310
  W.exports = pe;
311
311
  });
312
- var K = I(($e, F) => {
313
- var he = __require("fs"), ge = __require("os"), Q = __require("path"), me = __require("child_process"), ve = M(), R = N(), { optionsFromEnv: Ce } = S();
314
- function $() {
312
+ var K = S(($e, F) => {
313
+ var he = __require("fs"), ge = __require("os"), Q = __require("path"), me = __require("child_process"), ve = M(), R = N(), { optionsFromEnv: Ce } = j();
314
+ function D() {
315
315
  console.log(["Usage: dotenv run [--help] [-q|--quiet] [--debug] [--override] [--fast] [-f|--file <paths>] [--] <command> [args...]", "", "Run a command with environment variables from a .env file.", "Place dotenv options before the command; all following arguments go to the command.", "", "Options:", " -f, --file <paths> .env paths, comma-separated or repeated (default: .env)", " -q, --quiet suppress the injected env message", " --debug enable debug logging", " --override override existing environment variables", " --fast use the faster character-scanner parser", "", "Environment variables (DOTENV_CONFIG_* names remain as fallbacks):", " DOTENV_PATH, DOTENV_ENCODING, DOTENV_QUIET,", " DOTENV_DEBUG, DOTENV_OVERRIDE,", " DOTENV_FAST"].join(`
316
316
  `));
317
317
  }
@@ -383,24 +383,24 @@ var require_dist = __commonJS(function(exports, module) {
383
383
  function J(e) {
384
384
  let o = e[0];
385
385
  if (o === "--help" || o === "-h") {
386
- $();
386
+ D();
387
387
  return;
388
388
  }
389
389
  if (o !== "run") {
390
- $(), process.exitCode = 1;
390
+ D(), process.exitCode = 1;
391
391
  return;
392
392
  }
393
393
  let t = we(e.slice(1));
394
394
  if (t.help) {
395
- $();
395
+ D();
396
396
  return;
397
397
  }
398
398
  if (t.error) {
399
- console.error(`dotenv: ${t.error}`), $(), process.exitCode = 1;
399
+ console.error(`dotenv: ${t.error}`), D(), process.exitCode = 1;
400
400
  return;
401
401
  }
402
402
  if (t.command.length === 0) {
403
- $(), process.exitCode = 1;
403
+ D(), process.exitCode = 1;
404
404
  return;
405
405
  }
406
406
  let n = Ee(t);
@@ -456,13 +456,13 @@ var require_dist = __commonJS(function(exports, module) {
456
456
  F.exports = J;
457
457
  F === __require.main && J(process.argv.slice(2));
458
458
  });
459
- var D = N();
459
+ var I = N();
460
460
  var Oe = K();
461
- module.exports = D;
462
- module.exports.config = D.config;
463
- module.exports.configDotenv = D.configDotenv;
464
- module.exports.parse = D.parse;
465
- module.exports.populate = D.populate;
461
+ module.exports = I;
462
+ module.exports.config = I.config;
463
+ module.exports.configDotenv = I.configDotenv;
464
+ module.exports.parse = I.parse;
465
+ module.exports.populate = I.populate;
466
466
  });
467
467
 
468
468
  // src/load-env.ts
@@ -7453,6 +7453,14 @@ var typedOperations = {
7453
7453
  kind: "query",
7454
7454
  invoke: (client, input) => client.team.listRoutingGroups.query(input)
7455
7455
  },
7456
+ "team.listRoutingGroupReleaseHistory": {
7457
+ kind: "query",
7458
+ invoke: (client, input) => client.team.listRoutingGroupReleaseHistory.query(input)
7459
+ },
7460
+ "team.updateRoutingGroupRelease": {
7461
+ kind: "mutation",
7462
+ invoke: (client, input) => client.team.updateRoutingGroupRelease.mutate(input)
7463
+ },
7456
7464
  "team.createRoutingGroup": {
7457
7465
  kind: "mutation",
7458
7466
  invoke: (client, input) => client.team.createRoutingGroup.mutate(input)
@@ -7865,6 +7873,7 @@ var defaultMutationTargetKeys = [
7865
7873
  "name"
7866
7874
  ];
7867
7875
  var mutationTargetKeys = {
7876
+ "team.updateRoutingGroupRelease": ["groupId", "eventId"],
7868
7877
  "team.setGameplaySettings": ["eventId"],
7869
7878
  "team.createRoutingGroup": ["eventId"],
7870
7879
  "team.renameRoutingGroup": ["groupId"],
@@ -7985,6 +7994,8 @@ var commands = [
7985
7994
  mutation(["team", "set-station-routes"], "team.setStationRoutes", "Replace a team's linked station route and set its order.", ["state-change"]),
7986
7995
  mutation(["event", "set-gameplay"], "team.setGameplaySettings", "Set open or sequential progression, group routing, and per-mission skipping before play starts.", ["state-change"]),
7987
7996
  query(["routing-group", "list"], "team.listRoutingGroups", "List an event's routing groups, ordered stations, and assigned teams."),
7997
+ query(["routing-group", "release-history"], "team.listRoutingGroupReleaseHistory", "Show a routing group's five most recent release changes with actor, action and before/after UTC values."),
7998
+ mutation(["routing-group", "release"], "team.updateRoutingGroupRelease", "Schedule (future releaseAt ISO timestamp with offset), CLEAR schedule, HOLD, RELEASE_SCHEDULED_HOLD (keep schedule), or RELEASE_NOW for a routing group. After a group starts, only HOLD and RELEASE_SCHEDULED_HOLD are accepted. Requires launchEvent permission.", ["state-change"]),
7988
7999
  mutation(["routing-group", "create"], "team.createRoutingGroup", "Create a routing group within an event before play starts.", ["state-change"]),
7989
8000
  mutation(["routing-group", "rename"], "team.renameRoutingGroup", "Rename an event's routing group before play starts.", ["state-change"]),
7990
8001
  mutation(["routing-group", "delete"], "team.deleteRoutingGroup", "Delete an unassigned routing group before play starts.", ["state-change", "delete"]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/gosh",
3
- "version": "0.12.0",
3
+ "version": "0.14.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",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "@trpc/client": "^11.19.0",
22
- "dotenv": "^18.0.1",
22
+ "dotenv": "^18.0.6",
23
23
  "superjson": "^2.2.6"
24
24
  },
25
25
  "engines": {