@go-labs-sg/gosh 0.2.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +386 -384
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15069,6 +15069,291 @@ class AuthenticatedEventSource {
15069
15069
  }
15070
15070
  }
15071
15071
 
15072
+ // src/runtime.ts
15073
+ import { createInterface } from "readline/promises";
15074
+ var invocationCodes = new Set([
15075
+ "USAGE",
15076
+ "VALIDATION",
15077
+ "CONFIRMATION_REQUIRED",
15078
+ "ABORTED"
15079
+ ]);
15080
+ var trpcCodeMap = {
15081
+ BAD_REQUEST: "VALIDATION",
15082
+ CONFLICT: "CONFLICT",
15083
+ FORBIDDEN: "AUTHORIZATION",
15084
+ INTERNAL_SERVER_ERROR: "API",
15085
+ NOT_FOUND: "NOT_FOUND",
15086
+ PARSE_ERROR: "VALIDATION",
15087
+ TIMEOUT: "NETWORK",
15088
+ TOO_MANY_REQUESTS: "API",
15089
+ UNAUTHORIZED: "AUTHENTICATION",
15090
+ UNPROCESSABLE_CONTENT: "VALIDATION"
15091
+ };
15092
+ var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
15093
+ var isNetworkError = (error51, depth = 0) => {
15094
+ if (depth > 3 || !isRecord(error51))
15095
+ return false;
15096
+ const message = typeof error51.message === "string" ? error51.message : undefined;
15097
+ const code = typeof error51.code === "string" ? error51.code : undefined;
15098
+ if (message && networkMessagePattern.test(message) || code && networkMessagePattern.test(code)) {
15099
+ return true;
15100
+ }
15101
+ return isNetworkError(error51.cause, depth + 1);
15102
+ };
15103
+
15104
+ class CliError extends Error {
15105
+ code;
15106
+ details;
15107
+ constructor(code, message, details) {
15108
+ super(message);
15109
+ this.name = "CliError";
15110
+ this.code = code;
15111
+ this.details = details;
15112
+ }
15113
+ }
15114
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15115
+ var isJsonValue = (value) => {
15116
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
15117
+ return true;
15118
+ }
15119
+ if (Array.isArray(value))
15120
+ return value.every(isJsonValue);
15121
+ return isRecord(value) && Object.values(value).every(isJsonValue);
15122
+ };
15123
+ var toJsonValue = (value) => {
15124
+ const serialized = JSON.stringify(value, (_key, nestedValue) => {
15125
+ if (typeof nestedValue === "bigint")
15126
+ return nestedValue.toString();
15127
+ return nestedValue;
15128
+ });
15129
+ if (serialized === undefined)
15130
+ return null;
15131
+ return JSON.parse(serialized);
15132
+ };
15133
+ var sensitiveKeyPattern = /(api.?key|authorization|join.?code|password|secret|share.?token|token|payload|proof|receipt)/i;
15134
+ var presignedUrlPattern = /[?&](?:X-Amz|X-Goog)-(?:Credential|Signature)=/i;
15135
+ var sanitizeValue = (value, seen = new WeakSet) => {
15136
+ if (value === null || typeof value === "boolean" || typeof value === "number") {
15137
+ return value;
15138
+ }
15139
+ if (typeof value === "string") {
15140
+ return presignedUrlPattern.test(value) ? "[redacted]" : value;
15141
+ }
15142
+ if (typeof value === "bigint")
15143
+ return value.toString();
15144
+ if (value instanceof Date)
15145
+ return value.toISOString();
15146
+ if (typeof value !== "object" || value === null)
15147
+ return String(value);
15148
+ if (seen.has(value))
15149
+ return "[circular]";
15150
+ seen.add(value);
15151
+ if (Array.isArray(value)) {
15152
+ return value.map((item) => sanitizeValue(item, seen));
15153
+ }
15154
+ if (!isRecord(value))
15155
+ return String(value);
15156
+ return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
15157
+ key,
15158
+ sensitiveKeyPattern.test(key) ? "[redacted]" : sanitizeValue(nestedValue, seen)
15159
+ ]));
15160
+ };
15161
+ var normalizeError = (error51) => {
15162
+ if (error51 instanceof CliError) {
15163
+ return {
15164
+ code: error51.code,
15165
+ message: error51.message,
15166
+ ...error51.details ? { details: error51.details } : {}
15167
+ };
15168
+ }
15169
+ if (isNetworkError(error51)) {
15170
+ return {
15171
+ code: "NETWORK",
15172
+ message: error51 instanceof Error ? error51.message : "The GOSH API could not be reached."
15173
+ };
15174
+ }
15175
+ if (isRecord(error51)) {
15176
+ const data = isRecord(error51.data) ? error51.data : undefined;
15177
+ const rawCode = typeof data?.code === "string" ? data.code : undefined;
15178
+ const code = rawCode ? trpcCodeMap[rawCode] ?? "API" : "INTERNAL";
15179
+ const message = typeof error51.message === "string" ? error51.message : "The GOSH API request failed.";
15180
+ const details = isRecord(data?.zodError) && isJsonValue(data.zodError) ? { validation: data.zodError } : undefined;
15181
+ return { code, message, ...details ? { details } : {} };
15182
+ }
15183
+ return {
15184
+ code: "INTERNAL",
15185
+ message: error51 instanceof Error ? error51.message : String(error51)
15186
+ };
15187
+ };
15188
+ var serialize = (value) => `${JSON.stringify(value, null, 2)}
15189
+ `;
15190
+ var redactKnownSecrets = (value, _runtime) => {
15191
+ return value.replace(/\bBearer\s+\S+/giu, "Bearer [redacted]");
15192
+ };
15193
+ var emitSuccess = (runtime2, invocation, data) => {
15194
+ runtime2.stdout.write(serialize({
15195
+ ok: true,
15196
+ data: toJsonValue(data),
15197
+ meta: {
15198
+ apiBaseUrl: invocation.apiBaseUrl,
15199
+ cliVersion: invocation.cliVersion,
15200
+ command: invocation.command
15201
+ }
15202
+ }));
15203
+ return 0;
15204
+ };
15205
+ var emitSubscriptionEvent = (runtime2, invocation, data) => {
15206
+ runtime2.stdout.write(`${JSON.stringify({
15207
+ ok: true,
15208
+ data: toJsonValue(data),
15209
+ meta: {
15210
+ apiBaseUrl: invocation.apiBaseUrl,
15211
+ cliVersion: invocation.cliVersion,
15212
+ command: invocation.command,
15213
+ stream: true
15214
+ }
15215
+ })}
15216
+ `);
15217
+ };
15218
+ var emitError = (runtime2, command, error51) => {
15219
+ const normalized = normalizeError(error51);
15220
+ runtime2.stderr.write(redactKnownSecrets(serialize({ ok: false, error: normalized, meta: { command } }), runtime2));
15221
+ return invocationCodes.has(normalized.code) ? 2 : 1;
15222
+ };
15223
+ var effectLabels = {
15224
+ delete: "deletes or permanently revokes data",
15225
+ email: "sends email",
15226
+ "external-write": "writes to an external delivery surface",
15227
+ "state-change": "changes GOSH state"
15228
+ };
15229
+ var permissionFlag = {
15230
+ delete: "allow-delete",
15231
+ email: "allow-email",
15232
+ "external-write": "allow-external-write",
15233
+ "state-change": "allow-state-change"
15234
+ };
15235
+ var getTargetCandidates = (input, targetKeys) => {
15236
+ for (const key of targetKeys) {
15237
+ if (key === "$self")
15238
+ return new Set(["self"]);
15239
+ if (!isRecord(input) || !(key in input))
15240
+ continue;
15241
+ const rawValue = input[key];
15242
+ const values = Array.isArray(rawValue) ? rawValue : [rawValue];
15243
+ const candidates = new Set(values.filter((value) => typeof value === "string" || typeof value === "number").map(String));
15244
+ if (candidates.size > 0)
15245
+ return candidates;
15246
+ }
15247
+ return new Set;
15248
+ };
15249
+ var confirmCommand = async ({
15250
+ action,
15251
+ effects,
15252
+ flags,
15253
+ input,
15254
+ runtime: runtime2,
15255
+ target,
15256
+ targetKeys = []
15257
+ }) => {
15258
+ if (effects.length === 0)
15259
+ return;
15260
+ if (!target?.trim()) {
15261
+ throw new CliError("CONFIRMATION_REQUIRED", "Sensitive commands require --target with the exact resource being changed.", { target: "required" });
15262
+ }
15263
+ if (!getTargetCandidates(input, targetKeys).has(target.trim())) {
15264
+ throw new CliError("CONFIRMATION_REQUIRED", `--target must exactly match the command's primary resource (${targetKeys.map((key) => key === "$self" ? "self" : key).join(", ")}).`);
15265
+ }
15266
+ const uniqueEffects = [...new Set(effects)];
15267
+ const missingFlags = uniqueEffects.map((effect) => permissionFlag[effect]).filter((flag) => flags[flag] !== true).map((flag) => `--${flag}`);
15268
+ if (missingFlags.length === 0)
15269
+ return;
15270
+ if (!runtime2.isTTY) {
15271
+ throw new CliError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require every applicable allow flag.", { missingFlags });
15272
+ }
15273
+ runtime2.stderr.write(`${[
15274
+ "GOSH workflow preview",
15275
+ `Action: ${action}`,
15276
+ `Target: ${target}`,
15277
+ `Effects: ${uniqueEffects.map((effect) => effectLabels[effect]).join("; ")}`
15278
+ ].join(`
15279
+ `)}
15280
+ `);
15281
+ if (!runtime2.prompt) {
15282
+ throw new CliError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
15283
+ }
15284
+ const answer = await runtime2.prompt("Type CONFIRM to continue: ");
15285
+ if (answer !== "CONFIRM") {
15286
+ throw new CliError("ABORTED", "Command aborted by user.");
15287
+ }
15288
+ };
15289
+ var createProcessRuntime = () => ({
15290
+ stdout: { write: (value) => process.stdout.write(value) },
15291
+ stderr: { write: (value) => process.stderr.write(value) },
15292
+ isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
15293
+ env: process.env,
15294
+ prompt: async (message) => {
15295
+ const prompt = createInterface({
15296
+ input: process.stdin,
15297
+ output: process.stderr
15298
+ });
15299
+ try {
15300
+ return await prompt.question(message);
15301
+ } finally {
15302
+ prompt.close();
15303
+ }
15304
+ }
15305
+ });
15306
+
15307
+ // src/api-client.ts
15308
+ var DEFAULT_GOSH_API_BASE_URL = "https://app.gosh.sg";
15309
+ var normalizeApiBaseUrl = (value) => {
15310
+ const url2 = new URL(value);
15311
+ const isLocalHttp = url2.protocol === "http:" && (url2.hostname === "localhost" || url2.hostname === "127.0.0.1");
15312
+ if (url2.protocol !== "https:" && !isLocalHttp) {
15313
+ throw new Error("GOSH API URL must use HTTPS, except for localhost development.");
15314
+ }
15315
+ url2.pathname = url2.pathname.replace(/\/$/, "");
15316
+ return url2.toString().replace(/\/$/, "");
15317
+ };
15318
+ var createGoshClient = ({
15319
+ apiBaseUrl,
15320
+ accessToken,
15321
+ debug = false,
15322
+ quiet = false
15323
+ }) => {
15324
+ const headers = () => ({
15325
+ "x-trpc-source": "gosh-cli",
15326
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
15327
+ });
15328
+ return createTRPCProxyClient({
15329
+ links: [
15330
+ loggerLink({
15331
+ enabled: () => debug && !quiet,
15332
+ colorMode: "ansi",
15333
+ withContext: false,
15334
+ console: {
15335
+ log: (...args) => console.error(...args.map((argument) => sanitizeValue(argument))),
15336
+ error: (...args) => console.error(...args.map((argument) => sanitizeValue(argument)))
15337
+ }
15338
+ }),
15339
+ splitLink({
15340
+ condition: (operation) => operation.type === "subscription",
15341
+ true: httpSubscriptionLink({
15342
+ EventSource: AuthenticatedEventSource,
15343
+ eventSourceOptions: { headers: headers() },
15344
+ transformer: SuperJSON,
15345
+ url: `${apiBaseUrl}/api/trpc`
15346
+ }),
15347
+ false: httpBatchLink({
15348
+ headers,
15349
+ transformer: SuperJSON,
15350
+ url: `${apiBaseUrl}/api/trpc`
15351
+ })
15352
+ })
15353
+ ]
15354
+ });
15355
+ };
15356
+
15072
15357
  // src/registry/typed-operations.ts
15073
15358
  var typedOperations = {
15074
15359
  "health.status": {
@@ -15529,394 +15814,111 @@ var typedOperations = {
15529
15814
  },
15530
15815
  "event.onSubmissionReviewQueueChanged": {
15531
15816
  kind: "subscription",
15532
- subscribe: (client, input, observer) => client.event.onSubmissionReviewQueueChanged.subscribe(input, {
15533
- onComplete: observer.complete,
15534
- onData: observer.next,
15535
- onError: observer.error
15536
- })
15537
- },
15538
- "organization.create": {
15539
- kind: "mutation",
15540
- invoke: (client, input) => client.organization.create.mutate(input)
15541
- },
15542
- "organization.update": {
15543
- kind: "mutation",
15544
- invoke: (client, input) => client.organization.update.mutate(input)
15545
- },
15546
- "organization.checkSlug": {
15547
- kind: "mutation",
15548
- invoke: (client, input) => client.organization.checkSlug.mutate(input)
15549
- },
15550
- "organization.checkOrganizationAccess": {
15551
- kind: "query",
15552
- invoke: (client, input) => client.organization.checkOrganizationAccess.query(input)
15553
- },
15554
- "organization.getOnboardingState": {
15555
- kind: "query",
15556
- invoke: (client, input) => client.organization.getOnboardingState.query(input)
15557
- },
15558
- "organization.getInvitation": {
15559
- kind: "query",
15560
- invoke: (client, input) => client.organization.getInvitation.query(input)
15561
- },
15562
- "organization.acceptInvitation": {
15563
- kind: "mutation",
15564
- invoke: (client, input) => client.organization.acceptInvitation.mutate(input)
15565
- },
15566
- "organization.rejectInvitation": {
15567
- kind: "mutation",
15568
- invoke: (client, input) => client.organization.rejectInvitation.mutate(input)
15569
- },
15570
- "organization.cancelInvitation": {
15571
- kind: "mutation",
15572
- invoke: (client, input) => client.organization.cancelInvitation.mutate(input)
15573
- },
15574
- "organization.inviteMember": {
15575
- kind: "mutation",
15576
- invoke: (client, input) => client.organization.inviteMember.mutate(input)
15577
- },
15578
- "organization.removeMember": {
15579
- kind: "mutation",
15580
- invoke: (client, input) => client.organization.removeMember.mutate(input)
15581
- },
15582
- "organization.updateMemberRole": {
15583
- kind: "mutation",
15584
- invoke: (client, input) => client.organization.updateMemberRole.mutate(input)
15585
- },
15586
- "organization.listAllForAdmin": {
15587
- kind: "query",
15588
- invoke: (client, input) => client.organization.listAllForAdmin.query(input)
15589
- },
15590
- "template.createSystem": {
15591
- kind: "mutation",
15592
- invoke: (client, input) => client.template.createSystem.mutate(input)
15593
- },
15594
- "user.listAdmin": {
15595
- kind: "query",
15596
- invoke: (client, input) => client.user.listAdmin.query(input)
15597
- },
15598
- "user.updateGlobalRole": {
15599
- kind: "mutation",
15600
- invoke: (client, input) => client.user.updateGlobalRole.mutate(input)
15601
- },
15602
- "user.ban": {
15603
- kind: "mutation",
15604
- invoke: (client, input) => client.user.ban.mutate(input)
15605
- },
15606
- "user.unban": {
15607
- kind: "mutation",
15608
- invoke: (client, input) => client.user.unban.mutate(input)
15609
- }
15610
- };
15611
-
15612
- // src/runtime.ts
15613
- import { createInterface } from "readline/promises";
15614
- var invocationCodes = new Set([
15615
- "USAGE",
15616
- "VALIDATION",
15617
- "CONFIRMATION_REQUIRED",
15618
- "ABORTED"
15619
- ]);
15620
- var trpcCodeMap = {
15621
- BAD_REQUEST: "VALIDATION",
15622
- CONFLICT: "CONFLICT",
15623
- FORBIDDEN: "AUTHORIZATION",
15624
- INTERNAL_SERVER_ERROR: "API",
15625
- NOT_FOUND: "NOT_FOUND",
15626
- PARSE_ERROR: "VALIDATION",
15627
- TIMEOUT: "NETWORK",
15628
- TOO_MANY_REQUESTS: "API",
15629
- UNAUTHORIZED: "AUTHENTICATION",
15630
- UNPROCESSABLE_CONTENT: "VALIDATION"
15631
- };
15632
- var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
15633
- var isNetworkError = (error51, depth = 0) => {
15634
- if (depth > 3 || !isRecord(error51))
15635
- return false;
15636
- const message = typeof error51.message === "string" ? error51.message : undefined;
15637
- const code = typeof error51.code === "string" ? error51.code : undefined;
15638
- if (message && networkMessagePattern.test(message) || code && networkMessagePattern.test(code)) {
15639
- return true;
15640
- }
15641
- return isNetworkError(error51.cause, depth + 1);
15642
- };
15643
-
15644
- class CliError extends Error {
15645
- code;
15646
- details;
15647
- constructor(code, message, details) {
15648
- super(message);
15649
- this.name = "CliError";
15650
- this.code = code;
15651
- this.details = details;
15652
- }
15653
- }
15654
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15655
- var isJsonValue = (value) => {
15656
- if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
15657
- return true;
15658
- }
15659
- if (Array.isArray(value))
15660
- return value.every(isJsonValue);
15661
- return isRecord(value) && Object.values(value).every(isJsonValue);
15662
- };
15663
- var toJsonValue = (value) => {
15664
- const serialized = JSON.stringify(value, (_key, nestedValue) => {
15665
- if (typeof nestedValue === "bigint")
15666
- return nestedValue.toString();
15667
- return nestedValue;
15668
- });
15669
- if (serialized === undefined)
15670
- return null;
15671
- return JSON.parse(serialized);
15672
- };
15673
- var sensitiveKeyPattern = /(api.?key|authorization|join.?code|password|secret|share.?token|token|payload|proof|receipt)/i;
15674
- var presignedUrlPattern = /[?&](?:X-Amz|X-Goog)-(?:Credential|Signature)=/i;
15675
- var sanitizeValue = (value, seen = new WeakSet) => {
15676
- if (value === null || typeof value === "boolean" || typeof value === "number") {
15677
- return value;
15678
- }
15679
- if (typeof value === "string") {
15680
- return presignedUrlPattern.test(value) ? "[redacted]" : value;
15681
- }
15682
- if (typeof value === "bigint")
15683
- return value.toString();
15684
- if (value instanceof Date)
15685
- return value.toISOString();
15686
- if (typeof value !== "object" || value === null)
15687
- return String(value);
15688
- if (seen.has(value))
15689
- return "[circular]";
15690
- seen.add(value);
15691
- if (Array.isArray(value)) {
15692
- return value.map((item) => sanitizeValue(item, seen));
15693
- }
15694
- if (!isRecord(value))
15695
- return String(value);
15696
- return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
15697
- key,
15698
- sensitiveKeyPattern.test(key) ? "[redacted]" : sanitizeValue(nestedValue, seen)
15699
- ]));
15700
- };
15701
- var normalizeError = (error51) => {
15702
- if (error51 instanceof CliError) {
15703
- return {
15704
- code: error51.code,
15705
- message: error51.message,
15706
- ...error51.details ? { details: error51.details } : {}
15707
- };
15708
- }
15709
- if (isNetworkError(error51)) {
15710
- return {
15711
- code: "NETWORK",
15712
- message: error51 instanceof Error ? error51.message : "The GOSH API could not be reached."
15713
- };
15714
- }
15715
- if (isRecord(error51)) {
15716
- const data = isRecord(error51.data) ? error51.data : undefined;
15717
- const rawCode = typeof data?.code === "string" ? data.code : undefined;
15718
- const code = rawCode ? trpcCodeMap[rawCode] ?? "API" : "INTERNAL";
15719
- const message = typeof error51.message === "string" ? error51.message : "The GOSH API request failed.";
15720
- const details = isRecord(data?.zodError) && isJsonValue(data.zodError) ? { validation: data.zodError } : undefined;
15721
- return { code, message, ...details ? { details } : {} };
15817
+ subscribe: (client, input, observer) => client.event.onSubmissionReviewQueueChanged.subscribe(input, {
15818
+ onComplete: observer.complete,
15819
+ onData: observer.next,
15820
+ onError: observer.error
15821
+ })
15822
+ },
15823
+ "organization.create": {
15824
+ kind: "mutation",
15825
+ invoke: (client, input) => client.organization.create.mutate(input)
15826
+ },
15827
+ "organization.update": {
15828
+ kind: "mutation",
15829
+ invoke: (client, input) => client.organization.update.mutate(input)
15830
+ },
15831
+ "organization.checkSlug": {
15832
+ kind: "mutation",
15833
+ invoke: (client, input) => client.organization.checkSlug.mutate(input)
15834
+ },
15835
+ "organization.checkOrganizationAccess": {
15836
+ kind: "query",
15837
+ invoke: (client, input) => client.organization.checkOrganizationAccess.query(input)
15838
+ },
15839
+ "organization.getOnboardingState": {
15840
+ kind: "query",
15841
+ invoke: (client, input) => client.organization.getOnboardingState.query(input)
15842
+ },
15843
+ "organization.getInvitation": {
15844
+ kind: "query",
15845
+ invoke: (client, input) => client.organization.getInvitation.query(input)
15846
+ },
15847
+ "organization.acceptInvitation": {
15848
+ kind: "mutation",
15849
+ invoke: (client, input) => client.organization.acceptInvitation.mutate(input)
15850
+ },
15851
+ "organization.rejectInvitation": {
15852
+ kind: "mutation",
15853
+ invoke: (client, input) => client.organization.rejectInvitation.mutate(input)
15854
+ },
15855
+ "organization.cancelInvitation": {
15856
+ kind: "mutation",
15857
+ invoke: (client, input) => client.organization.cancelInvitation.mutate(input)
15858
+ },
15859
+ "organization.inviteMember": {
15860
+ kind: "mutation",
15861
+ invoke: (client, input) => client.organization.inviteMember.mutate(input)
15862
+ },
15863
+ "organization.removeMember": {
15864
+ kind: "mutation",
15865
+ invoke: (client, input) => client.organization.removeMember.mutate(input)
15866
+ },
15867
+ "organization.updateMemberRole": {
15868
+ kind: "mutation",
15869
+ invoke: (client, input) => client.organization.updateMemberRole.mutate(input)
15870
+ },
15871
+ "organization.listAllForAdmin": {
15872
+ kind: "query",
15873
+ invoke: (client, input) => client.organization.listAllForAdmin.query(input)
15874
+ },
15875
+ "template.createSystem": {
15876
+ kind: "mutation",
15877
+ invoke: (client, input) => client.template.createSystem.mutate(input)
15878
+ },
15879
+ "user.listAdmin": {
15880
+ kind: "query",
15881
+ invoke: (client, input) => client.user.listAdmin.query(input)
15882
+ },
15883
+ "user.updateGlobalRole": {
15884
+ kind: "mutation",
15885
+ invoke: (client, input) => client.user.updateGlobalRole.mutate(input)
15886
+ },
15887
+ "user.ban": {
15888
+ kind: "mutation",
15889
+ invoke: (client, input) => client.user.ban.mutate(input)
15890
+ },
15891
+ "user.unban": {
15892
+ kind: "mutation",
15893
+ invoke: (client, input) => client.user.unban.mutate(input)
15722
15894
  }
15723
- return {
15724
- code: "INTERNAL",
15725
- message: error51 instanceof Error ? error51.message : String(error51)
15726
- };
15727
- };
15728
- var serialize = (value) => `${JSON.stringify(value, null, 2)}
15729
- `;
15730
- var redactKnownSecrets = (value, _runtime) => {
15731
- return value.replace(/\bBearer\s+\S+/giu, "Bearer [redacted]");
15732
- };
15733
- var emitSuccess = (runtime2, invocation, data) => {
15734
- runtime2.stdout.write(serialize({
15735
- ok: true,
15736
- data: toJsonValue(data),
15737
- meta: {
15738
- apiBaseUrl: invocation.apiBaseUrl,
15739
- cliVersion: invocation.cliVersion,
15740
- command: invocation.command
15741
- }
15742
- }));
15743
- return 0;
15744
15895
  };
15745
- var emitSubscriptionEvent = (runtime2, invocation, data) => {
15746
- runtime2.stdout.write(`${JSON.stringify({
15747
- ok: true,
15748
- data: toJsonValue(data),
15749
- meta: {
15750
- apiBaseUrl: invocation.apiBaseUrl,
15751
- cliVersion: invocation.cliVersion,
15752
- command: invocation.command,
15753
- stream: true
15896
+
15897
+ // src/command-client.ts
15898
+ var createGoshCommandClient = (client) => ({
15899
+ mutation: (path2, input) => {
15900
+ const operation = typedOperations[path2];
15901
+ if (operation.kind !== "mutation") {
15902
+ throw new Error(`Expected mutation procedure, received ${path2}.`);
15903
+ }
15904
+ return operation.invoke(client, input);
15905
+ },
15906
+ query: (path2, input) => {
15907
+ const operation = typedOperations[path2];
15908
+ if (operation.kind !== "query") {
15909
+ throw new Error(`Expected query procedure, received ${path2}.`);
15754
15910
  }
15755
- })}
15756
- `);
15757
- };
15758
- var emitError = (runtime2, command, error51) => {
15759
- const normalized = normalizeError(error51);
15760
- runtime2.stderr.write(redactKnownSecrets(serialize({ ok: false, error: normalized, meta: { command } }), runtime2));
15761
- return invocationCodes.has(normalized.code) ? 2 : 1;
15762
- };
15763
- var effectLabels = {
15764
- delete: "deletes or permanently revokes data",
15765
- email: "sends email",
15766
- "external-write": "writes to an external delivery surface",
15767
- "state-change": "changes GOSH state"
15768
- };
15769
- var permissionFlag = {
15770
- delete: "allow-delete",
15771
- email: "allow-email",
15772
- "external-write": "allow-external-write",
15773
- "state-change": "allow-state-change"
15774
- };
15775
- var getTargetCandidates = (input, targetKeys) => {
15776
- for (const key of targetKeys) {
15777
- if (key === "$self")
15778
- return new Set(["self"]);
15779
- if (!isRecord(input) || !(key in input))
15780
- continue;
15781
- const rawValue = input[key];
15782
- const values = Array.isArray(rawValue) ? rawValue : [rawValue];
15783
- const candidates = new Set(values.filter((value) => typeof value === "string" || typeof value === "number").map(String));
15784
- if (candidates.size > 0)
15785
- return candidates;
15786
- }
15787
- return new Set;
15788
- };
15789
- var confirmCommand = async ({
15790
- action,
15791
- effects,
15792
- flags,
15793
- input,
15794
- runtime: runtime2,
15795
- target,
15796
- targetKeys = []
15797
- }) => {
15798
- if (effects.length === 0)
15799
- return;
15800
- if (!target?.trim()) {
15801
- throw new CliError("CONFIRMATION_REQUIRED", "Sensitive commands require --target with the exact resource being changed.", { target: "required" });
15802
- }
15803
- if (!getTargetCandidates(input, targetKeys).has(target.trim())) {
15804
- throw new CliError("CONFIRMATION_REQUIRED", `--target must exactly match the command's primary resource (${targetKeys.map((key) => key === "$self" ? "self" : key).join(", ")}).`);
15805
- }
15806
- const uniqueEffects = [...new Set(effects)];
15807
- const missingFlags = uniqueEffects.map((effect) => permissionFlag[effect]).filter((flag) => flags[flag] !== true).map((flag) => `--${flag}`);
15808
- if (missingFlags.length === 0)
15809
- return;
15810
- if (!runtime2.isTTY) {
15811
- throw new CliError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require every applicable allow flag.", { missingFlags });
15812
- }
15813
- runtime2.stderr.write(`${[
15814
- "GOSH workflow preview",
15815
- `Action: ${action}`,
15816
- `Target: ${target}`,
15817
- `Effects: ${uniqueEffects.map((effect) => effectLabels[effect]).join("; ")}`
15818
- ].join(`
15819
- `)}
15820
- `);
15821
- if (!runtime2.prompt) {
15822
- throw new CliError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
15823
- }
15824
- const answer = await runtime2.prompt("Type CONFIRM to continue: ");
15825
- if (answer !== "CONFIRM") {
15826
- throw new CliError("ABORTED", "Command aborted by user.");
15827
- }
15828
- };
15829
- var createProcessRuntime = () => ({
15830
- stdout: { write: (value) => process.stdout.write(value) },
15831
- stderr: { write: (value) => process.stderr.write(value) },
15832
- isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
15833
- env: process.env,
15834
- prompt: async (message) => {
15835
- const prompt = createInterface({
15836
- input: process.stdin,
15837
- output: process.stderr
15838
- });
15839
- try {
15840
- return await prompt.question(message);
15841
- } finally {
15842
- prompt.close();
15911
+ return operation.invoke(client, input);
15912
+ },
15913
+ subscription: (path2, input, observer) => {
15914
+ const operation = typedOperations[path2];
15915
+ if (operation.kind !== "subscription") {
15916
+ throw new Error(`Expected subscription procedure, received ${path2}.`);
15843
15917
  }
15918
+ return operation.subscribe(client, input, observer);
15844
15919
  }
15845
15920
  });
15846
15921
 
15847
- // src/api-client.ts
15848
- var DEFAULT_GOSH_API_BASE_URL = "https://app.gosh.sg";
15849
- var normalizeApiBaseUrl = (value) => {
15850
- const url2 = new URL(value);
15851
- const isLocalHttp = url2.protocol === "http:" && (url2.hostname === "localhost" || url2.hostname === "127.0.0.1");
15852
- if (url2.protocol !== "https:" && !isLocalHttp) {
15853
- throw new Error("GOSH API URL must use HTTPS, except for localhost development.");
15854
- }
15855
- url2.pathname = url2.pathname.replace(/\/$/, "");
15856
- return url2.toString().replace(/\/$/, "");
15857
- };
15858
- var createGoshClient = ({
15859
- apiBaseUrl,
15860
- accessToken,
15861
- debug = false,
15862
- quiet = false
15863
- }) => {
15864
- const headers = () => ({
15865
- "x-trpc-source": "gosh-cli",
15866
- ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
15867
- });
15868
- const typedClient = createTRPCProxyClient({
15869
- links: [
15870
- loggerLink({
15871
- enabled: () => debug && !quiet,
15872
- colorMode: "ansi",
15873
- withContext: false,
15874
- console: {
15875
- log: (...args) => console.error(...args.map((argument) => sanitizeValue(argument))),
15876
- error: (...args) => console.error(...args.map((argument) => sanitizeValue(argument)))
15877
- }
15878
- }),
15879
- splitLink({
15880
- condition: (operation) => operation.type === "subscription",
15881
- true: httpSubscriptionLink({
15882
- EventSource: AuthenticatedEventSource,
15883
- eventSourceOptions: { headers: headers() },
15884
- transformer: SuperJSON,
15885
- url: `${apiBaseUrl}/api/trpc`
15886
- }),
15887
- false: httpBatchLink({
15888
- headers,
15889
- transformer: SuperJSON,
15890
- url: `${apiBaseUrl}/api/trpc`
15891
- })
15892
- })
15893
- ]
15894
- });
15895
- return Object.assign(typedClient, {
15896
- mutation: (path2, input) => {
15897
- const operation = typedOperations[path2];
15898
- if (operation.kind !== "mutation") {
15899
- throw new Error(`Expected mutation procedure, received ${path2}.`);
15900
- }
15901
- return operation.invoke(typedClient, input);
15902
- },
15903
- query: (path2, input) => {
15904
- const operation = typedOperations[path2];
15905
- if (operation.kind !== "query") {
15906
- throw new Error(`Expected query procedure, received ${path2}.`);
15907
- }
15908
- return operation.invoke(typedClient, input);
15909
- },
15910
- subscription: (path2, input, observer) => {
15911
- const operation = typedOperations[path2];
15912
- if (operation.kind !== "subscription") {
15913
- throw new Error(`Expected subscription procedure, received ${path2}.`);
15914
- }
15915
- return operation.subscribe(typedClient, input, observer);
15916
- }
15917
- });
15918
- };
15919
-
15920
15922
  // src/parse-args.ts
15921
15923
  var parseArgs = (argv) => {
15922
15924
  const flags = {};
@@ -16556,13 +16558,13 @@ Waiting for approval\u2026
16556
16558
  apiBaseUrl,
16557
16559
  store: credentialStore
16558
16560
  }) : undefined;
16559
- const clientFactory = dependencies.createClient ?? createGoshClient;
16560
- const client = clientFactory({
16561
+ const clientOptions = {
16561
16562
  accessToken,
16562
16563
  apiBaseUrl,
16563
16564
  debug: flags.debug === true,
16564
16565
  quiet: flags.quiet === true
16565
- });
16566
+ };
16567
+ const client = dependencies.createClient ? dependencies.createClient(clientOptions) : createGoshCommandClient(createGoshClient(clientOptions));
16566
16568
  if (command.operation === "subscription") {
16567
16569
  const invocation = {
16568
16570
  apiBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/gosh",
3
- "version": "0.2.0",
3
+ "version": "0.3.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",