gamealgo-cli 0.1.6 → 0.1.7
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 +3 -0
- package/cli/README.md +71 -1
- package/dist/gamealgo.js +108 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,11 +23,14 @@ gamealgo docs agent-onboarding --host <admin-host>
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
gamealgo login --host <admin-host> --admin-key <ga_admin_xxx>
|
|
26
|
+
gamealgo integration get-plan --environment <domestic|overseas> --platform <ios|android|maker|rest> --out gamealgo-integration-plan.json
|
|
26
27
|
gamealgo key list --json
|
|
27
28
|
gamealgo key create --name <runtime-name> --json
|
|
28
29
|
gamealgo key reveal --name <runtime-name> --json
|
|
29
30
|
```
|
|
30
31
|
|
|
32
|
+
AI Agent 必须先执行 `integration get-plan`,并按返回的完整清单完成接入和人工验收。不要依赖只读某一份 README 后自行推断其他平台规则。
|
|
33
|
+
|
|
31
34
|
凭据保存在当前游戏项目的 `.gamealgo/cli.json`,不同游戏互不覆盖。需要其他主题时使用:
|
|
32
35
|
|
|
33
36
|
```bash
|
package/cli/README.md
CHANGED
|
@@ -71,6 +71,21 @@ GAMEALGO_GAME_ADMIN_KEY=ga_admin_xxx
|
|
|
71
71
|
gamealgo whoami
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
## 接入计划
|
|
75
|
+
|
|
76
|
+
登录后先让 CLI 根据开发者明确指定的环境和平台生成完整接入计划。CLI 不自动猜测环境或平台:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
gamealgo integration get-plan \
|
|
80
|
+
--environment domestic \
|
|
81
|
+
--platform maker \
|
|
82
|
+
--out gamealgo-integration-plan.json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
支持的环境是 `domestic`、`overseas`;支持的平台是 `ios`、`android`、`maker`、`rest`。计划包含正确地址、强制初始化规则、核心事件、广告/归因/实验条件项、验收标准和按需参考文档。
|
|
86
|
+
|
|
87
|
+
计划同时包含接入完成后必须逐项执行的人工验收清单。不能只根据命令执行成功或生产报表有数据来判断接入已经完成。
|
|
88
|
+
|
|
74
89
|
## 最新文档
|
|
75
90
|
|
|
76
91
|
AI Agent 可以通过 CLI 从 Admin Host 获取最新文档,不需要拉取本仓库:
|
|
@@ -311,6 +326,8 @@ gamealgo config publish configs/gameplay.json
|
|
|
311
326
|
## Report Pack
|
|
312
327
|
|
|
313
328
|
```bash
|
|
329
|
+
gamealgo events list --days 30 --json
|
|
330
|
+
gamealgo events apply gamealgo-events.yaml --json
|
|
314
331
|
gamealgo report pull --out gamealgo-report-pack.json
|
|
315
332
|
gamealgo report validate gamealgo-report-pack.json
|
|
316
333
|
gamealgo report publish gamealgo-report-pack.json
|
|
@@ -318,7 +335,7 @@ gamealgo report publish gamealgo-report-pack.json
|
|
|
318
335
|
|
|
319
336
|
每个游戏只有一个当前 Report Pack。`publish` 会整体覆盖上一次提交的配置,不需要传版本号;配置历史由游戏项目自己的 Git 管理。
|
|
320
337
|
|
|
321
|
-
`report validate` 和
|
|
338
|
+
`report validate` 和 `report publish` 使用同一套服务端元数据校验逻辑,只检查配置结构、事件词典与生成 SQL,不查询线上数据。凡是 Report Pack 顶层 `events` 引用的标准事件或自定义事件,其字段都必须已经登记、未废弃且字段类型一致;只引用标准看板和纯 marketing dataset 时,不需要重复登记平台内部字段。
|
|
322
339
|
|
|
323
340
|
## Adjust 投放花费
|
|
324
341
|
|
|
@@ -410,6 +427,59 @@ gamealgo report preview \
|
|
|
410
427
|
|
|
411
428
|
## 事件上报调试
|
|
412
429
|
|
|
430
|
+
### 先查看并维护事件词典
|
|
431
|
+
|
|
432
|
+
开始新增埋点或设计报表前,Agent 必须先查看当前游戏已经登记和实际上报的埋点:
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
gamealgo events list --days 30
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
列表会展示埋点描述、生效版本、创建时间、状态和字段定义,并根据近 30 天非调试数据给出事件级诊断:
|
|
439
|
+
|
|
440
|
+
- 线上出现但词典未登记;
|
|
441
|
+
- 词典已登记但近期未观测;
|
|
442
|
+
- 埋点已废弃但线上仍在上报。
|
|
443
|
+
|
|
444
|
+
诊断只检查埋点是否存在,不根据线上 payload 自动推断字段或字段类型。字段必须由开发 Agent 显式登记后再用于 Report Pack。标准事件同样可以维护词典并追加游戏业务字段,例如在 `level_end.payload` 中增加关卡、章节或玩法信息。
|
|
445
|
+
|
|
446
|
+
Report Pack 的 `events.<eventType>.fields` 字段 ID 必须与词典字段名一致,`path` 只描述 payload 中的实际 JSON 路径。例如词典登记 `ad_type` 后,Report Pack 可以用 `"path": "$.adType"` 读取驼峰字段,但字段类型必须与词典一致。
|
|
447
|
+
|
|
448
|
+
用 YAML 或 JSON 增量登记埋点:
|
|
449
|
+
|
|
450
|
+
```yaml
|
|
451
|
+
events:
|
|
452
|
+
level_end:
|
|
453
|
+
desc: 玩家结束关卡
|
|
454
|
+
effectiveVersion: 1.2.0
|
|
455
|
+
category: progression
|
|
456
|
+
source: standard
|
|
457
|
+
fields:
|
|
458
|
+
level_id:
|
|
459
|
+
type: string
|
|
460
|
+
required: true
|
|
461
|
+
desc: 关卡 ID
|
|
462
|
+
effectiveVersion: 1.2.0
|
|
463
|
+
chapter_id:
|
|
464
|
+
type: string
|
|
465
|
+
required: false
|
|
466
|
+
desc: 游戏追加的章节 ID
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
gamealgo events apply gamealgo-events.yaml
|
|
471
|
+
gamealgo events get level_end
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
规则:
|
|
475
|
+
|
|
476
|
+
- 埋点必须填写 `desc`,`effectiveVersion` 可省略;创建时间由平台首次登记时生成。
|
|
477
|
+
- 字段必须填写 `type` 和 `desc`,`effectiveVersion` 可省略;字段创建时间由平台生成。
|
|
478
|
+
- 标准事件使用 `source: standard`,自定义事件使用 `source: custom`;两者都可以登记游戏追加的 payload 字段。
|
|
479
|
+
- `apply` 只新增或更新文件中出现的埋点和字段,不会删除其他已有定义。
|
|
480
|
+
- 不再使用的埋点需要显式废弃:`gamealgo events deprecate old_event --yes`。
|
|
481
|
+
- 再次 `apply` 已废弃埋点会将其恢复为启用状态。
|
|
482
|
+
|
|
413
483
|
接入 SDK 后,Agent 可以用 `events count` 检查某天事件是否已经进入原始事件表:
|
|
414
484
|
|
|
415
485
|
```bash
|
package/dist/gamealgo.js
CHANGED
|
@@ -74,6 +74,10 @@ async function main() {
|
|
|
74
74
|
await handleEvents(client, args, global);
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
+
if (command === "integration") {
|
|
78
|
+
await handleIntegration(client, args, global);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
77
81
|
if (command === "marketing") {
|
|
78
82
|
await handleMarketing(client, args, global);
|
|
79
83
|
return;
|
|
@@ -832,9 +836,42 @@ async function handleReport(client, args, global) {
|
|
|
832
836
|
}
|
|
833
837
|
async function handleEvents(client, args, global) {
|
|
834
838
|
const sub = args.shift();
|
|
835
|
-
if (sub !== "count")
|
|
836
|
-
throw new Error("usage: gamealgo events count [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--event-type level_end]");
|
|
837
839
|
const flags = parseFlags(args);
|
|
840
|
+
if (sub === "list") {
|
|
841
|
+
if (args.length)
|
|
842
|
+
throw new Error("usage: gamealgo events list [--days 30] [--no-diagnostics]");
|
|
843
|
+
const days = Number(flags.days || 30);
|
|
844
|
+
if (!Number.isInteger(days) || days < 1 || days > 90)
|
|
845
|
+
throw new Error("--days must be an integer between 1 and 90");
|
|
846
|
+
await writeEventCatalogOutput(await client.listEventCatalog(days, flags["no-diagnostics"] !== true), flags, global);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (sub === "get") {
|
|
850
|
+
const eventType = args.shift();
|
|
851
|
+
if (!eventType || args.length)
|
|
852
|
+
throw new Error("usage: gamealgo events get <eventType>");
|
|
853
|
+
await writeEventCatalogOutput(await client.getEventCatalog(eventType), flags, global);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (sub === "apply") {
|
|
857
|
+
const filePath = args.shift();
|
|
858
|
+
if (!filePath || args.length)
|
|
859
|
+
throw new Error("usage: gamealgo events apply <events.yaml>");
|
|
860
|
+
const content = await readObjectFile(filePath, "event catalog");
|
|
861
|
+
await writeEventCatalogOutput(await client.applyEventCatalog(content), flags, global);
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
if (sub === "deprecate") {
|
|
865
|
+
const eventType = args.shift();
|
|
866
|
+
if (!eventType || args.length)
|
|
867
|
+
throw new Error("usage: gamealgo events deprecate <eventType> [--yes]");
|
|
868
|
+
if (!flags.yes && !(await confirm(`Deprecate event ${eventType}?`)))
|
|
869
|
+
throw new Error("Cancelled");
|
|
870
|
+
await writeEventCatalogOutput(await client.deprecateEventCatalog(eventType), flags, global);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (sub !== "count")
|
|
874
|
+
throw new Error("usage: gamealgo events <list|get|apply|deprecate|count>");
|
|
838
875
|
const timeoutMs = reportTimeoutMs(flags);
|
|
839
876
|
const startedAt = Date.now();
|
|
840
877
|
const stopProgress = startProgress("Querying event counts", timeoutMs);
|
|
@@ -859,6 +896,40 @@ async function handleEvents(client, args, global) {
|
|
|
859
896
|
}
|
|
860
897
|
await printResult(outputValue, global);
|
|
861
898
|
}
|
|
899
|
+
async function writeEventCatalogOutput(value, flags, global) {
|
|
900
|
+
if (flags.out) {
|
|
901
|
+
const filePath = String(flags.out);
|
|
902
|
+
const content = filePath.endsWith(".json") ? JSON.stringify(value, null, 2) + "\n" : YAML.stringify(value);
|
|
903
|
+
await writeTextFile(filePath, content);
|
|
904
|
+
await printResult({ ok: true, out: filePath }, global);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
await printResult(value, global);
|
|
908
|
+
}
|
|
909
|
+
async function handleIntegration(client, args, global) {
|
|
910
|
+
const sub = args.shift();
|
|
911
|
+
const flags = parseFlags(args);
|
|
912
|
+
if (args.length)
|
|
913
|
+
throw new Error("unexpected positional arguments for integration command");
|
|
914
|
+
const environment = integrationEnvironment(flags.environment);
|
|
915
|
+
const platform = integrationPlatform(flags.platform);
|
|
916
|
+
if (sub === "get-plan" || sub === "plan") {
|
|
917
|
+
const response = await client.getIntegrationPlan(environment, platform);
|
|
918
|
+
await writeIntegrationOutput(response, flags, global);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
throw new Error("usage: gamealgo integration get-plan --environment <domestic|overseas> --platform <ios|android|maker|rest>");
|
|
922
|
+
}
|
|
923
|
+
async function writeIntegrationOutput(value, flags, global) {
|
|
924
|
+
if (flags.out) {
|
|
925
|
+
if (typeof flags.out !== "string")
|
|
926
|
+
throw new Error("--out requires a file path");
|
|
927
|
+
await writeTextFile(flags.out, JSON.stringify(value, null, 2) + "\n");
|
|
928
|
+
await printResult({ ok: true, out: flags.out }, global);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
await printResult(value, global);
|
|
932
|
+
}
|
|
862
933
|
async function handleMarketing(client, args, global) {
|
|
863
934
|
const provider = args.shift();
|
|
864
935
|
if (provider !== "adjust")
|
|
@@ -1168,6 +1239,23 @@ class GameAlgoAdminClient {
|
|
|
1168
1239
|
async countEvents(body, options = {}) {
|
|
1169
1240
|
return await this.post(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/count`, body, options);
|
|
1170
1241
|
}
|
|
1242
|
+
async listEventCatalog(days = 30, diagnostics = true) {
|
|
1243
|
+
const params = new URLSearchParams({ days: String(days), diagnostics: diagnostics ? "1" : "0" });
|
|
1244
|
+
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/catalog?${params.toString()}`);
|
|
1245
|
+
}
|
|
1246
|
+
async getEventCatalog(eventType) {
|
|
1247
|
+
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/catalog/${encodeURIComponent(eventType)}`);
|
|
1248
|
+
}
|
|
1249
|
+
async applyEventCatalog(content) {
|
|
1250
|
+
return await this.put(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/catalog`, content);
|
|
1251
|
+
}
|
|
1252
|
+
async deprecateEventCatalog(eventType) {
|
|
1253
|
+
return await this.post(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/events/catalog/${encodeURIComponent(eventType)}/deprecate`, {});
|
|
1254
|
+
}
|
|
1255
|
+
async getIntegrationPlan(environment, platform) {
|
|
1256
|
+
const params = new URLSearchParams({ environment, platform });
|
|
1257
|
+
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/integration/plan?${params.toString()}`);
|
|
1258
|
+
}
|
|
1171
1259
|
async getAdjustMarketing() {
|
|
1172
1260
|
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/marketing/adjust`);
|
|
1173
1261
|
}
|
|
@@ -1680,6 +1768,18 @@ async function confirm(question) {
|
|
|
1680
1768
|
function normalizeHost(value) {
|
|
1681
1769
|
return value.trim().replace(/\/+$/, "");
|
|
1682
1770
|
}
|
|
1771
|
+
function integrationEnvironment(value) {
|
|
1772
|
+
const environment = optionalString(value);
|
|
1773
|
+
if (environment === "domestic" || environment === "overseas")
|
|
1774
|
+
return environment;
|
|
1775
|
+
throw new Error("--environment must be domestic or overseas");
|
|
1776
|
+
}
|
|
1777
|
+
function integrationPlatform(value) {
|
|
1778
|
+
const platform = optionalString(value);
|
|
1779
|
+
if (platform === "ios" || platform === "android" || platform === "maker" || platform === "rest")
|
|
1780
|
+
return platform;
|
|
1781
|
+
throw new Error("--platform must be ios, android, maker, or rest");
|
|
1782
|
+
}
|
|
1683
1783
|
function optionalString(value) {
|
|
1684
1784
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
1685
1785
|
}
|
|
@@ -1719,6 +1819,8 @@ Usage:
|
|
|
1719
1819
|
gamealgo docs experiment-integration-versions --open
|
|
1720
1820
|
gamealgo docs --host <admin-url>
|
|
1721
1821
|
|
|
1822
|
+
gamealgo integration get-plan --environment domestic --platform maker --out gamealgo-integration-plan.json
|
|
1823
|
+
|
|
1722
1824
|
gamealgo experiment strategies
|
|
1723
1825
|
gamealgo experiment strategies --include-archived
|
|
1724
1826
|
gamealgo experiment integration-version latest
|
|
@@ -1767,6 +1869,10 @@ Usage:
|
|
|
1767
1869
|
|
|
1768
1870
|
gamealgo events count --from 2026-06-23 --to 2026-06-23
|
|
1769
1871
|
gamealgo events count --from 2026-06-23 --to 2026-06-23 --event-type level_end
|
|
1872
|
+
gamealgo events list --days 30
|
|
1873
|
+
gamealgo events get level_start
|
|
1874
|
+
gamealgo events apply gamealgo-events.yaml
|
|
1875
|
+
gamealgo events deprecate old_event --yes
|
|
1770
1876
|
|
|
1771
1877
|
gamealgo marketing adjust get
|
|
1772
1878
|
gamealgo marketing adjust configure --api-token <token> --app-token <app-token> --platform ios --currency USD
|