gamealgo-cli 0.4.3 → 0.4.4

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
@@ -18,10 +18,13 @@ Run login from the game repository that should own the credentials:
18
18
  ```bash
19
19
  gamealgo login --host <admin-url> --admin-key <ga_admin_key>
20
20
  gamealgo whoami
21
+ gamealgo quota --json
21
22
  ```
22
23
 
23
24
  The login is stored in the current project's `.gamealgo/cli.json`, which is automatically ignored by Git. Authenticated commands require this project-scoped login and verify its `gameId` against the Server before continuing, preventing credentials from another game from being reused accidentally.
24
25
 
26
+ `gamealgo quota --json` returns the current effective plan, the per-user daily ordinary-event limit, UTC reset time, detail-query and retention windows, exempt revenue events, and today's actual quota-filtered count. Check it before adding or increasing telemetry.
27
+
25
28
  ## Current help and documentation
26
29
 
27
30
  ```bash
package/dist/client.js CHANGED
@@ -201,6 +201,9 @@ export class GameAlgoAdminClient {
201
201
  async countEvents(body, options = {}) {
202
202
  return await this.post(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/count`, body, options);
203
203
  }
204
+ async getGameQuota() {
205
+ return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/quota`);
206
+ }
204
207
  async listEventCatalog(days = 30, diagnostics = true) {
205
208
  const params = new URLSearchParams({ days: String(days), diagnostics: diagnostics ? "1" : "0" });
206
209
  return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/catalog?${params.toString()}`);
@@ -49,6 +49,12 @@ export async function handleDynamicConfig(client, args, global) {
49
49
  const key = optionalString(definition.key);
50
50
  if (!key)
51
51
  throw new Error("dynamic config definition.key is required");
52
+ if (definition.reportPack !== undefined) {
53
+ throw new Error("dynamic config definition.reportPack is no longer supported; inputs must reference the current Report Pack v2");
54
+ }
55
+ if (!Array.isArray(definition.inputs) || definition.inputs.length === 0) {
56
+ throw new Error("dynamic config definition.inputs must be a non-empty array");
57
+ }
52
58
  const body = { ...definition };
53
59
  delete body.key;
54
60
  const scriptPath = optionalString(flags.script);
@@ -0,0 +1,16 @@
1
+ import { parseFlags } from "../args.js";
2
+ import { printResult } from "../output.js";
3
+ import { writeTextFile } from "../resources.js";
4
+ export async function handleQuota(client, args, global) {
5
+ const flags = parseFlags(args);
6
+ if (args.length)
7
+ throw new Error("usage: gamealgo quota [--out quota.json]");
8
+ const value = await client.getGameQuota();
9
+ if (flags.out) {
10
+ const filePath = String(flags.out);
11
+ await writeTextFile(filePath, JSON.stringify(value, null, 2) + "\n");
12
+ await printResult({ ok: true, out: filePath }, global);
13
+ return;
14
+ }
15
+ await printResult(value, global);
16
+ }
package/dist/main.js CHANGED
@@ -7,6 +7,7 @@ import { handleIntegration } from "./commands/integration.js";
7
7
  import { handleGameKey } from "./commands/keys.js";
8
8
  import { handleMarketing } from "./commands/marketing.js";
9
9
  import { handleOptimize } from "./commands/optimize.js";
10
+ import { handleQuota } from "./commands/quota.js";
10
11
  import { handleReport } from "./commands/report.js";
11
12
  import { handleFileResource } from "./commands/resources.js";
12
13
  import { handleSkill } from "./commands/skill.js";
@@ -95,6 +96,10 @@ export async function main() {
95
96
  await handleEvents(client, args, global);
96
97
  return;
97
98
  }
99
+ if (command === "quota") {
100
+ await handleQuota(client, args, global);
101
+ return;
102
+ }
98
103
  if (command === "integration") {
99
104
  await handleIntegration(client, args, global);
100
105
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gamealgo-cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "GameAlgo command-line client for AI agents and game developers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -114,11 +114,16 @@ After approval, update the actual emission call sites and their normal-flow test
114
114
 
115
115
  ## Bound Custom Event Volume
116
116
 
117
- Before adding or changing a custom event, prefer a protocol-standard event or an existing custom event with the same meaning. Keep the event type stable and put variable values in payload fields; never generate event types from item IDs, levels, timestamps, user input, or other runtime values.
117
+ Before adding or changing any event, run `gamealgo quota --json` and `gamealgo events list --days 30 --json`. Treat the returned effective plan and `ordinaryEvents.dailyLimitPerUser` as a hard per-game, per-user, per-UTC-day budget shared by standard and custom ordinary events. Estimate the user's total ordinary event volume across all existing and proposed call sites, leave headroom for normal retries and future events, and reduce or aggregate high-frequency telemetry before implementation when the bound can reach the current limit.
118
+
119
+ Prefer a protocol-standard event or an existing custom event with the same meaning. Keep the event type stable and put variable values in payload fields; never generate event types from item IDs, levels, timestamps, user input, or other runtime values.
118
120
 
119
121
  Inspect every call site for render/update loops, timers, polling, transport retries, duplicate lifecycle callbacks, and re-entrant listeners. State the trigger boundary and estimate the maximum calls per user, session, context, and active user-day. Use the game's active-user estimate to describe the expected daily volume when it is available.
120
122
 
121
123
  - Standard SDK semantic events are governed by their protocol semantics. Do not disguise custom telemetry as a standard event to bypass safeguards.
124
+ - `purchase` and `ad_view` are exempt only when they represent their real protocol semantics and pass their strict validation. Every other accepted event consumes the ordinary daily budget, including standard events and repeated `eventId` submissions.
125
+ - `quota.today.filteredEvents` is the number of ordinary events actually rejected by quota enforcement today; `shadowWouldRejectEvents` is observation-only and must not be reported as data loss. If actual filtering is nonzero, identify and fix the highest-frequency emission paths before proposing more events.
126
+ - The quota resets at UTC midnight, not the player's local midnight. If quota information is unavailable, do not assume Free, Enterprise, or unlimited capacity; report the missing prerequisite and avoid publishing a higher-volume telemetry design.
122
127
  - The SDK limits custom events to 1,000 occurrences per `context × eventType`, 5,000 total occurrences per context, and 100 distinct custom event types per context.
123
128
  - A custom event with an unbounded trigger or an expected rate above 1,000 calls per context requires explicit developer review before implementation. Explain the expected volume, storage and processing impact, and the guard that makes the trigger bounded.
124
129
  - Never add client-side retries around `track`, rename an event after rejection, remove its custom-event prefix, declare it standard, raise a safety limit, or call a lower-level transport to bypass a quota.
@@ -128,14 +133,15 @@ Before delivery, report the estimated bound, normal-flow tests, the limiting loo
128
133
 
129
134
  ## Treat Dynamic Config As A Production Publisher
130
135
 
131
- A dynamic config pipeline is Server-side automation that turns aggregated Report Pack inputs into a live config file. It does not authorize SDK changes, detailed production-data downloads, or live publication by itself.
136
+ A dynamic config pipeline is Server-side automation that turns current Report Pack v2 ordinary Dataset snapshots into a live config file. It does not authorize SDK changes, detailed production-data downloads, or live publication by itself.
132
137
 
133
138
  - Define bounded aggregate inputs with Report Pack syntax. Keep filters, joins, aggregation, and data reduction in the managed query rather than passing event detail to JavaScript.
134
139
  - Keep transforms synchronous and deterministic. They may use only the supplied datasets, previous output, and scheduled metadata; never depend on network, filesystem, environment variables, wall-clock APIs, randomness, async functions, or Promise output.
135
140
  - The output file name and content type belong to the reviewed pipeline definition. The script may return only file content and JSON diagnostics.
136
141
  - Before publishing or materially updating an active pipeline, show the target game, input reports and windows, complete script diff, output file and content type, schedule, expected behavior, failure behavior, and user-facing or operational risks. Obtain explicit approval for that exact publication unless the user already gave it after seeing those details.
137
142
  - Prefer a disabled pipeline or a local script test while details are still under discussion. After an authorized publish, trigger one run, inspect the persisted run result and generated file version, and confirm that an unchanged second run is hash-deduplicated before treating the automation as verified.
138
- - Do not raise the 1 MiB script/input/output caps, the 5,000-row per-dataset cap, the 5-second execution cap, or the minimum 60-minute schedule to bypass a failing design. Reduce data in the Report Pack or simplify the transform.
143
+ - Do not embed a Report Pack, use Advanced Query/raw SQL, or trigger warehouse queries from Dynamic Config. Inputs reuse v2 View aggregation over already-materialized ordinary Datasets and pin the Dataset fingerprint at publish time.
144
+ - Do not raise the 1 MiB script/input/output caps, the 100-row per-input cap, the 500-row total cap, the 5-second execution cap, or the minimum daily schedule to bypass a failing design. Reduce the ordinary Dataset/View result or simplify the transform.
139
145
 
140
146
  Use `gamealgo dynamic-config list|get|publish|run|runs` for lifecycle operations. Dynamic config output continues through the ordinary SDK config-file delivery mechanism, so do not change client SDK code merely to consume a generated file that already uses the existing config API.
141
147