@thinkingai/ae-cli 6.1.9 → 6.1.11
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 +25 -3
- package/README.zh.md +25 -3
- package/dist/{auth-56Z45UVR.js → auth-GDV3H5I4.js} +18 -8
- package/dist/{auth-ENM3FE6L.js → auth-NN55553T.js} +3 -3
- package/dist/{capability-2H6PAOA3.js → capability-TPORIKRQ.js} +19 -15
- package/dist/{capability-YPOQX6PL.js → capability-VQNC5CF7.js} +19 -15
- package/dist/chunk-4NN5IWVN.js +26 -0
- package/dist/{chunk-DB4Q3ANU.js → chunk-6A2FUCIS.js} +3 -3
- package/dist/{chunk-PVBYJWC2.js → chunk-7KQWSBSL.js} +4 -4
- package/dist/{chunk-24BAVOX3.js → chunk-AXDXJTPC.js} +3 -1
- package/dist/{chunk-CPVTECJ3.js → chunk-DT6Y3TD7.js} +5 -5
- package/dist/{chunk-EGEIXA2Z.js → chunk-DWO43OIB.js} +0 -248
- package/dist/{chunk-V6FR6WTW.js → chunk-GJJA4CQZ.js} +34 -2
- package/dist/{chunk-RGXCNC4N.js → chunk-GS2P7LFD.js} +50 -17
- package/dist/{chunk-LYVNONC4.js → chunk-JHENBQ5B.js} +35 -0
- package/dist/{chunk-3P3562ZX.js → chunk-MVDZ7DBQ.js} +5 -5
- package/dist/{chunk-LCXU3AAT.js → chunk-NBPKWKRA.js} +2 -2
- package/dist/{chunk-HQ2A7ITL.js → chunk-RBNKI5ZW.js} +50 -17
- package/dist/{chunk-LHVM35J4.js → chunk-TS6BUGUY.js} +4 -4
- package/dist/chunk-TUKQZTMI.js +250 -0
- package/dist/{chunk-J7MZHDHQ.js → chunk-VKD5WQKN.js} +5 -5
- package/dist/{chunk-3KI3RRXX.js → chunk-VTXHDCBW.js} +3 -3
- package/dist/{chunk-KTYR3U6D.js → chunk-YTG6Q75E.js} +10 -6
- package/dist/{chunk-IR4ZLVPW.js → chunk-YV52FB5G.js} +25 -3
- package/dist/{chunk-E7UXXHO3.js → chunk-Z3OXWCIA.js} +3 -3
- package/dist/{cli-token-4SBMXAUK.js → cli-token-GL5MS5FK.js} +4 -4
- package/dist/{client-PP5FETMW.js → client-DAIPF7XN.js} +4 -4
- package/dist/{community-report-client-KK2QBANO.js → community-report-client-M2RW4MXD.js} +2 -2
- package/dist/config-4VZNLBKF.js +489 -0
- package/dist/index.js +94 -35
- package/dist/memory-RWJW4XFO.js +892 -0
- package/dist/memory-VO2ZJCRT.js +892 -0
- package/dist/{metadata-JIQ77HFY.js → metadata-YGTHR2XJ.js} +8 -8
- package/dist/{metadata-AN3YFZEV.js → metadata-ZRN2GHPN.js} +8 -8
- package/dist/{model-JASQVOFD.js → model-CLUIK3K5.js} +7 -5
- package/dist/{raw-XJCAT3HX.js → raw-52B4UKO4.js} +8 -7
- package/dist/sync-3REDHGY6.js +10259 -0
- package/dist/{te-agent-JHUG6DVV.js → te-agent-XNPELAKX.js} +580 -84
- package/dist/{te-analysis-IWQZO423.js → te-analysis-GJI5FZUL.js} +1611 -25
- package/dist/{te-analysis-X222IRYR.js → te-analysis-N2BRDJZ5.js} +1611 -25
- package/dist/{te-community-QOYIYEJI.js → te-community-SQXKE5OO.js} +7 -7
- package/dist/{te-community-UFKI6ONP.js → te-community-TYSNU3NQ.js} +7 -7
- package/dist/{te-dataops-PZQ5NQLY.js → te-dataops-5TM7WZDI.js} +370 -189
- package/dist/{te-dataops-XTWVTJCA.js → te-dataops-OWIADNSM.js} +370 -189
- package/dist/{te-engage-NLZUPSBK.js → te-engage-F7V55KVW.js} +333 -105
- package/dist/{te-engage-E7F4HTXU.js → te-engage-FMYAYCNV.js} +333 -105
- package/dist/{te-experiment-N63WF7XA.js → te-experiment-JWXOYJ3W.js} +83 -10
- package/dist/{te-experiment-D32TB6ZB.js → te-experiment-UPDMHCAJ.js} +83 -10
- package/dist/{te-kb-E7NSCBRB.js → te-kb-VRMEY3D4.js} +6 -6
- package/dist/{te-meta-GBDTMPEL.js → te-meta-53BVXPFI.js} +7 -7
- package/dist/{te-meta-ZTLTSHXC.js → te-meta-TOCBPBXI.js} +7 -7
- package/dist/{te-system-AH7DMCAQ.js → te-system-XGS5EQIQ.js} +4 -4
- package/dist/{te-team-BQ3SKSZV.js → te-team-BZRDV2CM.js} +7 -7
- package/dist/{update-DKG6UXEM.js → update-HEDXGOJH.js} +8 -6
- package/package.json +5 -2
- package/skills/ae-agent/SKILL.md +178 -16
- package/skills/ae-agent/references/add-skill.md +22 -10
- package/skills/ae-agent/references/edit-skill.md +24 -14
- package/skills/ae-agent/references/find-archived-conversations.md +82 -0
- package/skills/ae-agent/references/restore-conversation.md +54 -0
- package/skills/ae-agent/references/upload-skill.md +24 -14
- package/skills/ae-analysis/SKILL.md +1 -1
- package/skills/ae-analysis/references/command_index.md +109 -42
- package/skills/ae-analysis/references/debug_data_list.md +28 -0
- package/skills/ae-analysis/references/debug_device_add.md +23 -0
- package/skills/ae-analysis/references/debug_device_list.md +19 -0
- package/skills/ae-analysis/references/debug_device_select.md +22 -0
- package/skills/ae-analysis/references/entity_id_import_options.md +1 -1
- package/skills/ae-analysis/references/plan_sync_display_names.md +28 -0
- package/skills/ae-analysis/references/project_access_detail_get.md +3 -3
- package/skills/ae-analysis/references/project_data_power_delete.md +3 -3
- package/skills/ae-analysis/references/project_data_power_get.md +3 -3
- package/skills/ae-analysis/references/project_data_power_list.md +3 -3
- package/skills/ae-analysis/references/project_data_power_upsert.md +3 -3
- package/skills/ae-analysis/references/project_entity_create.md +3 -3
- package/skills/ae-analysis/references/project_entity_delete.md +3 -3
- package/skills/ae-analysis/references/project_entity_event_list.md +3 -3
- package/skills/ae-analysis/references/project_entity_get.md +3 -3
- package/skills/ae-analysis/references/project_entity_list.md +3 -3
- package/skills/ae-analysis/references/project_entity_update.md +3 -3
- package/skills/ae-analysis/references/project_function_list.md +3 -3
- package/skills/ae-analysis/references/project_info_create.md +25 -0
- package/skills/ae-analysis/references/project_info_delete.md +24 -0
- package/skills/ae-analysis/references/project_info_get.md +3 -3
- package/skills/ae-analysis/references/project_info_list.md +3 -3
- package/skills/ae-analysis/references/project_info_update.md +3 -3
- package/skills/ae-analysis/references/project_mark_time_create.md +3 -3
- package/skills/ae-analysis/references/project_mark_time_delete.md +3 -3
- package/skills/ae-analysis/references/project_mark_time_list.md +3 -3
- package/skills/ae-analysis/references/project_mark_time_update.md +3 -3
- package/skills/ae-analysis/references/project_member_add.md +3 -3
- package/skills/ae-analysis/references/project_member_batch_update.md +3 -3
- package/skills/ae-analysis/references/project_member_candidate_list.md +3 -3
- package/skills/ae-analysis/references/project_member_handover_export.md +3 -3
- package/skills/ae-analysis/references/project_member_handover_run.md +3 -3
- package/skills/ae-analysis/references/project_member_import.md +3 -3
- package/skills/ae-analysis/references/project_member_list.md +3 -3
- package/skills/ae-analysis/references/project_member_receiver_list.md +3 -3
- package/skills/ae-analysis/references/project_member_remove.md +3 -3
- package/skills/ae-analysis/references/project_member_update.md +3 -3
- package/skills/ae-analysis/references/project_owner_update.md +3 -3
- package/skills/ae-analysis/references/project_permission_binding_list.md +3 -3
- package/skills/ae-analysis/references/project_receive_status_update.md +3 -3
- package/skills/ae-analysis/references/project_role_delete.md +3 -3
- package/skills/ae-analysis/references/project_role_function_list.md +3 -3
- package/skills/ae-analysis/references/project_role_get.md +3 -3
- package/skills/ae-analysis/references/project_role_list.md +3 -3
- package/skills/ae-analysis/references/project_role_upsert.md +3 -3
- package/skills/ae-analysis/references/project_role_user_list.md +3 -3
- package/skills/ae-analysis/references/project_space_list.md +1 -1
- package/skills/ae-analysis/references/project_timezone_get.md +3 -3
- package/skills/ae-analysis/references/project_timezone_overview.md +3 -3
- package/skills/ae-analysis/references/project_timezone_update.md +3 -3
- package/skills/ae-analysis/references/project_user_id_items_update.md +3 -3
- package/skills/ae-analysis/references/system_admin_function_list.md +22 -0
- package/skills/ae-analysis/references/system_admin_function_update.md +26 -0
- package/skills/ae-analysis/references/system_admin_list.md +21 -0
- package/skills/ae-analysis/references/system_admin_remove.md +25 -0
- package/skills/ae-analysis/references/system_admin_upsert.md +25 -0
- package/skills/ae-analysis/references/system_function_list.md +21 -0
- package/skills/ae-analysis/references/system_member_add.md +25 -0
- package/skills/ae-analysis/references/system_member_candidate_list.md +22 -0
- package/skills/ae-analysis/references/system_member_delete.md +25 -0
- package/skills/ae-analysis/references/system_member_list.md +24 -0
- package/skills/ae-analysis/references/system_member_mfa_unbind.md +25 -0
- package/skills/ae-analysis/references/system_member_password_reset.md +32 -0
- package/skills/ae-analysis/references/system_member_project_batch_update.md +27 -0
- package/skills/ae-analysis/references/system_member_status_update.md +26 -0
- package/skills/ae-analysis/references/system_member_update.md +23 -0
- package/skills/ae-analysis/references/system_mfa_get.md +21 -0
- package/skills/ae-analysis/references/system_mfa_update.md +25 -0
- package/skills/ae-analysis/references/system_node_monitor_list.md +24 -0
- package/skills/ae-analysis/references/system_oauth2_update.md +22 -0
- package/skills/ae-analysis/references/system_ops_alert_contact_delete.md +25 -0
- package/skills/ae-analysis/references/system_ops_alert_contact_list.md +23 -0
- package/skills/ae-analysis/references/system_ops_alert_contact_test.md +29 -0
- package/skills/ae-analysis/references/system_ops_alert_contact_upsert.md +39 -0
- package/skills/ae-analysis/references/system_preference_get.md +21 -0
- package/skills/ae-analysis/references/system_preference_update.md +22 -0
- package/skills/ae-analysis/references/system_project_usage_list.md +28 -0
- package/skills/ae-analysis/references/system_query_alert_rule_list.md +21 -0
- package/skills/ae-analysis/references/system_query_alert_rule_update.md +26 -0
- package/skills/ae-analysis/references/system_query_monitor_overview.md +26 -0
- package/skills/ae-analysis/references/system_query_task_cancel.md +25 -0
- package/skills/ae-analysis/references/system_query_task_export.md +49 -0
- package/skills/ae-analysis/references/system_query_task_get.md +23 -0
- package/skills/ae-analysis/references/system_query_task_list.md +35 -0
- package/skills/ae-analysis/references/system_query_task_options.md +27 -0
- package/skills/ae-analysis/references/system_receiver_address_delete.md +26 -0
- package/skills/ae-analysis/references/system_receiver_address_overview.md +21 -0
- package/skills/ae-analysis/references/system_receiver_address_project_list.md +21 -0
- package/skills/ae-analysis/references/system_receiver_address_promote.md +24 -0
- package/skills/ae-analysis/references/system_receiver_address_upsert.md +27 -0
- package/skills/ae-analysis/references/system_receiver_detection_get.md +22 -0
- package/skills/ae-analysis/references/system_receiver_detection_run.md +22 -0
- package/skills/ae-analysis/references/system_receiver_detection_update.md +25 -0
- package/skills/ae-analysis/references/system_role_delete.md +26 -0
- package/skills/ae-analysis/references/system_role_function_list.md +22 -0
- package/skills/ae-analysis/references/system_role_get.md +22 -0
- package/skills/ae-analysis/references/system_role_list.md +24 -0
- package/skills/ae-analysis/references/system_role_upsert.md +27 -0
- package/skills/ae-analysis/references/system_role_user_list.md +22 -0
- package/skills/ae-analysis/references/system_seat_list.md +25 -0
- package/skills/ae-analysis/references/system_seat_update.md +26 -0
- package/skills/ae-analysis/references/system_smtp_delete.md +24 -0
- package/skills/ae-analysis/references/system_smtp_get.md +21 -0
- package/skills/ae-analysis/references/system_smtp_test.md +22 -0
- package/skills/ae-analysis/references/system_smtp_upsert.md +31 -0
- package/skills/ae-analysis/references/system_third_party_login_disable.md +25 -0
- package/skills/ae-analysis/references/system_third_party_login_list.md +21 -0
- package/skills/ae-analysis/references/system_third_party_login_upsert.md +33 -0
- package/skills/ae-analysis/references/system_usage_overview.md +21 -0
- package/skills/ae-analysis/references/system_usage_trend_export.md +44 -0
- package/skills/ae-analysis/references/system_usage_trend_query.md +28 -0
- package/skills/ae-dataops/SKILL.md +1 -1
- package/skills/ae-dataops/references/dataops-flow-create.md +51 -16
- package/skills/ae-engage/SKILL.md +56 -26
- package/skills/ae-engage/references/activity-activity.md +3 -0
- package/skills/ae-engage/references/activity-approval.md +12 -4
- package/skills/ae-engage/references/activity-task.md +21 -6
- package/skills/ae-engage/references/activity-topic.md +31 -13
- package/skills/ae-engage/references/add-channel.md +170 -41
- package/skills/ae-engage/references/build-task-save-guide.md +40 -27
- package/skills/ae-engage/references/channel-update-config.md +3 -2
- package/skills/ae-engage/references/common-metric.md +48 -120
- package/skills/ae-engage/references/flow-detail.md +13 -0
- package/skills/ae-engage/references/preset-event.md +12 -32
- package/skills/ae-engage/references/save-flow.md +127 -64
- package/skills/ae-engage/references/save-task.md +197 -59
- package/skills/ae-engage/references/scene-config-metric.md +3 -0
- package/skills/ae-engage/references/scene-preset-metric.md +8 -37
- package/skills/ae-engage/references/scene-strategy-audience.md +51 -643
- package/skills/ae-engage/references/scene-strategy.md +6 -6
- package/skills/ae-engage/references/task-detail.md +10 -0
- package/skills/ae-engage/references/task-submit-approval.md +45 -0
- package/skills/ae-engage/references/validate-flow-node-config.md +1 -1
- package/skills/ae-experiment/SKILL.md +21 -1
- package/skills/ae-experiment/references/check_experiment_ready.md +2 -0
- package/skills/ae-experiment/references/query_experiment_detail.md +6 -0
- package/skills/ae-experiment/references/query_experiment_list.md +4 -0
- package/skills/ae-experiment/references/query_experiment_list_archived.md +3 -0
- package/skills/ae-experiment/references/query_metric_detail.md +5 -0
- package/skills/ae-experiment/references/query_metric_list.md +3 -0
- package/skills/ae-experiment/references/save_experiment.md +46 -3
- package/skills/ae-experiment/references/save_metric.md +62 -0
- package/skills/ae-experiment/references/save_submit_experiment.md +3 -0
- package/skills/ae-experiment-design/SKILL.md +149 -0
- package/skills/ae-experiment-design/agents/openai.yaml +4 -0
- package/skills/ae-experiment-design/references/client-experiment-sdk.md +147 -0
- package/skills/ae-experiment-design/references/experiment-creation.md +108 -0
- package/skills/ae-experiment-design/references/experiment-sdk-contract.md +100 -0
- package/skills/ae-experiment-design/references/exposure-contract.md +91 -0
- package/skills/ae-experiment-design/references/hybrid-experiment-sdk.md +74 -0
- package/skills/ae-experiment-design/references/metric-readiness.md +143 -0
- package/skills/ae-experiment-design/references/platform-operations.md +99 -0
- package/skills/ae-experiment-design/references/sdk-index.md +76 -0
- package/skills/ae-experiment-design/references/sdk-integration.md +114 -0
- package/skills/ae-experiment-design/references/sdk-troubleshooting.md +139 -0
- package/skills/ae-experiment-design/references/server-experiment-sdk.md +78 -0
- package/skills/ae-experiment-design/scripts/calculate_experiment_plan.py +450 -0
- package/skills/ae-experiment-insight/SKILL.md +149 -0
- package/skills/ae-experiment-insight/agents/openai.yaml +4 -0
- package/skills/ae-experiment-insight/references/decision-framework.md +69 -0
- package/skills/ae-experiment-insight/references/diagnostic-playbook.md +225 -0
- package/skills/ae-experiment-insight/references/platform-operations.md +76 -0
- package/skills/ae-experiment-insight/scripts/analyze_experiment.py +478 -0
- package/skills/ae-generate-tracking-code/SKILL.md +24 -8
- package/skills/ae-generate-tracking-code/references/debug-script.md +46 -37
- package/skills/ae-generate-tracking-plan/SKILL.md +26 -1
- package/skills/ae-metadata/SKILL.md +1 -1
- package/dist/config-BSSALXEN.js +0 -128
- package/dist/sync-QFP4XFN3.js +0 -485
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Calculate deterministic SRM and treatment-versus-control comparisons."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def normal_ppf(probability: float) -> float:
|
|
13
|
+
"""Return the inverse standard-normal CDF using Acklam's approximation."""
|
|
14
|
+
if not 0.0 < probability < 1.0:
|
|
15
|
+
raise ValueError("probability must be between 0 and 1")
|
|
16
|
+
a = (
|
|
17
|
+
-3.969683028665376e01,
|
|
18
|
+
2.209460984245205e02,
|
|
19
|
+
-2.759285104469687e02,
|
|
20
|
+
1.383577518672690e02,
|
|
21
|
+
-3.066479806614716e01,
|
|
22
|
+
2.506628277459239e00,
|
|
23
|
+
)
|
|
24
|
+
b = (
|
|
25
|
+
-5.447609879822406e01,
|
|
26
|
+
1.615858368580409e02,
|
|
27
|
+
-1.556989798598866e02,
|
|
28
|
+
6.680131188771972e01,
|
|
29
|
+
-1.328068155288572e01,
|
|
30
|
+
)
|
|
31
|
+
c = (
|
|
32
|
+
-7.784894002430293e-03,
|
|
33
|
+
-3.223964580411365e-01,
|
|
34
|
+
-2.400758277161838e00,
|
|
35
|
+
-2.549732539343734e00,
|
|
36
|
+
4.374664141464968e00,
|
|
37
|
+
2.938163982698783e00,
|
|
38
|
+
)
|
|
39
|
+
d = (
|
|
40
|
+
7.784695709041462e-03,
|
|
41
|
+
3.224671290700398e-01,
|
|
42
|
+
2.445134137142996e00,
|
|
43
|
+
3.754408661907416e00,
|
|
44
|
+
)
|
|
45
|
+
low = 0.02425
|
|
46
|
+
high = 1.0 - low
|
|
47
|
+
if probability < low:
|
|
48
|
+
q = math.sqrt(-2.0 * math.log(probability))
|
|
49
|
+
return (
|
|
50
|
+
(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
|
|
51
|
+
/ ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0)
|
|
52
|
+
)
|
|
53
|
+
if probability <= high:
|
|
54
|
+
q = probability - 0.5
|
|
55
|
+
r = q * q
|
|
56
|
+
return (
|
|
57
|
+
(((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5])
|
|
58
|
+
* q
|
|
59
|
+
/ (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1.0)
|
|
60
|
+
)
|
|
61
|
+
q = math.sqrt(-2.0 * math.log(1.0 - probability))
|
|
62
|
+
return -(
|
|
63
|
+
(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
|
|
64
|
+
/ ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def regularized_gamma_q(shape: float, value: float) -> float:
|
|
69
|
+
"""Return the regularized upper incomplete gamma function Q(shape, value)."""
|
|
70
|
+
if shape <= 0.0 or value < 0.0:
|
|
71
|
+
raise ValueError("invalid gamma arguments")
|
|
72
|
+
if value == 0.0:
|
|
73
|
+
return 1.0
|
|
74
|
+
epsilon = 3e-14
|
|
75
|
+
minimum = 1e-300
|
|
76
|
+
max_iterations = 10000
|
|
77
|
+
log_gamma = math.lgamma(shape)
|
|
78
|
+
|
|
79
|
+
if value < shape + 1.0:
|
|
80
|
+
term = 1.0 / shape
|
|
81
|
+
total = term
|
|
82
|
+
current = shape
|
|
83
|
+
for _ in range(max_iterations):
|
|
84
|
+
current += 1.0
|
|
85
|
+
term *= value / current
|
|
86
|
+
total += term
|
|
87
|
+
if abs(term) < abs(total) * epsilon:
|
|
88
|
+
lower = total * math.exp(-value + shape * math.log(value) - log_gamma)
|
|
89
|
+
return max(0.0, min(1.0, 1.0 - lower))
|
|
90
|
+
raise ValueError("gamma series did not converge")
|
|
91
|
+
|
|
92
|
+
b = value + 1.0 - shape
|
|
93
|
+
c = 1.0 / minimum
|
|
94
|
+
d = 1.0 / b
|
|
95
|
+
h = d
|
|
96
|
+
for iteration in range(1, max_iterations + 1):
|
|
97
|
+
coefficient = -iteration * (iteration - shape)
|
|
98
|
+
b += 2.0
|
|
99
|
+
d = coefficient * d + b
|
|
100
|
+
if abs(d) < minimum:
|
|
101
|
+
d = minimum
|
|
102
|
+
c = b + coefficient / c
|
|
103
|
+
if abs(c) < minimum:
|
|
104
|
+
c = minimum
|
|
105
|
+
d = 1.0 / d
|
|
106
|
+
delta = d * c
|
|
107
|
+
h *= delta
|
|
108
|
+
if abs(delta - 1.0) < epsilon:
|
|
109
|
+
result = math.exp(-value + shape * math.log(value) - log_gamma) * h
|
|
110
|
+
return max(0.0, min(1.0, result))
|
|
111
|
+
raise ValueError("gamma continued fraction did not converge")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def beta_continued_fraction(a: float, b: float, x: float) -> float:
|
|
115
|
+
max_iterations = 10000
|
|
116
|
+
epsilon = 3e-14
|
|
117
|
+
minimum = 1e-300
|
|
118
|
+
qab = a + b
|
|
119
|
+
qap = a + 1.0
|
|
120
|
+
qam = a - 1.0
|
|
121
|
+
c = 1.0
|
|
122
|
+
d = 1.0 - qab * x / qap
|
|
123
|
+
if abs(d) < minimum:
|
|
124
|
+
d = minimum
|
|
125
|
+
d = 1.0 / d
|
|
126
|
+
h = d
|
|
127
|
+
for iteration in range(1, max_iterations + 1):
|
|
128
|
+
even = 2 * iteration
|
|
129
|
+
coefficient = iteration * (b - iteration) * x / ((qam + even) * (a + even))
|
|
130
|
+
d = 1.0 + coefficient * d
|
|
131
|
+
if abs(d) < minimum:
|
|
132
|
+
d = minimum
|
|
133
|
+
c = 1.0 + coefficient / c
|
|
134
|
+
if abs(c) < minimum:
|
|
135
|
+
c = minimum
|
|
136
|
+
d = 1.0 / d
|
|
137
|
+
h *= d * c
|
|
138
|
+
|
|
139
|
+
coefficient = -(a + iteration) * (qab + iteration) * x / (
|
|
140
|
+
(a + even) * (qap + even)
|
|
141
|
+
)
|
|
142
|
+
d = 1.0 + coefficient * d
|
|
143
|
+
if abs(d) < minimum:
|
|
144
|
+
d = minimum
|
|
145
|
+
c = 1.0 + coefficient / c
|
|
146
|
+
if abs(c) < minimum:
|
|
147
|
+
c = minimum
|
|
148
|
+
d = 1.0 / d
|
|
149
|
+
delta = d * c
|
|
150
|
+
h *= delta
|
|
151
|
+
if abs(delta - 1.0) < epsilon:
|
|
152
|
+
return h
|
|
153
|
+
raise ValueError("beta continued fraction did not converge")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def regularized_beta(a: float, b: float, x: float) -> float:
|
|
157
|
+
if a <= 0.0 or b <= 0.0 or not 0.0 <= x <= 1.0:
|
|
158
|
+
raise ValueError("invalid beta arguments")
|
|
159
|
+
if x == 0.0:
|
|
160
|
+
return 0.0
|
|
161
|
+
if x == 1.0:
|
|
162
|
+
return 1.0
|
|
163
|
+
front = math.exp(
|
|
164
|
+
math.lgamma(a + b) - math.lgamma(a) - math.lgamma(b)
|
|
165
|
+
+ a * math.log(x) + b * math.log(1.0 - x)
|
|
166
|
+
)
|
|
167
|
+
if x < (a + 1.0) / (a + b + 2.0):
|
|
168
|
+
return front * beta_continued_fraction(a, b, x) / a
|
|
169
|
+
return 1.0 - front * beta_continued_fraction(b, a, 1.0 - x) / b
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def student_t_two_sided_p(t_value: float, degrees_freedom: float) -> float:
|
|
173
|
+
x = degrees_freedom / (degrees_freedom + t_value * t_value)
|
|
174
|
+
return regularized_beta(degrees_freedom / 2.0, 0.5, x)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def student_t_critical(alpha: float, degrees_freedom: float) -> float:
|
|
178
|
+
low = 0.0
|
|
179
|
+
high = 1.0
|
|
180
|
+
while student_t_two_sided_p(high, degrees_freedom) > alpha:
|
|
181
|
+
high *= 2.0
|
|
182
|
+
if high > 1e6:
|
|
183
|
+
raise ValueError("could not bracket t critical value")
|
|
184
|
+
for _ in range(100):
|
|
185
|
+
middle = (low + high) / 2.0
|
|
186
|
+
if student_t_two_sided_p(middle, degrees_freedom) > alpha:
|
|
187
|
+
low = middle
|
|
188
|
+
else:
|
|
189
|
+
high = middle
|
|
190
|
+
return (low + high) / 2.0
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def validate_groups(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
194
|
+
groups = params.get("groups")
|
|
195
|
+
if not isinstance(groups, list) or len(groups) < 2:
|
|
196
|
+
raise ValueError("groups must contain at least two groups")
|
|
197
|
+
names: set[str] = set()
|
|
198
|
+
for group in groups:
|
|
199
|
+
if not isinstance(group, dict):
|
|
200
|
+
raise ValueError("every group must be an object")
|
|
201
|
+
name = group.get("name")
|
|
202
|
+
if not isinstance(name, str) or not name:
|
|
203
|
+
raise ValueError("every group must have a non-empty name")
|
|
204
|
+
if name in names:
|
|
205
|
+
raise ValueError(f"duplicate group name: {name}")
|
|
206
|
+
names.add(name)
|
|
207
|
+
return groups
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def calculate_srm(
|
|
211
|
+
groups: list[dict[str, Any]],
|
|
212
|
+
expected_allocations: dict[str, Any],
|
|
213
|
+
alpha_srm: float,
|
|
214
|
+
) -> dict[str, Any]:
|
|
215
|
+
names = [group["name"] for group in groups]
|
|
216
|
+
if set(expected_allocations) != set(names):
|
|
217
|
+
raise ValueError("expected_allocations keys must exactly match group names")
|
|
218
|
+
allocations = {name: float(expected_allocations[name]) for name in names}
|
|
219
|
+
if any(value <= 0.0 for value in allocations.values()):
|
|
220
|
+
raise ValueError("expected allocations must be positive")
|
|
221
|
+
if not math.isclose(sum(allocations.values()), 1.0, rel_tol=0.0, abs_tol=1e-9):
|
|
222
|
+
raise ValueError("expected allocations must sum to 1.0")
|
|
223
|
+
|
|
224
|
+
observed: dict[str, int] = {}
|
|
225
|
+
for group in groups:
|
|
226
|
+
assigned = group.get("assigned")
|
|
227
|
+
if isinstance(assigned, bool) or not isinstance(assigned, int) or assigned < 0:
|
|
228
|
+
raise ValueError("every group must have a non-negative integer assigned count")
|
|
229
|
+
observed[group["name"]] = assigned
|
|
230
|
+
total = sum(observed.values())
|
|
231
|
+
if total == 0:
|
|
232
|
+
raise ValueError("total assigned count must be greater than 0")
|
|
233
|
+
|
|
234
|
+
expected = {name: total * allocations[name] for name in names}
|
|
235
|
+
chi_square = sum(
|
|
236
|
+
(observed[name] - expected[name]) ** 2 / expected[name] for name in names
|
|
237
|
+
)
|
|
238
|
+
degrees_freedom = len(names) - 1
|
|
239
|
+
p_value = regularized_gamma_q(degrees_freedom / 2.0, chi_square / 2.0)
|
|
240
|
+
return {
|
|
241
|
+
"chi_square": chi_square,
|
|
242
|
+
"degrees_freedom": degrees_freedom,
|
|
243
|
+
"p_value": p_value,
|
|
244
|
+
"alpha": alpha_srm,
|
|
245
|
+
"srm_detected": p_value < alpha_srm,
|
|
246
|
+
"observed": observed,
|
|
247
|
+
"expected": expected,
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def binary_comparison(
|
|
252
|
+
control: dict[str, Any],
|
|
253
|
+
treatment: dict[str, Any],
|
|
254
|
+
alpha: float,
|
|
255
|
+
) -> dict[str, Any]:
|
|
256
|
+
n0 = int(control.get("sample_size", 0))
|
|
257
|
+
n1 = int(treatment.get("sample_size", 0))
|
|
258
|
+
x0 = int(control.get("successes", -1))
|
|
259
|
+
x1 = int(treatment.get("successes", -1))
|
|
260
|
+
if n0 <= 0 or n1 <= 0:
|
|
261
|
+
raise ValueError("binary sample_size must be greater than 0")
|
|
262
|
+
if not 0 <= x0 <= n0 or not 0 <= x1 <= n1:
|
|
263
|
+
raise ValueError("binary successes must be between 0 and sample_size")
|
|
264
|
+
p0 = x0 / n0
|
|
265
|
+
p1 = x1 / n1
|
|
266
|
+
difference = p1 - p0
|
|
267
|
+
pooled = (x0 + x1) / (n0 + n1)
|
|
268
|
+
pooled_se = math.sqrt(pooled * (1.0 - pooled) * (1.0 / n0 + 1.0 / n1))
|
|
269
|
+
if pooled_se == 0.0:
|
|
270
|
+
z_value = 0.0 if difference == 0.0 else math.copysign(math.inf, difference)
|
|
271
|
+
p_value = 1.0 if difference == 0.0 else 0.0
|
|
272
|
+
else:
|
|
273
|
+
z_value = difference / pooled_se
|
|
274
|
+
p_value = math.erfc(abs(z_value) / math.sqrt(2.0))
|
|
275
|
+
interval_se = math.sqrt(
|
|
276
|
+
p0 * (1.0 - p0) / n0 + p1 * (1.0 - p1) / n1
|
|
277
|
+
)
|
|
278
|
+
critical = normal_ppf(1.0 - alpha / 2.0)
|
|
279
|
+
relative_lift = None if p0 == 0.0 else difference / p0
|
|
280
|
+
return {
|
|
281
|
+
"control_value": p0,
|
|
282
|
+
"treatment_value": p1,
|
|
283
|
+
"absolute_difference": difference,
|
|
284
|
+
"relative_lift": relative_lift,
|
|
285
|
+
"test": "two_sided_pooled_two_proportion_z",
|
|
286
|
+
"statistic": z_value,
|
|
287
|
+
"p_value": p_value,
|
|
288
|
+
"confidence_interval_absolute": [
|
|
289
|
+
difference - critical * interval_se,
|
|
290
|
+
difference + critical * interval_se,
|
|
291
|
+
],
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def continuous_comparison(
|
|
296
|
+
control: dict[str, Any],
|
|
297
|
+
treatment: dict[str, Any],
|
|
298
|
+
alpha: float,
|
|
299
|
+
) -> dict[str, Any]:
|
|
300
|
+
n0 = int(control.get("sample_size", 0))
|
|
301
|
+
n1 = int(treatment.get("sample_size", 0))
|
|
302
|
+
mean0 = float(control.get("mean"))
|
|
303
|
+
mean1 = float(treatment.get("mean"))
|
|
304
|
+
sd0 = float(control.get("standard_deviation"))
|
|
305
|
+
sd1 = float(treatment.get("standard_deviation"))
|
|
306
|
+
if n0 < 2 or n1 < 2:
|
|
307
|
+
raise ValueError("continuous sample_size must be at least 2")
|
|
308
|
+
if sd0 < 0.0 or sd1 < 0.0:
|
|
309
|
+
raise ValueError("standard_deviation must be non-negative")
|
|
310
|
+
variance0 = sd0 * sd0 / n0
|
|
311
|
+
variance1 = sd1 * sd1 / n1
|
|
312
|
+
standard_error = math.sqrt(variance0 + variance1)
|
|
313
|
+
difference = mean1 - mean0
|
|
314
|
+
if standard_error == 0.0:
|
|
315
|
+
t_value = 0.0 if difference == 0.0 else math.copysign(math.inf, difference)
|
|
316
|
+
p_value = 1.0 if difference == 0.0 else 0.0
|
|
317
|
+
degrees_freedom = math.inf
|
|
318
|
+
critical = normal_ppf(1.0 - alpha / 2.0)
|
|
319
|
+
else:
|
|
320
|
+
t_value = difference / standard_error
|
|
321
|
+
denominator = (
|
|
322
|
+
variance0 * variance0 / (n0 - 1)
|
|
323
|
+
+ variance1 * variance1 / (n1 - 1)
|
|
324
|
+
)
|
|
325
|
+
degrees_freedom = (
|
|
326
|
+
math.inf if denominator == 0.0 else (variance0 + variance1) ** 2 / denominator
|
|
327
|
+
)
|
|
328
|
+
if math.isinf(degrees_freedom):
|
|
329
|
+
p_value = math.erfc(abs(t_value) / math.sqrt(2.0))
|
|
330
|
+
critical = normal_ppf(1.0 - alpha / 2.0)
|
|
331
|
+
else:
|
|
332
|
+
p_value = student_t_two_sided_p(t_value, degrees_freedom)
|
|
333
|
+
critical = student_t_critical(alpha, degrees_freedom)
|
|
334
|
+
relative_lift = None if mean0 == 0.0 else difference / mean0
|
|
335
|
+
return {
|
|
336
|
+
"control_value": mean0,
|
|
337
|
+
"treatment_value": mean1,
|
|
338
|
+
"absolute_difference": difference,
|
|
339
|
+
"relative_lift": relative_lift,
|
|
340
|
+
"test": "two_sided_welch_t",
|
|
341
|
+
"statistic": t_value,
|
|
342
|
+
"degrees_freedom": degrees_freedom,
|
|
343
|
+
"p_value": p_value,
|
|
344
|
+
"confidence_interval_absolute": [
|
|
345
|
+
difference - critical * standard_error,
|
|
346
|
+
difference + critical * standard_error,
|
|
347
|
+
],
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def calculate(params: dict[str, Any]) -> dict[str, Any]:
|
|
352
|
+
groups = validate_groups(params)
|
|
353
|
+
metric_type = params.get("metric_type")
|
|
354
|
+
if metric_type not in {"binary", "continuous"}:
|
|
355
|
+
raise ValueError("metric_type must be binary or continuous")
|
|
356
|
+
control_name = params.get("control")
|
|
357
|
+
by_name = {group["name"]: group for group in groups}
|
|
358
|
+
if control_name not in by_name:
|
|
359
|
+
raise ValueError("control must match one group name")
|
|
360
|
+
|
|
361
|
+
alpha = float(params.get("alpha", 0.05))
|
|
362
|
+
alpha_srm = float(params.get("alpha_srm", 0.01))
|
|
363
|
+
if not 0.0 < alpha < 0.5 or not 0.0 < alpha_srm < 0.5:
|
|
364
|
+
raise ValueError("alpha and alpha_srm must be between 0 and 0.5")
|
|
365
|
+
multiple_testing = params.get("multiple_testing", "bonferroni")
|
|
366
|
+
if multiple_testing not in {"bonferroni", "none"}:
|
|
367
|
+
raise ValueError("multiple_testing must be bonferroni or none")
|
|
368
|
+
|
|
369
|
+
srm = None
|
|
370
|
+
expected_allocations = params.get("expected_allocations")
|
|
371
|
+
if expected_allocations is not None:
|
|
372
|
+
if not isinstance(expected_allocations, dict):
|
|
373
|
+
raise ValueError("expected_allocations must be an object")
|
|
374
|
+
srm = calculate_srm(groups, expected_allocations, alpha_srm)
|
|
375
|
+
|
|
376
|
+
control = by_name[control_name]
|
|
377
|
+
comparisons: list[dict[str, Any]] = []
|
|
378
|
+
number_of_comparisons = len(groups) - 1
|
|
379
|
+
comparison_alpha = (
|
|
380
|
+
alpha / number_of_comparisons
|
|
381
|
+
if multiple_testing == "bonferroni"
|
|
382
|
+
else alpha
|
|
383
|
+
)
|
|
384
|
+
for treatment in groups:
|
|
385
|
+
if treatment["name"] == control_name:
|
|
386
|
+
continue
|
|
387
|
+
result = (
|
|
388
|
+
binary_comparison(control, treatment, comparison_alpha)
|
|
389
|
+
if metric_type == "binary"
|
|
390
|
+
else continuous_comparison(control, treatment, comparison_alpha)
|
|
391
|
+
)
|
|
392
|
+
difference = result["absolute_difference"]
|
|
393
|
+
significant = result["p_value"] < comparison_alpha
|
|
394
|
+
if significant and difference > 0:
|
|
395
|
+
classification = "statistically_significant_positive"
|
|
396
|
+
elif significant and difference < 0:
|
|
397
|
+
classification = "statistically_significant_negative"
|
|
398
|
+
else:
|
|
399
|
+
classification = "inconclusive_at_selected_alpha"
|
|
400
|
+
result.update(
|
|
401
|
+
{
|
|
402
|
+
"control": control_name,
|
|
403
|
+
"treatment": treatment["name"],
|
|
404
|
+
"comparison_alpha": comparison_alpha,
|
|
405
|
+
"statistically_significant": significant,
|
|
406
|
+
"classification": classification,
|
|
407
|
+
}
|
|
408
|
+
)
|
|
409
|
+
comparisons.append(result)
|
|
410
|
+
|
|
411
|
+
exposure_coverage: dict[str, float] = {}
|
|
412
|
+
for group in groups:
|
|
413
|
+
assigned = group.get("assigned")
|
|
414
|
+
sample_size = group.get("sample_size")
|
|
415
|
+
if (
|
|
416
|
+
isinstance(assigned, int)
|
|
417
|
+
and assigned > 0
|
|
418
|
+
and isinstance(sample_size, int)
|
|
419
|
+
and sample_size >= 0
|
|
420
|
+
):
|
|
421
|
+
exposure_coverage[group["name"]] = sample_size / assigned
|
|
422
|
+
|
|
423
|
+
warnings: list[str] = []
|
|
424
|
+
if len(comparisons) > 1 and multiple_testing == "none":
|
|
425
|
+
warnings.append(
|
|
426
|
+
"P-values are unadjusted across treatments; confirm that this matches the pre-registered policy."
|
|
427
|
+
)
|
|
428
|
+
if srm and srm["srm_detected"]:
|
|
429
|
+
warnings.append(
|
|
430
|
+
"SRM was detected from assignment counts; diagnose allocation before using causal comparisons."
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
"ok": True,
|
|
435
|
+
"data": {
|
|
436
|
+
"metric_type": metric_type,
|
|
437
|
+
"control": control_name,
|
|
438
|
+
"familywise_alpha": alpha,
|
|
439
|
+
"multiple_testing": multiple_testing,
|
|
440
|
+
"comparison_alpha": comparison_alpha,
|
|
441
|
+
"srm": srm,
|
|
442
|
+
"exposure_coverage": exposure_coverage,
|
|
443
|
+
"comparisons": comparisons,
|
|
444
|
+
"warnings": warnings,
|
|
445
|
+
},
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def main() -> int:
|
|
450
|
+
try:
|
|
451
|
+
if len(sys.argv) != 2:
|
|
452
|
+
raise ValueError("usage: analyze_experiment.py '<json-object>'")
|
|
453
|
+
raw = sys.argv[1]
|
|
454
|
+
params = json.load(sys.stdin) if raw == "-" else json.loads(raw)
|
|
455
|
+
if not isinstance(params, dict):
|
|
456
|
+
raise ValueError("input must be a JSON object")
|
|
457
|
+
print(json.dumps(calculate(params), ensure_ascii=False, indent=2, allow_nan=False))
|
|
458
|
+
return 0
|
|
459
|
+
except (ValueError, TypeError, KeyError, json.JSONDecodeError) as error:
|
|
460
|
+
print(
|
|
461
|
+
json.dumps(
|
|
462
|
+
{
|
|
463
|
+
"ok": False,
|
|
464
|
+
"error": {
|
|
465
|
+
"type": "validation",
|
|
466
|
+
"code": "INVALID_INPUT",
|
|
467
|
+
"message": str(error),
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
ensure_ascii=False,
|
|
471
|
+
indent=2,
|
|
472
|
+
)
|
|
473
|
+
)
|
|
474
|
+
return 2
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
if __name__ == "__main__":
|
|
478
|
+
raise SystemExit(main())
|
|
@@ -91,12 +91,12 @@ First, check if `.ae-cli/draft.json` exists and read existing configuration:
|
|
|
91
91
|
**⚠️ Key: SERVER_URL and APP_ID are independent config items. Even if draft.json has project_id and host, you MUST resolve and confirm them. `project_id` ≠ `APP_ID`, `host` ≠ `SERVER_URL`.**
|
|
92
92
|
|
|
93
93
|
1. **APP_ID** — Prefer automatic lookup before asking:
|
|
94
|
-
- If `meta.project_id` is known, run `ae-cli
|
|
94
|
+
- If `meta.project_id` is known, run `ae-cli project info list` once for the current host.
|
|
95
95
|
- Find the project whose `projectId` matches `meta.project_id`.
|
|
96
96
|
- If the matched project has `appId`, ask: **"I found APP_ID `<appId>` for project `<projectId>`. Use it? yes / enter new value"**
|
|
97
97
|
- If the project is missing, ambiguous, or has no `appId`, ask the user to copy APP_ID from AE Admin → "Project Settings" → "Integration Config".
|
|
98
98
|
2. **SERVER_URL** — Data ingestion endpoint (**different from web URL**; go to AE Admin → "Project Settings" → "Integration Config" → fill in "Public URL")
|
|
99
|
-
- If `meta.project_id` is known, you may try `ae-cli
|
|
99
|
+
- If `meta.project_id` is known, you may try `ae-cli project info get --project-id <project_id>` once.
|
|
100
100
|
- Use the returned value only if the response explicitly contains a receiver URL field such as `serverUrl`, `pushUrl`, `push_url`, `receiverUrl`, `publicUrl`, `publicReceiverAddress`, `privateReceiverAddress`, or equivalent ingestion endpoint field.
|
|
101
101
|
- If both `publicReceiverAddress` and `privateReceiverAddress` are present, prefer `publicReceiverAddress` as `SERVER_URL` for generated snippets unless the user explicitly needs an internal/private-network receiver.
|
|
102
102
|
- If a value is found, ask: **"I found SERVER_URL `<url>` for project `<projectId>`. Use it? yes / enter new value"**
|
|
@@ -105,7 +105,9 @@ First, check if `.ae-cli/draft.json` exists and read existing configuration:
|
|
|
105
105
|
|
|
106
106
|
**host handling** (optional):
|
|
107
107
|
- If you need to fetch plan from AE (no local draft.json) → ask for host
|
|
108
|
-
- If local draft.json exists → host is not required
|
|
108
|
+
- If local draft.json exists → host is not required for code generation
|
|
109
|
+
- Before Debug validation, run `ae-cli config current` and confirm the active host matches the target AE environment; if it does not, run `ae-cli config set-host <AE_HOST>`
|
|
110
|
+
- Complete Debug validation with `ae-cli tracking debug-device` and `ae-cli tracking debug-data` by default; only hint the user to open the AE Debug page when those CLI capabilities are unavailable
|
|
109
111
|
|
|
110
112
|
### If only xlsx file exists (no draft.json)
|
|
111
113
|
|
|
@@ -605,16 +607,30 @@ Provide validation guidance based on selected platforms:
|
|
|
605
607
|
|
|
606
608
|
```
|
|
607
609
|
Validation steps:
|
|
608
|
-
1.
|
|
610
|
+
1. Confirm the active AE environment:
|
|
611
|
+
ae-cli config current
|
|
612
|
+
|
|
613
|
+
2. List existing Debug devices:
|
|
614
|
+
ae-cli tracking debug-device list --project-id <project_id>
|
|
615
|
+
|
|
616
|
+
3. Create the script's stable device ID if it is missing, then select it:
|
|
617
|
+
ae-cli tracking debug-device add --project-id <project_id> --device-id <device_id> --device-name <name>
|
|
618
|
+
ae-cli tracking debug-device select --project-id <project_id> --device-id <device_id>
|
|
619
|
+
|
|
620
|
+
4. Run validation script:
|
|
609
621
|
- Client: run .ae-cli/output/te-debug-client.<ext>
|
|
610
622
|
- Server: run .ae-cli/output/te-debug-server.<ext>
|
|
611
623
|
|
|
612
|
-
|
|
613
|
-
|
|
624
|
+
5. Query the most recent hour of Debug data:
|
|
625
|
+
ae-cli tracking debug-data list --project-id <project_id> --device-id <device_id>
|
|
626
|
+
|
|
627
|
+
6. Confirm has_data=true, then inspect event names, property structures, and error fields.
|
|
628
|
+
If needed, add --event-name <event_name> or --start-time "YYYY-MM-DD HH:mm:ss".
|
|
614
629
|
|
|
615
|
-
|
|
630
|
+
7. Only if the CLI capability is unavailable, open the AE Debug page:
|
|
631
|
+
https://<host>/#/data/debug
|
|
616
632
|
|
|
617
|
-
|
|
633
|
+
8. For LogBus2:
|
|
618
634
|
- Copy daemon.json to LogBus2 conf/ directory
|
|
619
635
|
- Start: ./logbus start
|
|
620
636
|
- Official docs: https://docs-v2.thinkingdata.cn/?version=latest&code=logbus2_installation&lan=en-US
|