@thinkingai/ae-cli 6.1.18 → 6.1.20

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 (127) hide show
  1. package/README.md +97 -38
  2. package/README.zh.md +101 -38
  3. package/dist/{auth-ROB2EDYV.js → auth-FUM37MHF.js} +241 -126
  4. package/dist/{capability-DKMYUTLC.js → capability-AXFQW5WM.js} +49 -35
  5. package/dist/{chunk-JHENBQ5B.js → chunk-4P355ZWB.js} +70 -1
  6. package/dist/chunk-6ZIQV5GW.js +26 -0
  7. package/dist/{chunk-TUKQZTMI.js → chunk-7K24F7T2.js} +2 -0
  8. package/dist/{chunk-OO6XR6LK.js → chunk-AMBFK2K3.js} +2 -0
  9. package/dist/chunk-ATSM5XAW.js +623 -0
  10. package/dist/{chunk-4XXOWOTA.js → chunk-BBEFP4SB.js} +31 -38
  11. package/dist/{chunk-BYYS3ANB.js → chunk-CZU3V3DQ.js} +5 -15
  12. package/dist/{chunk-VTWMIC5L.js → chunk-E2JKXMVO.js} +2 -0
  13. package/dist/chunk-ECBLHAZO.js +15 -0
  14. package/dist/{sync-HKIOZXQE.js → chunk-I4WQAEYB.js} +31 -540
  15. package/dist/{chunk-DWO43OIB.js → chunk-JQ3ENZZH.js} +2 -0
  16. package/dist/chunk-LNZBEQXW.js +25216 -0
  17. package/dist/{chunk-ZQ47LWTI.js → chunk-QJQZH5GQ.js} +49 -79
  18. package/dist/{chunk-QZ3AS4KK.js → chunk-RSG4ONOI.js} +9 -8
  19. package/dist/{chunk-4NN5IWVN.js → chunk-T6OSFQZD.js} +2 -0
  20. package/dist/{chunk-Y3LOALAV.js → chunk-TAL6CZH6.js} +8 -7
  21. package/dist/{chunk-3KWQYGYI.js → chunk-TKHSULJT.js} +2 -0
  22. package/dist/chunk-VXNMYUXU.js +202 -0
  23. package/dist/{chunk-3FY3RJ26.js → chunk-WZ6YVQSF.js} +15 -14
  24. package/dist/{chunk-6EIJSNBD.js → chunk-Y74WTIKJ.js} +2 -0
  25. package/dist/{community-report-client-C7WDGET3.js → community-report-client-XXWGSBSD.js} +3 -4
  26. package/dist/{config-BMYZX2UE.js → config-EXUGQN5T.js} +10 -10
  27. package/dist/{data-integration-QEKDWQDY.js → data-integration-4NZ53OMT.js} +920 -97
  28. package/dist/index.js +56 -81
  29. package/dist/{local-data-upload-client-4YYHSYD6.js → local-data-upload-client-KYOKVYZV.js} +3 -4
  30. package/dist/{memory-I2WXDTV2.js → memory-ATNKZNW5.js} +6 -7
  31. package/dist/{metadata-I4C2EWUN.js → metadata-VZVC7YMH.js} +10 -11
  32. package/dist/{model-HLHIEFMU.js → model-E4JMQ4V2.js} +8 -9
  33. package/dist/{personal-semantic-preference-LIPACBDX.js → personal-semantic-preference-7S2SQ3UV.js} +9 -10
  34. package/dist/{project-semantic-RT3R2VQD.js → project-semantic-2SZP2OPO.js} +13 -14
  35. package/dist/sync-YV3E66IF.js +520 -0
  36. package/dist/{te-agent-BR6VDBNX.js → te-agent-JB5T3PO7.js} +396 -92
  37. package/dist/{te-analysis-7VUNUYWZ.js → te-analysis-3YJAAT2D.js} +196 -43
  38. package/dist/{te-community-5DMNKJWY.js → te-community-UDBI672N.js} +12 -34
  39. package/dist/{te-dataops-6P5IKWNJ.js → te-dataops-ZLYOCXZ4.js} +480 -81
  40. package/dist/{te-engage-KZPR5R22.js → te-engage-4XG6OJML.js} +88 -16
  41. package/dist/{te-experiment-6BITX4RD.js → te-experiment-VXUWPINJ.js} +83 -12
  42. package/dist/te-kb-WYQWHFSC.js +1732 -0
  43. package/dist/{te-system-FXITO2JG.js → te-system-7G6F2LJA.js} +569 -35
  44. package/dist/{te-team-ADOC2ROP.js → te-team-E7FBBXMQ.js} +8 -7
  45. package/dist/{update-YCYCKJOO.js → update-D47BUG25.js} +8 -8
  46. package/package.json +22 -10
  47. package/skills/ae-agent/SKILL.md +30 -13
  48. package/skills/ae-agent/references/agent-distribution.md +94 -0
  49. package/skills/ae-agent/references/approval-request.md +4 -0
  50. package/skills/ae-agent/references/command_index.md +9 -2
  51. package/skills/ae-agent/references/create-automation.md +20 -2
  52. package/skills/ae-agent/references/get-agent-context.md +70 -0
  53. package/skills/ae-agent/references/list-automations.md +18 -3
  54. package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
  55. package/skills/ae-agent/references/mcp-token.md +3 -3
  56. package/skills/ae-agent/references/set-mcp-credential.md +0 -1
  57. package/skills/ae-agent/references/update-automation.md +18 -0
  58. package/skills/ae-analysis/SKILL.md +11 -2
  59. package/skills/ae-analysis/references/adhoc_run.md +2 -0
  60. package/skills/ae-analysis/references/ai_models.md +23 -3
  61. package/skills/ae-analysis/references/analysis_gateway_assets.md +3 -3
  62. package/skills/ae-analysis/references/audience_models.md +18 -0
  63. package/skills/ae-analysis/references/command_index.md +9 -9
  64. package/skills/ae-analysis/references/cross_source_config.md +84 -0
  65. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  66. package/skills/ae-analysis/references/project_timezone_update.md +13 -4
  67. package/skills/ae-analysis/references/property_create.md +2 -0
  68. package/skills/ae-analysis/references/super_metadata_batch_create.md +2 -0
  69. package/skills/ae-analysis/references/user_cluster_models.md +2 -0
  70. package/skills/ae-analysis/references/user_cluster_update.md +8 -4
  71. package/skills/ae-analysis/references/user_tag_create.md +30 -2
  72. package/skills/ae-analysis/references/user_tag_models.md +17 -3
  73. package/skills/ae-analysis/references/user_tag_update.md +14 -2
  74. package/skills/ae-data-integration/SKILL.md +3 -1
  75. package/skills/ae-data-integration/references/dimension-routing.md +36 -0
  76. package/skills/ae-data-integration/references/error-handling.md +54 -1
  77. package/skills/ae-data-integration/references/local-analysis.md +2 -0
  78. package/skills/ae-data-integration/references/source-inspect.md +18 -2
  79. package/skills/ae-data-integration/references/tracking-plan.md +1 -1
  80. package/skills/ae-data-integration/references/transform.md +4 -2
  81. package/skills/ae-data-integration/references/ue-mapping.md +5 -2
  82. package/skills/ae-data-integration/references/ue-routing.md +40 -1
  83. package/skills/ae-dataops/SKILL.md +11 -1
  84. package/skills/ae-dataops/references/dataops-backfill.md +135 -0
  85. package/skills/ae-engage/SKILL.md +5 -0
  86. package/skills/ae-engage/references/build-task-save-guide.md +5 -1
  87. package/skills/ae-engage/references/save-flow.md +37 -1
  88. package/skills/ae-engage/references/save-task.md +6 -0
  89. package/skills/ae-experiment/SKILL.md +6 -2
  90. package/skills/ae-experiment/references/save_metric.md +20 -8
  91. package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
  92. package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
  93. package/skills/ae-kb/SKILL.md +218 -36
  94. package/skills/ae-kb/references/query-workflow.md +59 -29
  95. package/skills/ae-kb/references/versions.md +46 -0
  96. package/skills/ae-system/SKILL.md +29 -31
  97. package/skills/ae-system/references/channel-management.md +303 -0
  98. package/skills/ae-use-agent/SKILL.md +42 -0
  99. package/skills/ae-use-agent/references/local-agent.md +114 -0
  100. package/dist/auth-GBMV6TEJ.js +0 -14
  101. package/dist/capability-HYVVPG25.js +0 -352
  102. package/dist/chunk-EFH4XWYC.js +0 -556
  103. package/dist/chunk-J2DEBMRF.js +0 -313
  104. package/dist/chunk-JRJY5DMJ.js +0 -71
  105. package/dist/chunk-OMPRXM3V.js +0 -349
  106. package/dist/chunk-QNOLN2LJ.js +0 -509
  107. package/dist/chunk-RJDU7NYP.js +0 -1198
  108. package/dist/chunk-RNAALWJK.js +0 -98
  109. package/dist/chunk-SERWF6G5.js +0 -13
  110. package/dist/chunk-UW5UN47B.js +0 -70
  111. package/dist/chunk-ZQKDZXDO.js +0 -317
  112. package/dist/client-L2YDMHQ6.js +0 -203
  113. package/dist/memory-3ORCR7JH.js +0 -893
  114. package/dist/metadata-VUOQJE26.js +0 -339
  115. package/dist/model-UGRDX4MW.js +0 -139
  116. package/dist/personal-semantic-preference-OEISBRHM.js +0 -239
  117. package/dist/project-semantic-FFPWFPIW.js +0 -1114
  118. package/dist/sync-TFHU2UTG.js +0 -10261
  119. package/dist/te-agent-VLYOV7S4.js +0 -3894
  120. package/dist/te-analysis-4YGQL5RC.js +0 -9357
  121. package/dist/te-community-ISDQWJU7.js +0 -1859
  122. package/dist/te-dataops-CVULXNVB.js +0 -2209
  123. package/dist/te-engage-N5WI32H6.js +0 -4898
  124. package/dist/te-experiment-UVR4HLND.js +0 -988
  125. package/dist/te-kb-RCLSSH2Q.js +0 -935
  126. package/dist/te-system-K2GYMCTB.js +0 -2213
  127. package/skills/ae-agent/references/auto-provision-mcp-credentials.md +0 -57
@@ -1,29 +1,28 @@
1
+ import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
1
2
  import {
2
3
  createCapabilityCommand
3
- } from "./chunk-Y3LOALAV.js";
4
- import {
5
- registerCapabilityGatewayRoute,
6
- resolveGatewayDomain
7
- } from "./chunk-3KWQYGYI.js";
8
- import {
9
- printError,
10
- withOutputMetadata
11
- } from "./chunk-BYYS3ANB.js";
4
+ } from "./chunk-TAL6CZH6.js";
12
5
  import {
13
6
  CapabilityGatewayError,
14
7
  buildCapabilityGatewayUrl,
15
8
  fetchCapabilityGateway,
16
9
  requestCapabilityGateway,
17
10
  requestCapabilityGatewayWithEnvelope
18
- } from "./chunk-ZQ47LWTI.js";
19
- import "./chunk-SERWF6G5.js";
20
- import "./chunk-J2DEBMRF.js";
21
- import "./chunk-ZQKDZXDO.js";
22
- import "./chunk-3FY3RJ26.js";
11
+ } from "./chunk-QJQZH5GQ.js";
12
+ import {
13
+ registerCapabilityGatewayRoute,
14
+ resolveGatewayDomain
15
+ } from "./chunk-TKHSULJT.js";
16
+ import {
17
+ printError,
18
+ withOutputMetadata
19
+ } from "./chunk-CZU3V3DQ.js";
20
+ import "./chunk-Y74WTIKJ.js";
21
+ import "./chunk-ATSM5XAW.js";
22
+ import "./chunk-WZ6YVQSF.js";
23
23
  import {
24
24
  CliValidationError
25
- } from "./chunk-UW5UN47B.js";
26
- import "./chunk-JHENBQ5B.js";
25
+ } from "./chunk-4P355ZWB.js";
27
26
 
28
27
  // src/commands/te-engage/shared.ts
29
28
  function createEngageCapabilityCommand(cliService, config) {
@@ -323,6 +322,9 @@ function validateSemanticMetricDefinition(value, path) {
323
322
  function validateEmbeddedSemanticDefinitions(value, path) {
324
323
  walk(value, path);
325
324
  }
325
+ function validateFlowAbIndicatorDefinitions(value, path) {
326
+ walkFlowAbIndicators(value, path);
327
+ }
326
328
  function validateTaskTriggerPeriodDefinition(value, path) {
327
329
  const request = object(value, path);
328
330
  if (!isObject(request.triggerConfig)) return;
@@ -335,6 +337,20 @@ function validateTaskTriggerPeriodDefinition(value, path) {
335
337
  required(primaryRule.periodTimeSymbol, symbolPath);
336
338
  enumValue(primaryRule.periodTimeSymbol, TASK_TRIGGER_PERIOD_SYMBOLS, symbolPath);
337
339
  }
340
+ function validateTaskAudienceChannelCompatibility(value, path) {
341
+ const request = object(value, path);
342
+ if (!isObject(request.channelConfig) || !isObject(request.targetConfig)) return;
343
+ const channelType = request.channelConfig.channelType;
344
+ const targetClusterType = request.targetConfig.targetClusterType;
345
+ if (typeof channelType !== "number" || typeof targetClusterType !== "number") return;
346
+ const clientSide = channelType === 3;
347
+ if (clientSide && targetClusterType === 2) {
348
+ invalid(`${path}.targetConfig.targetClusterType=2 (existing cluster) is not supported for client-side delivery; use 1 or 3.`);
349
+ }
350
+ if (!clientSide && targetClusterType === 3) {
351
+ invalid(`${path}.targetConfig.targetClusterType=3 (all users) is not supported for server-side delivery; use 1 or 2.`);
352
+ }
353
+ }
338
354
  function walk(value, path) {
339
355
  if (Array.isArray(value)) {
340
356
  value.forEach((item, index) => walk(item, `${path}[${index}]`));
@@ -357,6 +373,60 @@ function walk(value, path) {
357
373
  }
358
374
  }
359
375
  }
376
+ function walkFlowAbIndicators(value, path) {
377
+ if (Array.isArray(value)) {
378
+ value.forEach((item, index) => walkFlowAbIndicators(item, `${path}[${index}]`));
379
+ return;
380
+ }
381
+ if (!isObject(value)) return;
382
+ if (value.indicatorsDef !== void 0 && value.indicatorsDef !== null) {
383
+ array(value.indicatorsDef, `${path}.indicatorsDef`).forEach((item, index) => {
384
+ const indicatorPath = `${path}.indicatorsDef[${index}]`;
385
+ const indicator = object(item, indicatorPath);
386
+ const hasDefinition = indicator.eventDefinition !== void 0 && indicator.eventDefinition !== null;
387
+ const hasLegacyEvent = indicator.event !== void 0 && indicator.event !== null;
388
+ if (hasDefinition && hasLegacyEvent) {
389
+ invalid(`${indicatorPath} cannot contain both event and eventDefinition.`);
390
+ }
391
+ if (!hasDefinition && !hasLegacyEvent) {
392
+ invalid(`${indicatorPath} must contain eventDefinition or legacy event.`);
393
+ }
394
+ if (hasDefinition) validateFlowAbIndicatorEvent(indicator.eventDefinition, `${indicatorPath}.eventDefinition`);
395
+ });
396
+ }
397
+ for (const [key, item] of Object.entries(value)) {
398
+ const itemPath = `${path}.${key}`;
399
+ if (key === "config" && typeof item === "string") {
400
+ try {
401
+ walkFlowAbIndicators(JSON.parse(item), itemPath);
402
+ } catch (error) {
403
+ if (error instanceof SyntaxError) invalid(`${itemPath} must contain valid JSON.`);
404
+ throw error;
405
+ }
406
+ } else {
407
+ walkFlowAbIndicators(item, itemPath);
408
+ }
409
+ }
410
+ }
411
+ function validateFlowAbIndicatorEvent(value, path) {
412
+ validateSemanticEventDefinition(value, path);
413
+ const event = object(value, path);
414
+ enumValue(event.aggregation, AUDIENCE_AGGREGATIONS, `${path}.aggregation`);
415
+ enumValue(event.operator, /* @__PURE__ */ new Set(["eq", "neq", "lt", "lte", "gt", "gte"]), `${path}.operator`);
416
+ required(event.value, `${path}.value`);
417
+ optionalNumber(event.value, `${path}.value`);
418
+ if (["sum", "avg", "max", "min", "distinct_count"].includes(String(event.aggregation))) {
419
+ nonBlankString(event.property, `${path}.property`);
420
+ }
421
+ if (isObject(event.filters)) {
422
+ array(event.filters.items, `${path}.filters.items`, 1).forEach((filterValue, index) => {
423
+ const filter = object(filterValue, `${path}.filters.items[${index}]`);
424
+ if (!["exists", "not_exists", "is_true", "is_false"].includes(String(filter.operator))) {
425
+ array(filter.values, `${path}.filters.items[${index}].values`, 1);
426
+ }
427
+ });
428
+ }
429
+ }
360
430
  function validateTriggerDefinition(value, path) {
361
431
  const definition = object(value, path);
362
432
  rejectUnknown(definition, ["rules"], path);
@@ -608,6 +678,7 @@ function readRequest(ctx) {
608
678
  throw new Error("Flag --req.sourceFlowUuid is unsupported; get the source flow and build nodes/edges instead");
609
679
  }
610
680
  validateEmbeddedSemanticDefinitions(req, "--req");
681
+ validateFlowAbIndicatorDefinitions(req, "--req");
611
682
  return req;
612
683
  }
613
684
  var flowSave = createEngageFlowCapabilityCommand({
@@ -4609,6 +4680,7 @@ var taskSave = createEngageTaskCapabilityCommand({
4609
4680
  validate: (ctx) => {
4610
4681
  const req = readRequiredJsonObject(ctx, "req");
4611
4682
  validateEmbeddedSemanticDefinitions(req, "--req");
4683
+ validateTaskAudienceChannelCompatibility(req, "--req");
4612
4684
  validateTaskTriggerPeriodDefinition(req, "--req");
4613
4685
  },
4614
4686
  buildInput: (ctx) => ({ project_id: ctx.num("project-id"), req: readRequiredJsonObject(ctx, "req") })
@@ -1,17 +1,16 @@
1
+ import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
1
2
  import {
2
3
  createCapabilityCommand
3
- } from "./chunk-JRJY5DMJ.js";
4
+ } from "./chunk-TAL6CZH6.js";
5
+ import "./chunk-QJQZH5GQ.js";
4
6
  import {
5
7
  registerCapabilityGatewayRoute
6
- } from "./chunk-3KWQYGYI.js";
7
- import "./chunk-BYYS3ANB.js";
8
- import "./chunk-OMPRXM3V.js";
9
- import "./chunk-6EIJSNBD.js";
10
- import "./chunk-J2DEBMRF.js";
11
- import "./chunk-ZQKDZXDO.js";
12
- import "./chunk-3FY3RJ26.js";
13
- import "./chunk-UW5UN47B.js";
14
- import "./chunk-JHENBQ5B.js";
8
+ } from "./chunk-TKHSULJT.js";
9
+ import "./chunk-CZU3V3DQ.js";
10
+ import "./chunk-Y74WTIKJ.js";
11
+ import "./chunk-ATSM5XAW.js";
12
+ import "./chunk-WZ6YVQSF.js";
13
+ import "./chunk-4P355ZWB.js";
15
14
 
16
15
  // src/commands/te-experiment/capability-shared.ts
17
16
  function createExperimentCapabilityCommand(config) {
@@ -650,6 +649,78 @@ var metricList = createExperimentCapabilityCommand({
650
649
  });
651
650
 
652
651
  // src/commands/te-experiment/metric/save.ts
652
+ var aggregations = /* @__PURE__ */ new Set([
653
+ "total_count",
654
+ "user_count",
655
+ "active_days",
656
+ "sum",
657
+ "avg",
658
+ "max",
659
+ "distinct_count"
660
+ ]);
661
+ var propertyAggregations = /* @__PURE__ */ new Set(["sum", "avg", "max", "distinct_count"]);
662
+ var metricDefinitionFields = /* @__PURE__ */ new Set([
663
+ "type",
664
+ "event",
665
+ "aggregation",
666
+ "property",
667
+ "filters"
668
+ ]);
669
+ function validateMetricFilters(rawFilters) {
670
+ if (rawFilters === void 0) return;
671
+ if (!rawFilters || typeof rawFilters !== "object" || Array.isArray(rawFilters)) {
672
+ throw new Error("Flag --req.metricDefinition.filters must be a JSON object");
673
+ }
674
+ const filters = rawFilters;
675
+ if (!["and", "or"].includes(String(filters.relation))) {
676
+ throw new Error("Flag --req.metricDefinition.filters.relation must be one of: and, or");
677
+ }
678
+ if (!Array.isArray(filters.items) || filters.items.length === 0) {
679
+ throw new Error("Flag --req.metricDefinition.filters.items must be a non-empty array");
680
+ }
681
+ filters.items.forEach((rawItem, index) => {
682
+ const path = `Flag --req.metricDefinition.filters.items[${index}]`;
683
+ if (!rawItem || typeof rawItem !== "object" || Array.isArray(rawItem)) {
684
+ throw new Error(`${path} must be a JSON object`);
685
+ }
686
+ const item = rawItem;
687
+ const field = item.field;
688
+ const validField = typeof field === "string" && field.trim() !== "" || Boolean(field && typeof field === "object" && !Array.isArray(field) && typeof field.name === "string" && String(field.name).trim() !== "");
689
+ if (!validField) throw new Error(`${path}.field is required`);
690
+ if (typeof item.operator !== "string" || item.operator.trim() === "") {
691
+ throw new Error(`${path}.operator is required and expresses the property comparison`);
692
+ }
693
+ if (!Array.isArray(item.values)) {
694
+ throw new Error(`${path}.values must be an array containing the comparison value(s)`);
695
+ }
696
+ });
697
+ }
698
+ function validateMetricSaveRequest(req) {
699
+ const rawDefinition = req.metricDefinition;
700
+ if (rawDefinition === void 0 && req.update === true) return;
701
+ if (!rawDefinition || typeof rawDefinition !== "object" || Array.isArray(rawDefinition)) {
702
+ throw new Error("Flag --req.metricDefinition must be a JSON object");
703
+ }
704
+ const definition = rawDefinition;
705
+ for (const field of Object.keys(definition)) {
706
+ if (!metricDefinitionFields.has(field)) {
707
+ throw new Error(`Flag --req.metricDefinition.${field} is not supported`);
708
+ }
709
+ }
710
+ if (definition.type !== "event") {
711
+ throw new Error("Flag --req.metricDefinition.type must be event");
712
+ }
713
+ if (typeof definition.event !== "string" || definition.event.trim() === "") {
714
+ throw new Error("Flag --req.metricDefinition.event is required");
715
+ }
716
+ if (typeof definition.aggregation !== "string" || !aggregations.has(definition.aggregation)) {
717
+ throw new Error(`Flag --req.metricDefinition.aggregation must be one of: ${[...aggregations].join(", ")}`);
718
+ }
719
+ if (propertyAggregations.has(definition.aggregation) && (typeof definition.property !== "string" || definition.property.trim() === "")) {
720
+ throw new Error(`Flag --req.metricDefinition.property is required for ${definition.aggregation}`);
721
+ }
722
+ validateMetricFilters(definition.filters);
723
+ }
653
724
  var metricSave = createExperimentCapabilityCommand({
654
725
  resource: "metric",
655
726
  command: "save",
@@ -661,12 +732,12 @@ var metricSave = createExperimentCapabilityCommand({
661
732
  name: "req",
662
733
  type: "json",
663
734
  required: true,
664
- desc: "Native camelCase request; use metricDefinition, never metricConfig or calcType."
735
+ desc: "Native camelCase request. Property comparisons use metricDefinition.filters={relation,items:[{field,operator,values}]}. Never use metricConfig, calcType, or raw filts."
665
736
  }
666
737
  ],
667
738
  risk: "write",
668
739
  validate: (ctx) => {
669
- readRequiredObject(ctx, "req");
740
+ validateMetricSaveRequest(readRequiredObject(ctx, "req"));
670
741
  },
671
742
  buildInput: (ctx) => ({ project_id: ctx.num("project-id"), req: readRequiredObject(ctx, "req") })
672
743
  });