@go-labs-sg/gosh 0.11.0 → 0.12.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 +45 -0
- package/command-manifest.json +12 -0
- package/command-reference.md +12 -0
- package/dist/index.js +273 -122
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
`@go-labs-sg/gosh` provides the `gosh` binary for permission-aware GOSH organizer operations and agent automation. It calls the same tRPC procedures as the web app using the authorizing user's global, organization, and event permissions.
|
|
4
4
|
|
|
5
|
+
## First step: update before use
|
|
6
|
+
|
|
7
|
+
Before starting a task, **update to the latest published release first**, including when an AI agent runs this CLI:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add --global @go-labs-sg/gosh@latest
|
|
11
|
+
gosh version
|
|
12
|
+
gosh --help
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Run this sequence once per task, not before every command. Re-read the updated help before choosing commands or flags. If updating fails, stop and report the failure rather than silently using an outdated CLI. If the user explicitly requested a pinned version or a development/staging build, preserve that version instead of replacing it with the public release.
|
|
16
|
+
|
|
17
|
+
New installation? Follow the runtime and installation instructions below to install Bun first, then return to this step.
|
|
18
|
+
|
|
5
19
|
## Runtime and installation
|
|
6
20
|
|
|
7
21
|
The CLI requires [Bun](https://bun.com/docs/installation) 1.4.0 or newer. npm remains the package distribution channel, while the installed `gosh` command runs under Bun.
|
|
@@ -237,6 +251,37 @@ measures mission work only: travel between stations and staff review time are
|
|
|
237
251
|
excluded. Existing leaderboard and results commands include the resulting
|
|
238
252
|
timing information.
|
|
239
253
|
|
|
254
|
+
### Artwork requirements (no login required)
|
|
255
|
+
|
|
256
|
+
Read the bundled upload policy before preparing station, mission, or branding artwork:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
gosh assets requirements --type station
|
|
260
|
+
gosh assets requirements --type mission --format text
|
|
261
|
+
gosh assets requirements
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The default output is the standard JSON envelope; `--format text` gives a readable brief.
|
|
265
|
+
Omitting `--type` lists every supported artwork type. `gosh commands`,
|
|
266
|
+
`gosh help assets requirements`, and the upload-command help expose this guidance too.
|
|
267
|
+
This command is local and read-only: it does not access credentials, contact the API,
|
|
268
|
+
or upload a file. API URL configuration is not used or validated for this command or
|
|
269
|
+
its help. Its policy comes from the installed CLI version, not a live deployment.
|
|
270
|
+
Update the CLI when newer policy definitions are published.
|
|
271
|
+
|
|
272
|
+
- Station and mission setup images: JPEG, PNG, WebP; at most **10 MiB (10,485,760 bytes)**.
|
|
273
|
+
- Branding images (logo, sponsor-logo, login-banner, map-marker, player-background):
|
|
274
|
+
the same formats; at most **5 MiB (5,242,880 bytes)**.
|
|
275
|
+
- Neither artwork upload policy enforces pixel dimensions or aspect ratio.
|
|
276
|
+
- **Recommendations, not requirements:** station artwork 1920 × 1080 (16:9),
|
|
277
|
+
mission artwork 1600 × 1000 (16:10). Participant cover containers can crop artwork;
|
|
278
|
+
the standard station card also becomes shorter on completion. Keep essential
|
|
279
|
+
text in station/mission text fields and important image content away from the edges.
|
|
280
|
+
- Logos, login banners, map markers, and player backgrounds have separate display
|
|
281
|
+
guidance; no exact canvas is prescribed for them. Preview on the relevant mobile surface.
|
|
282
|
+
|
|
283
|
+
These policies describe organizer artwork, not participant submission media or profile images.
|
|
284
|
+
|
|
240
285
|
### Local file uploads
|
|
241
286
|
|
|
242
287
|
Upload commands add the local file metadata to the supplied API input, request a permission-scoped signed URL, and upload the bytes directly to storage without sending the GOSH CLI access token to storage:
|
package/command-manifest.json
CHANGED
|
@@ -2947,5 +2947,17 @@
|
|
|
2947
2947
|
"slug",
|
|
2948
2948
|
"name"
|
|
2949
2949
|
]
|
|
2950
|
+
},
|
|
2951
|
+
{
|
|
2952
|
+
"path": ["assets", "requirements"],
|
|
2953
|
+
"operation": "local",
|
|
2954
|
+
"input": "none",
|
|
2955
|
+
"requiresAuth": false,
|
|
2956
|
+
"effects": [],
|
|
2957
|
+
"summary": "Show upload limits and separate artwork recommendations without login or network access.",
|
|
2958
|
+
"options": {
|
|
2959
|
+
"--type": "Select station, mission, logo, sponsor-logo, login-banner, map-marker, player-background; omit for all asset types.",
|
|
2960
|
+
"--format": "json (default, standard CLI envelope) or text."
|
|
2961
|
+
}
|
|
2950
2962
|
}
|
|
2951
2963
|
]
|
package/command-reference.md
CHANGED
|
@@ -953,6 +953,7 @@ Upload an event branding asset from a local file.
|
|
|
953
953
|
- Authentication: browser-authenticated CLI session required
|
|
954
954
|
- Input: `--input '<json>'` required
|
|
955
955
|
- Effects: state-change, external-write
|
|
956
|
+
- Artwork guidance: `gosh assets requirements` (bundled policy, no login required).
|
|
956
957
|
|
|
957
958
|
## `gosh upload setup-image`
|
|
958
959
|
|
|
@@ -961,6 +962,7 @@ Upload a station or mission setup image from a local file.
|
|
|
961
962
|
- Authentication: browser-authenticated CLI session required
|
|
962
963
|
- Input: `--input '<json>'` required
|
|
963
964
|
- Effects: state-change, external-write
|
|
965
|
+
- Artwork guidance: `gosh assets requirements` (bundled policy, no login required).
|
|
964
966
|
|
|
965
967
|
## `gosh upload profile-image`
|
|
966
968
|
|
|
@@ -1153,3 +1155,13 @@ Unban a user.
|
|
|
1153
1155
|
- Authentication: browser-authenticated CLI session required
|
|
1154
1156
|
- Input: `--input '<json>'` required
|
|
1155
1157
|
- Effects: state-change
|
|
1158
|
+
|
|
1159
|
+
## `gosh assets requirements`
|
|
1160
|
+
|
|
1161
|
+
Show upload limits and separate artwork recommendations without login or network access.
|
|
1162
|
+
|
|
1163
|
+
- Authentication: not required
|
|
1164
|
+
- Input: none
|
|
1165
|
+
- Effects: read-only
|
|
1166
|
+
- --type: Select station, mission, logo, sponsor-logo, login-banner, map-marker, player-background; omit for all asset types.
|
|
1167
|
+
- --format: json (default, standard CLI envelope) or text.
|
package/dist/index.js
CHANGED
|
@@ -6716,126 +6716,64 @@ var logoutCliSession = async ({
|
|
|
6716
6716
|
return { remoteRevoked };
|
|
6717
6717
|
});
|
|
6718
6718
|
};
|
|
6719
|
-
// src/
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
if (this.closed)
|
|
6766
|
-
return;
|
|
6767
|
-
this.readyState = this.CONNECTING;
|
|
6768
|
-
this.abortController = new AbortController;
|
|
6769
|
-
try {
|
|
6770
|
-
const response = await fetch(this.url, {
|
|
6771
|
-
headers: {
|
|
6772
|
-
Accept: "text/event-stream",
|
|
6773
|
-
...this.init.headers,
|
|
6774
|
-
...this.lastEventId ? { "Last-Event-ID": this.lastEventId } : {}
|
|
6775
|
-
},
|
|
6776
|
-
signal: this.abortController.signal
|
|
6777
|
-
});
|
|
6778
|
-
if (!response.ok || !response.body) {
|
|
6779
|
-
throw new Error(`SSE connection failed with HTTP ${response.status}.`);
|
|
6780
|
-
}
|
|
6781
|
-
this.readyState = this.OPEN;
|
|
6782
|
-
this.dispatch("open", new Event("open"));
|
|
6783
|
-
await this.consume(response.body);
|
|
6784
|
-
} catch (error) {
|
|
6785
|
-
if (!this.closed) {
|
|
6786
|
-
this.dispatch("error", new ErrorEvent("error", {
|
|
6787
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
6788
|
-
}));
|
|
6789
|
-
}
|
|
6790
|
-
}
|
|
6791
|
-
if (!this.closed) {
|
|
6792
|
-
setTimeout(() => void this.connect(), this.retryMilliseconds);
|
|
6793
|
-
}
|
|
6794
|
-
}
|
|
6795
|
-
async consume(body) {
|
|
6796
|
-
const reader = body.getReader();
|
|
6797
|
-
const decoder = new TextDecoder;
|
|
6798
|
-
let pending = "";
|
|
6799
|
-
while (!this.closed) {
|
|
6800
|
-
const { done, value } = await reader.read();
|
|
6801
|
-
pending += decoder.decode(value, { stream: !done });
|
|
6802
|
-
const blocks = pending.split(/\r?\n\r?\n/);
|
|
6803
|
-
pending = blocks.pop() ?? "";
|
|
6804
|
-
for (const block of blocks)
|
|
6805
|
-
this.processBlock(block);
|
|
6806
|
-
if (done)
|
|
6807
|
-
break;
|
|
6808
|
-
}
|
|
6809
|
-
}
|
|
6810
|
-
processBlock(block) {
|
|
6811
|
-
let data = "";
|
|
6812
|
-
let eventType = "message";
|
|
6813
|
-
for (const line of block.split(/\r?\n/)) {
|
|
6814
|
-
if (!line || line.startsWith(":"))
|
|
6815
|
-
continue;
|
|
6816
|
-
const separator = line.indexOf(":");
|
|
6817
|
-
const field = separator === -1 ? line : line.slice(0, separator);
|
|
6818
|
-
const rawValue = separator === -1 ? "" : line.slice(separator + 1);
|
|
6819
|
-
const value = rawValue.startsWith(" ") ? rawValue.slice(1) : rawValue;
|
|
6820
|
-
if (field === "data")
|
|
6821
|
-
data += `${value}
|
|
6822
|
-
`;
|
|
6823
|
-
if (field === "event")
|
|
6824
|
-
eventType = value;
|
|
6825
|
-
if (field === "id")
|
|
6826
|
-
this.lastEventId = value;
|
|
6827
|
-
if (field === "retry" && /^\d+$/.test(value)) {
|
|
6828
|
-
this.retryMilliseconds = Number(value);
|
|
6829
|
-
}
|
|
6830
|
-
}
|
|
6831
|
-
if (!data)
|
|
6832
|
-
return;
|
|
6833
|
-
this.dispatch(eventType, new MessageEvent(eventType, {
|
|
6834
|
-
data: data.slice(0, -1),
|
|
6835
|
-
lastEventId: this.lastEventId
|
|
6836
|
-
}));
|
|
6719
|
+
// ../validators/src/asset-requirements.ts
|
|
6720
|
+
var goshImageContentTypes = ["image/jpeg", "image/png", "image/webp"];
|
|
6721
|
+
var goshImageAccept = goshImageContentTypes.join(",");
|
|
6722
|
+
var maxGoshSetupImageBytes = 10 * 1024 * 1024;
|
|
6723
|
+
var maxGoshBrandingImageBytes = 5 * 1024 * 1024;
|
|
6724
|
+
var goshAssetTypes = [
|
|
6725
|
+
"station",
|
|
6726
|
+
"mission",
|
|
6727
|
+
"logo",
|
|
6728
|
+
"sponsor-logo",
|
|
6729
|
+
"login-banner",
|
|
6730
|
+
"map-marker",
|
|
6731
|
+
"player-background"
|
|
6732
|
+
];
|
|
6733
|
+
var logoGuidance = {
|
|
6734
|
+
recommendedCanvas: null,
|
|
6735
|
+
display: "Logo containers use contain sizing; available space varies by surface.",
|
|
6736
|
+
artworkAdvice: "Use PNG or WebP for transparency. Keep the logo legible at small sizes; there is no prescribed canvas."
|
|
6737
|
+
};
|
|
6738
|
+
var guidance2 = {
|
|
6739
|
+
station: {
|
|
6740
|
+
recommendedCanvas: { width: 1920, height: 1080, aspectRatio: "16:9" },
|
|
6741
|
+
display: "The standard event-home station card uses a 16:9 cover crop and a shorter crop when completed. Other station surfaces differ.",
|
|
6742
|
+
artworkAdvice: "Keep important text and logos away from the edges. Prefer putting essential instructions in station text rather than the image."
|
|
6743
|
+
},
|
|
6744
|
+
mission: {
|
|
6745
|
+
recommendedCanvas: { width: 1600, height: 1000, aspectRatio: "16:10" },
|
|
6746
|
+
display: "Participant mission cards and mission artwork use 16:10 cover crops.",
|
|
6747
|
+
artworkAdvice: "Keep important content away from the edges. Use mission text for essential instructions."
|
|
6748
|
+
},
|
|
6749
|
+
logo: logoGuidance,
|
|
6750
|
+
"sponsor-logo": logoGuidance,
|
|
6751
|
+
"login-banner": {
|
|
6752
|
+
recommendedCanvas: null,
|
|
6753
|
+
display: "The login banner uses full container width and automatic height; there is no prescribed upload ratio.",
|
|
6754
|
+
artworkAdvice: "Preview in the mobile login card. No exact canvas is prescribed; do not assume the station's 16:9 recommendation applies."
|
|
6755
|
+
},
|
|
6756
|
+
"map-marker": {
|
|
6757
|
+
recommendedCanvas: null,
|
|
6758
|
+
display: "Custom markers appear in small map-marker containers.",
|
|
6759
|
+
artworkAdvice: "Use a simple, legible icon and PNG or WebP for transparency. Preview on the participant map."
|
|
6760
|
+
},
|
|
6761
|
+
"player-background": {
|
|
6762
|
+
recommendedCanvas: null,
|
|
6763
|
+
display: "Participant backgrounds use centered cover sizing and can crop differently with the viewport.",
|
|
6764
|
+
artworkAdvice: "Use decorative artwork without essential text. Check both portrait and landscape previews."
|
|
6837
6765
|
}
|
|
6838
|
-
}
|
|
6766
|
+
};
|
|
6767
|
+
var getGoshAssetRequirements = (type) => ({
|
|
6768
|
+
type,
|
|
6769
|
+
requirements: {
|
|
6770
|
+
acceptedContentTypes: goshImageContentTypes,
|
|
6771
|
+
maxBytes: type === "station" || type === "mission" ? maxGoshSetupImageBytes : maxGoshBrandingImageBytes,
|
|
6772
|
+
dimensions: null,
|
|
6773
|
+
aspectRatio: null
|
|
6774
|
+
},
|
|
6775
|
+
recommendations: guidance2[type]
|
|
6776
|
+
});
|
|
6839
6777
|
|
|
6840
6778
|
// src/runtime/index.ts
|
|
6841
6779
|
import { createInterface } from "readline/promises";
|
|
@@ -7075,6 +7013,168 @@ var createProcessRuntime = () => ({
|
|
|
7075
7013
|
}
|
|
7076
7014
|
});
|
|
7077
7015
|
|
|
7016
|
+
// src/features/asset-requirements.ts
|
|
7017
|
+
var assetRequirementsCommand = {
|
|
7018
|
+
path: ["assets", "requirements"],
|
|
7019
|
+
operation: "local",
|
|
7020
|
+
input: "none",
|
|
7021
|
+
requiresAuth: false,
|
|
7022
|
+
effects: [],
|
|
7023
|
+
summary: "Show upload limits and separate artwork recommendations without login or network access.",
|
|
7024
|
+
options: {
|
|
7025
|
+
"--type": `Select ${goshAssetTypes.join(", ")}; omit for all asset types.`,
|
|
7026
|
+
"--format": "json (default, standard CLI envelope) or text."
|
|
7027
|
+
}
|
|
7028
|
+
};
|
|
7029
|
+
var getAssetRequirementsOutput = (type) => {
|
|
7030
|
+
const selectedTypes = type !== undefined ? goshAssetTypes.filter((candidate) => candidate === type) : goshAssetTypes;
|
|
7031
|
+
if (selectedTypes.length === 0) {
|
|
7032
|
+
throw new CliError("USAGE", `Unknown asset type: ${type}. Choose ${goshAssetTypes.join(", ")}.`);
|
|
7033
|
+
}
|
|
7034
|
+
return {
|
|
7035
|
+
provenance: "Bundled CLI policy; not a live-server verification. Update the CLI to obtain newer policy definitions.",
|
|
7036
|
+
assets: selectedTypes.map(getGoshAssetRequirements)
|
|
7037
|
+
};
|
|
7038
|
+
};
|
|
7039
|
+
var formatAssetRequirementsText = (output) => [
|
|
7040
|
+
output.provenance,
|
|
7041
|
+
...output.assets.map(({ type, requirements, recommendations }) => {
|
|
7042
|
+
const canvas = recommendations.recommendedCanvas;
|
|
7043
|
+
return [
|
|
7044
|
+
`
|
|
7045
|
+
${type}`,
|
|
7046
|
+
`Required: JPEG, PNG, WebP; maximum ${requirements.maxBytes} bytes (${requirements.maxBytes / (1024 * 1024)} MiB).`,
|
|
7047
|
+
"Enforced pixel dimensions / aspect ratio: none.",
|
|
7048
|
+
`Recommended canvas (not required): ${canvas ? `${canvas.width} \xD7 ${canvas.height} px (${canvas.aspectRatio})` : "not prescribed"}.`,
|
|
7049
|
+
`Display: ${recommendations.display}`,
|
|
7050
|
+
`Artwork advice: ${recommendations.artworkAdvice}`
|
|
7051
|
+
].join(`
|
|
7052
|
+
`);
|
|
7053
|
+
})
|
|
7054
|
+
].join(`
|
|
7055
|
+
`);
|
|
7056
|
+
|
|
7057
|
+
// src/api-client.ts
|
|
7058
|
+
import {
|
|
7059
|
+
createTRPCProxyClient,
|
|
7060
|
+
httpBatchLink,
|
|
7061
|
+
httpSubscriptionLink,
|
|
7062
|
+
loggerLink,
|
|
7063
|
+
splitLink
|
|
7064
|
+
} from "@trpc/client";
|
|
7065
|
+
import SuperJSON from "superjson";
|
|
7066
|
+
|
|
7067
|
+
// src/features/authenticated-event-source.ts
|
|
7068
|
+
class AuthenticatedEventSource {
|
|
7069
|
+
url;
|
|
7070
|
+
init;
|
|
7071
|
+
CLOSED = 2;
|
|
7072
|
+
CONNECTING = 0;
|
|
7073
|
+
OPEN = 1;
|
|
7074
|
+
readyState = this.CONNECTING;
|
|
7075
|
+
abortController = null;
|
|
7076
|
+
closed = false;
|
|
7077
|
+
lastEventId = "";
|
|
7078
|
+
listeners = new Map;
|
|
7079
|
+
retryMilliseconds = 1000;
|
|
7080
|
+
constructor(url, init = {}) {
|
|
7081
|
+
this.url = url;
|
|
7082
|
+
this.init = init;
|
|
7083
|
+
this.connect();
|
|
7084
|
+
}
|
|
7085
|
+
addEventListener(type, listener) {
|
|
7086
|
+
const listeners = this.listeners.get(type) ?? new Set;
|
|
7087
|
+
listeners.add(listener);
|
|
7088
|
+
this.listeners.set(type, listeners);
|
|
7089
|
+
}
|
|
7090
|
+
removeEventListener(type, listener) {
|
|
7091
|
+
this.listeners.get(type)?.delete(listener);
|
|
7092
|
+
}
|
|
7093
|
+
close = () => {
|
|
7094
|
+
this.closed = true;
|
|
7095
|
+
this.readyState = this.CLOSED;
|
|
7096
|
+
this.abortController?.abort();
|
|
7097
|
+
};
|
|
7098
|
+
dispatch(type, event) {
|
|
7099
|
+
for (const listener of this.listeners.get(type) ?? [])
|
|
7100
|
+
listener(event);
|
|
7101
|
+
}
|
|
7102
|
+
async connect() {
|
|
7103
|
+
if (this.closed)
|
|
7104
|
+
return;
|
|
7105
|
+
this.readyState = this.CONNECTING;
|
|
7106
|
+
this.abortController = new AbortController;
|
|
7107
|
+
try {
|
|
7108
|
+
const response = await fetch(this.url, {
|
|
7109
|
+
headers: {
|
|
7110
|
+
Accept: "text/event-stream",
|
|
7111
|
+
...this.init.headers,
|
|
7112
|
+
...this.lastEventId ? { "Last-Event-ID": this.lastEventId } : {}
|
|
7113
|
+
},
|
|
7114
|
+
signal: this.abortController.signal
|
|
7115
|
+
});
|
|
7116
|
+
if (!response.ok || !response.body) {
|
|
7117
|
+
throw new Error(`SSE connection failed with HTTP ${response.status}.`);
|
|
7118
|
+
}
|
|
7119
|
+
this.readyState = this.OPEN;
|
|
7120
|
+
this.dispatch("open", new Event("open"));
|
|
7121
|
+
await this.consume(response.body);
|
|
7122
|
+
} catch (error) {
|
|
7123
|
+
if (!this.closed) {
|
|
7124
|
+
this.dispatch("error", new ErrorEvent("error", {
|
|
7125
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
7126
|
+
}));
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
if (!this.closed) {
|
|
7130
|
+
setTimeout(() => void this.connect(), this.retryMilliseconds);
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7133
|
+
async consume(body) {
|
|
7134
|
+
const reader = body.getReader();
|
|
7135
|
+
const decoder = new TextDecoder;
|
|
7136
|
+
let pending = "";
|
|
7137
|
+
while (!this.closed) {
|
|
7138
|
+
const { done, value } = await reader.read();
|
|
7139
|
+
pending += decoder.decode(value, { stream: !done });
|
|
7140
|
+
const blocks = pending.split(/\r?\n\r?\n/);
|
|
7141
|
+
pending = blocks.pop() ?? "";
|
|
7142
|
+
for (const block of blocks)
|
|
7143
|
+
this.processBlock(block);
|
|
7144
|
+
if (done)
|
|
7145
|
+
break;
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
processBlock(block) {
|
|
7149
|
+
let data = "";
|
|
7150
|
+
let eventType = "message";
|
|
7151
|
+
for (const line of block.split(/\r?\n/)) {
|
|
7152
|
+
if (!line || line.startsWith(":"))
|
|
7153
|
+
continue;
|
|
7154
|
+
const separator = line.indexOf(":");
|
|
7155
|
+
const field = separator === -1 ? line : line.slice(0, separator);
|
|
7156
|
+
const rawValue = separator === -1 ? "" : line.slice(separator + 1);
|
|
7157
|
+
const value = rawValue.startsWith(" ") ? rawValue.slice(1) : rawValue;
|
|
7158
|
+
if (field === "data")
|
|
7159
|
+
data += `${value}
|
|
7160
|
+
`;
|
|
7161
|
+
if (field === "event")
|
|
7162
|
+
eventType = value;
|
|
7163
|
+
if (field === "id")
|
|
7164
|
+
this.lastEventId = value;
|
|
7165
|
+
if (field === "retry" && /^\d+$/.test(value)) {
|
|
7166
|
+
this.retryMilliseconds = Number(value);
|
|
7167
|
+
}
|
|
7168
|
+
}
|
|
7169
|
+
if (!data)
|
|
7170
|
+
return;
|
|
7171
|
+
this.dispatch(eventType, new MessageEvent(eventType, {
|
|
7172
|
+
data: data.slice(0, -1),
|
|
7173
|
+
lastEventId: this.lastEventId
|
|
7174
|
+
}));
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
|
|
7078
7178
|
// src/config.ts
|
|
7079
7179
|
var DEFAULT_GOSH_API_BASE_URL = "https://app.gosh.sg";
|
|
7080
7180
|
var normalizeApiBaseUrl = (value) => {
|
|
@@ -8228,10 +8328,14 @@ var allowedFlags = new Set([
|
|
|
8228
8328
|
"input",
|
|
8229
8329
|
"no-browser",
|
|
8230
8330
|
"quiet",
|
|
8231
|
-
"target"
|
|
8331
|
+
"target",
|
|
8332
|
+
"type",
|
|
8333
|
+
"format"
|
|
8232
8334
|
]);
|
|
8233
8335
|
var commandName = (command) => command.path.join(" ");
|
|
8336
|
+
var updateBeforeUse = "First step before each task: update to the latest published release with 'bun add --global @go-labs-sg/gosh@latest', then run 'gosh version' and 'gosh --help' before using commands. Update once per task. If updating fails, stop and report it. Preserve an explicitly requested pinned or development/staging version.";
|
|
8234
8337
|
var commandHelp = (command) => ({
|
|
8338
|
+
beforeUse: updateBeforeUse,
|
|
8235
8339
|
effects: command.effects,
|
|
8236
8340
|
input: command.input === "json" ? "Pass the exact tRPC input object with --input '<json>'." : "This command does not accept --input.",
|
|
8237
8341
|
path: command.path,
|
|
@@ -8241,9 +8345,14 @@ var commandHelp = (command) => ({
|
|
|
8241
8345
|
target: "Pass --target with the exact resource being changed."
|
|
8242
8346
|
},
|
|
8243
8347
|
summary: command.summary,
|
|
8348
|
+
...command.upload === "setup-image" || command.upload === "branding" ? {
|
|
8349
|
+
assetRequirements: goshAssetTypes.filter((type) => command.upload === "setup-image" ? type === "station" || type === "mission" : type !== "station" && type !== "mission").map(getGoshAssetRequirements),
|
|
8350
|
+
assetRequirementsCommand: "gosh assets requirements"
|
|
8351
|
+
} : {},
|
|
8244
8352
|
usage: `gosh ${commandName(command)}${command.input === "json" ? " --input '<json>'" : ""}`
|
|
8245
8353
|
});
|
|
8246
8354
|
var rootHelp = {
|
|
8355
|
+
beforeUse: updateBeforeUse,
|
|
8247
8356
|
authentication: {
|
|
8248
8357
|
instructions: "Run 'gosh auth login'. Approve the device in your browser; credentials are stored through Bun.secrets."
|
|
8249
8358
|
},
|
|
@@ -8251,6 +8360,7 @@ var rootHelp = {
|
|
|
8251
8360
|
"gosh help [command path]",
|
|
8252
8361
|
"gosh commands",
|
|
8253
8362
|
"gosh version",
|
|
8363
|
+
"gosh assets requirements [--type station] [--format text]",
|
|
8254
8364
|
"gosh <command path> [--input '<json>']"
|
|
8255
8365
|
],
|
|
8256
8366
|
globalOptions: {
|
|
@@ -8286,6 +8396,16 @@ var emitHelp = (runtime, apiBaseUrl, positional) => {
|
|
|
8286
8396
|
command: "help"
|
|
8287
8397
|
}, rootHelp);
|
|
8288
8398
|
}
|
|
8399
|
+
if (positional.join(" ") === "assets requirements") {
|
|
8400
|
+
return emitSuccess(runtime, {
|
|
8401
|
+
apiBaseUrl,
|
|
8402
|
+
cliVersion: GOSH_CLI_VERSION,
|
|
8403
|
+
command: "help assets requirements"
|
|
8404
|
+
}, {
|
|
8405
|
+
...assetRequirementsCommand,
|
|
8406
|
+
usage: "gosh assets requirements [--type station] [--format json|text]"
|
|
8407
|
+
});
|
|
8408
|
+
}
|
|
8289
8409
|
const resolved = findCommand(positional);
|
|
8290
8410
|
if (!resolved || resolved.consumed !== positional.length) {
|
|
8291
8411
|
throw new CliError("USAGE", `Unknown GOSH command: ${positional.join(" ")}`);
|
|
@@ -8302,8 +8422,14 @@ var runGoshCli = async (argv, dependencies) => {
|
|
|
8302
8422
|
let apiBaseUrl = DEFAULT_GOSH_API_BASE_URL;
|
|
8303
8423
|
try {
|
|
8304
8424
|
validateFlags(flags);
|
|
8305
|
-
apiBaseUrl = resolveApiBaseUrl(dependencies.runtime, flags);
|
|
8306
8425
|
const [first, ...rest] = positional;
|
|
8426
|
+
const isAssetRequirements = (first === "help" ? rest : positional).join(" ") === "assets requirements";
|
|
8427
|
+
if (!isAssetRequirements) {
|
|
8428
|
+
apiBaseUrl = resolveApiBaseUrl(dependencies.runtime, flags);
|
|
8429
|
+
}
|
|
8430
|
+
if (!isAssetRequirements && (flags.type !== undefined || flags.format !== undefined)) {
|
|
8431
|
+
throw new CliError("USAGE", "--type and --format are only supported by assets requirements.");
|
|
8432
|
+
}
|
|
8307
8433
|
if (first === undefined || first === "help" || flags.help === true) {
|
|
8308
8434
|
return emitHelp(dependencies.runtime, apiBaseUrl, first === "help" ? rest : positional);
|
|
8309
8435
|
}
|
|
@@ -8325,7 +8451,32 @@ var runGoshCli = async (argv, dependencies) => {
|
|
|
8325
8451
|
apiBaseUrl,
|
|
8326
8452
|
cliVersion: GOSH_CLI_VERSION,
|
|
8327
8453
|
command: "commands"
|
|
8328
|
-
}, commandManifest);
|
|
8454
|
+
}, [...commandManifest, assetRequirementsCommand]);
|
|
8455
|
+
}
|
|
8456
|
+
if (isAssetRequirements && first !== "help") {
|
|
8457
|
+
for (const flag of Object.keys(flags)) {
|
|
8458
|
+
if (!["type", "format", "api-url", "debug", "quiet", "help"].includes(flag)) {
|
|
8459
|
+
throw new CliError("USAGE", `assets requirements does not accept --${flag}.`);
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
if (flags.type === true || flags.format === true) {
|
|
8463
|
+
throw new CliError("USAGE", "--type and --format require values.");
|
|
8464
|
+
}
|
|
8465
|
+
const format = getStringFlag(flags, "format") ?? "json";
|
|
8466
|
+
if (format !== "json" && format !== "text") {
|
|
8467
|
+
throw new CliError("USAGE", "--format must be json or text.");
|
|
8468
|
+
}
|
|
8469
|
+
const output = getAssetRequirementsOutput(getStringFlag(flags, "type"));
|
|
8470
|
+
if (format === "text") {
|
|
8471
|
+
dependencies.runtime.stdout.write(`${formatAssetRequirementsText(output)}
|
|
8472
|
+
`);
|
|
8473
|
+
return 0;
|
|
8474
|
+
}
|
|
8475
|
+
return emitSuccess(dependencies.runtime, {
|
|
8476
|
+
apiBaseUrl,
|
|
8477
|
+
cliVersion: GOSH_CLI_VERSION,
|
|
8478
|
+
command: "assets requirements"
|
|
8479
|
+
}, output);
|
|
8329
8480
|
}
|
|
8330
8481
|
const credentialStore = dependencies.credentialStore ?? createBunCredentialStore({
|
|
8331
8482
|
account: resolveCliCredentialAccount(dependencies.runtime.env),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/gosh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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",
|