maxc-cli 0.8.2__tar.gz → 0.9.0__tar.gz

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 (139) hide show
  1. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/PKG-INFO +14 -2
  2. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/README.md +13 -1
  3. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/__init__.py +1 -1
  4. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/_samples.py +8 -0
  5. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/app.py +68 -0
  6. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/meta.py +94 -0
  7. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/cli.py +38 -0
  8. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/exceptions.py +14 -0
  9. maxc_cli-0.9.0/src/maxc_cli/function_metadata.py +36 -0
  10. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/helpers.py +17 -1
  11. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/models.py +4 -0
  12. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/SKILL.md +14 -2
  13. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/command-patterns.md +7 -0
  14. maxc_cli-0.9.0/src/maxc_cli/skills/references/registered-functions.md +78 -0
  15. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/PKG-INFO +14 -2
  16. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/SOURCES.txt +3 -0
  17. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_help_version_e2e.py +8 -0
  18. maxc_cli-0.9.0/tests/test_registered_functions.py +207 -0
  19. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/MANIFEST.in +0 -0
  20. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/pyproject.toml +0 -0
  21. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/scripts/pyinstaller_entry.py +0 -0
  22. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/scripts/regression_test.py +0 -0
  23. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/setup.cfg +0 -0
  24. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/setup.py +0 -0
  25. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/__main__.py +0 -0
  26. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/agent_platforms.py +0 -0
  27. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/audit.py +0 -0
  28. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/auth_continuation.py +0 -0
  29. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/auth_providers.py +0 -0
  30. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/__init__.py +0 -0
  31. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/auth.py +0 -0
  32. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/catalog.py +0 -0
  33. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/data.py +0 -0
  34. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/job.py +0 -0
  35. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/mcp.py +0 -0
  36. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/odps.py +0 -0
  37. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/query.py +0 -0
  38. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/semantic.py +0 -0
  39. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/cache.py +0 -0
  40. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/catalog_bootstrap.py +0 -0
  41. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/config.py +0 -0
  42. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/enterprise_tls.py +0 -0
  43. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/help_format.py +0 -0
  44. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/job_ids.py +0 -0
  45. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/masking.py +0 -0
  46. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/mcp_serve.py +0 -0
  47. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/oauth.py +0 -0
  48. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/odps_runtime.py +0 -0
  49. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/output.py +0 -0
  50. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/proxy_auth.py +0 -0
  51. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/semantic.py +0 -0
  52. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/semantic_management.py +0 -0
  53. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/setting_parser.py +0 -0
  54. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/agents/openai.yaml +0 -0
  55. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -0
  56. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/bootstrap-flow.md +0 -0
  57. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/json-output-format.md +0 -0
  58. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/maxcompute-select-guide.md +0 -0
  59. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +0 -0
  60. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/partition-guide.md +0 -0
  61. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/red-lines.md +0 -0
  62. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/semantic-packages.md +0 -0
  63. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/setup-install.md +0 -0
  64. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/sql-common-errors.md +0 -0
  65. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/sql-query-patterns.md +0 -0
  66. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/text2sql-principles.md +0 -0
  67. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/state_permissions.py +0 -0
  68. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/store.py +0 -0
  69. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/utils.py +0 -0
  70. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
  71. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/entry_points.txt +0 -0
  72. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/requires.txt +0 -0
  73. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/top_level.txt +0 -0
  74. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_hints_and_cli.py +0 -0
  75. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_platforms.py +0 -0
  76. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_skill_commands.py +0 -0
  77. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_skill_commands_context.py +0 -0
  78. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_ai_native_contract_regressions.py +0 -0
  79. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_auth_logout.py +0 -0
  80. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_auth.py +0 -0
  81. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_data.py +0 -0
  82. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_data_serialization.py +0 -0
  83. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_mcp.py +0 -0
  84. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_meta.py +0 -0
  85. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_build_release_archive_compat.py +0 -0
  86. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_build_release_script.py +0 -0
  87. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cache.py +0 -0
  88. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_catalog.py +0 -0
  89. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_catalog_bootstrap.py +0 -0
  90. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cli_arg_validation.py +0 -0
  91. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cli_mock.py +0 -0
  92. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cli_query_parse_and_sanitize.py +0 -0
  93. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_compat.py +0 -0
  94. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_config_atomic_write.py +0 -0
  95. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_connection_bootstrap.py +0 -0
  96. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_e2e_smoke.py +0 -0
  97. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_effective_hints_contract.py +0 -0
  98. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_enterprise_tls.py +0 -0
  99. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_envelope_shape.py +0 -0
  100. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_error_self_correction.py +0 -0
  101. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_error_translation.py +0 -0
  102. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_exit_codes.py +0 -0
  103. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_external_auth.py +0 -0
  104. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_flag_hoist.py +0 -0
  105. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_help_format.py +0 -0
  106. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_helpers.py +0 -0
  107. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_helpers_csv.py +0 -0
  108. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_installer_contracts.py +0 -0
  109. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_integration.py +0 -0
  110. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_integration_real.py +0 -0
  111. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_job_improvements.py +0 -0
  112. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_job_inspection.py +0 -0
  113. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_job_store_durability.py +0 -0
  114. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_kb_commands.py +0 -0
  115. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_manifest_runtime_contract.py +0 -0
  116. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_masking.py +0 -0
  117. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_mcp_serve.py +0 -0
  118. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_meta_schema_and_partition_cols.py +0 -0
  119. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_oauth.py +0 -0
  120. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_odps_runtime.py +0 -0
  121. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_output_action_safety.py +0 -0
  122. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_output_format_contract.py +0 -0
  123. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_packaging_metadata.py +0 -0
  124. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_phase1_improvements.py +0 -0
  125. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_proxy_auth.py +0 -0
  126. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_pyinstaller_bundle.py +0 -0
  127. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_python39_compat.py +0 -0
  128. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_query_auto_promote.py +0 -0
  129. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_query_result_csv_fallback.py +0 -0
  130. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_semantic_management.py +0 -0
  131. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_semantic_scope.py +0 -0
  132. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_semantic_transport.py +0 -0
  133. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_setting_parser.py +0 -0
  134. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_skill_cli_consistency.py +0 -0
  135. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_skill_eval.py +0 -0
  136. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_skill_renderer.py +0 -0
  137. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_startup_imports.py +0 -0
  138. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_state_permissions.py +0 -0
  139. {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_state_portability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxc-cli
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: Agent-native MaxCompute CLI for external coding agents
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.9
@@ -70,7 +70,7 @@ maxc agent doctor --online --json
70
70
  |------|------|------|
71
71
  | **query** | `query [run]`, `query cost`, `query explain` | SQL 执行、成本估算、执行计划 |
72
72
  | **job** | `submit`, `status`, `wait`, `result`, `cancel`, `diagnose`, `list` | 异步任务全生命周期 |
73
- | **meta** | `list-tables`, `describe`, `search`, `search-columns`, `partitions`, `latest-partition`, `freshness`, `list-projects`, `list-schemas`, `semantic set/get/clear/list-missing` | 元数据发现与语义管理 |
73
+ | **meta** | `list-tables`, `describe`, `list-functions`, `describe-function`, `search`, `search-columns`, `partitions`, `latest-partition`, `freshness`, `list-projects`, `list-schemas`, `semantic set/get/clear/list-missing` | 元数据发现与语义管理 |
74
74
  | **data** | `sample`, `profile`, `upload`, `download` | 数据采样、画像与 CSV/TSV 传输 |
75
75
  | **auth** | `login`, `login-external`, `logout`, `whoami`, `can-i` | 认证与权限 |
76
76
  | **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
@@ -81,6 +81,18 @@ maxc agent doctor --online --json
81
81
  使用 `--json`;CSV/NDJSON 行流和 `job wait --stream` 生命周期流是明确例外,
82
82
  不会为每条记录重复封装 Envelope。
83
83
 
84
+ 0.9.0 起支持已注册函数的目录与详情查询:
85
+
86
+ ```bash
87
+ aliyun maxc meta list-functions --project <project> --prefix <prefix> --limit 20 --json
88
+ aliyun maxc meta describe-function <alias> --project <project> --json
89
+ ```
90
+
91
+ 三层项目可加 `--schema`;详情参数使用裸函数别名。列表有界分页,详情保留
92
+ 类名和原始资源引用,不下载资源内容,也不推断签名或 Python 版本。
93
+ 按作者文档确定调用契约后,复用现有 SQL 成本、执行和作业命令引用函数。
94
+ 具体流程见 [已注册函数](src/maxc_cli/skills/references/registered-functions.md)。
95
+
84
96
  ## Agent 集成
85
97
 
86
98
  ### 方式 1:公共 Skill(主路径)
@@ -48,7 +48,7 @@ maxc agent doctor --online --json
48
48
  |------|------|------|
49
49
  | **query** | `query [run]`, `query cost`, `query explain` | SQL 执行、成本估算、执行计划 |
50
50
  | **job** | `submit`, `status`, `wait`, `result`, `cancel`, `diagnose`, `list` | 异步任务全生命周期 |
51
- | **meta** | `list-tables`, `describe`, `search`, `search-columns`, `partitions`, `latest-partition`, `freshness`, `list-projects`, `list-schemas`, `semantic set/get/clear/list-missing` | 元数据发现与语义管理 |
51
+ | **meta** | `list-tables`, `describe`, `list-functions`, `describe-function`, `search`, `search-columns`, `partitions`, `latest-partition`, `freshness`, `list-projects`, `list-schemas`, `semantic set/get/clear/list-missing` | 元数据发现与语义管理 |
52
52
  | **data** | `sample`, `profile`, `upload`, `download` | 数据采样、画像与 CSV/TSV 传输 |
53
53
  | **auth** | `login`, `login-external`, `logout`, `whoami`, `can-i` | 认证与权限 |
54
54
  | **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
@@ -59,6 +59,18 @@ maxc agent doctor --online --json
59
59
  使用 `--json`;CSV/NDJSON 行流和 `job wait --stream` 生命周期流是明确例外,
60
60
  不会为每条记录重复封装 Envelope。
61
61
 
62
+ 0.9.0 起支持已注册函数的目录与详情查询:
63
+
64
+ ```bash
65
+ aliyun maxc meta list-functions --project <project> --prefix <prefix> --limit 20 --json
66
+ aliyun maxc meta describe-function <alias> --project <project> --json
67
+ ```
68
+
69
+ 三层项目可加 `--schema`;详情参数使用裸函数别名。列表有界分页,详情保留
70
+ 类名和原始资源引用,不下载资源内容,也不推断签名或 Python 版本。
71
+ 按作者文档确定调用契约后,复用现有 SQL 成本、执行和作业命令引用函数。
72
+ 具体流程见 [已注册函数](src/maxc_cli/skills/references/registered-functions.md)。
73
+
62
74
  ## Agent 集成
63
75
 
64
76
  ### 方式 1:公共 Skill(主路径)
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.8.2"
5
+ __version__ = "0.9.0"
@@ -85,6 +85,14 @@ SAMPLES: dict[str, str] = {
85
85
  "maxc meta describe default.orders --project my_proj\n"
86
86
  "maxc meta describe default.orders --project my_proj --full --json"
87
87
  ),
88
+ "meta.list-functions": (
89
+ "maxc meta list-functions --project my_proj --prefix normalize_ --limit 20 --json\n"
90
+ "maxc meta list-functions --project my_proj --schema default --limit 20 --json"
91
+ ),
92
+ "meta.describe-function": (
93
+ "maxc meta describe-function normalize_phone --project my_proj --json\n"
94
+ "maxc meta describe-function normalize_phone --project my_proj --schema default --json"
95
+ ),
88
96
  "meta.search": (
89
97
  "maxc meta search orders\n"
90
98
  "maxc meta search user --project my_proj --json"
@@ -44,6 +44,7 @@ from .exceptions import (
44
44
  TwoTierNamespaceError,
45
45
  ValidationError,
46
46
  )
47
+ from .function_metadata import function_cursor, function_offset
47
48
  from .helpers import (
48
49
  build_odps_identity_payload,
49
50
  build_task_summary,
@@ -2023,6 +2024,73 @@ class MaxCApp:
2023
2024
  self.log("job.list", envelope.status, envelope.metadata)
2024
2025
  return envelope
2025
2026
 
2027
+ def meta_list_functions(
2028
+ self,
2029
+ *,
2030
+ schema: 'str | None' = None,
2031
+ project: 'str | None' = None,
2032
+ prefix: 'str | None' = None,
2033
+ limit: int = 50,
2034
+ cursor: 'str | None' = None,
2035
+ ) -> Envelope:
2036
+ if type(limit) is not int or not 1 <= limit <= 1000:
2037
+ raise ValidationError("Function list limit must be between 1 and 1000.")
2038
+ target_project = project or self.config.default_project
2039
+ effective_schema = schema or self.config.default_schema
2040
+ offset = function_offset(cursor, project=target_project, schema=effective_schema, prefix=prefix)
2041
+ rows, has_more = self.backend.list_functions(
2042
+ project=target_project, schema=effective_schema, prefix=prefix, limit=limit, offset=offset,
2043
+ )
2044
+ data = {
2045
+ "functions": [dict(row, project=target_project, schema_name=effective_schema) for row in rows],
2046
+ "pagination": {
2047
+ "returned_count": len(rows),
2048
+ "has_more": has_more,
2049
+ "limit": limit,
2050
+ "offset": offset,
2051
+ "next_cursor": function_cursor(
2052
+ offset + len(rows), project=target_project, schema=effective_schema, prefix=prefix,
2053
+ ) if has_more else None,
2054
+ },
2055
+ }
2056
+ metadata = {"project": target_project, "schema": effective_schema, "prefix": prefix}
2057
+ envelope = Envelope(
2058
+ command="meta.list-functions", status="success", data=data, metadata=metadata,
2059
+ agent_hints=AgentHints(warnings=[
2060
+ "Catalog pages are live and not a snapshot; registrations can change between calls.",
2061
+ "Function aliases are metadata names, not SQL-qualified references. Verify namespace settings before constructing SQL.",
2062
+ ]),
2063
+ )
2064
+ self.log(envelope.command, envelope.status, metadata)
2065
+ return envelope
2066
+
2067
+ def meta_describe_function(
2068
+ self,
2069
+ function_name: str,
2070
+ *,
2071
+ schema: 'str | None' = None,
2072
+ project: 'str | None' = None,
2073
+ ) -> Envelope:
2074
+ if not function_name or any(char.isspace() or char in ".:/\\" for char in function_name):
2075
+ raise ValidationError(
2076
+ "Pass a bare registered function alias.",
2077
+ suggestion="Select the function's project and schema with --project and --schema.",
2078
+ )
2079
+ target_project = project or self.config.default_project
2080
+ effective_schema = schema or self.config.default_schema
2081
+ detail = self.backend.describe_function(function_name, project=target_project, schema=effective_schema)
2082
+ detail = dict(detail, project=target_project, schema_name=effective_schema)
2083
+ metadata = {"project": target_project, "schema": effective_schema}
2084
+ envelope = Envelope(
2085
+ command="meta.describe-function", status="success", data={"function": detail}, metadata=metadata,
2086
+ agent_hints=AgentHints(warnings=[
2087
+ "Registration metadata does not establish the input/output signature or runtime version. Use the function owner's documentation or verified user context; do not infer them from class or resource names.",
2088
+ "Metadata Read and SQL Execute are separate permissions. A failed describe is not proof that an already-known function cannot be executed.",
2089
+ ]),
2090
+ )
2091
+ self.log(envelope.command, envelope.status, metadata)
2092
+ return envelope
2093
+
2026
2094
  def meta_list_tables(
2027
2095
  self,
2028
2096
  *,
@@ -20,6 +20,100 @@ from ..helpers import (
20
20
  class MetaMixin:
21
21
  """Mixin providing metadata methods."""
22
22
 
23
+ def list_functions(
24
+ self,
25
+ *,
26
+ project: 'str | None' = None,
27
+ schema: 'str | None' = None,
28
+ prefix: 'str | None' = None,
29
+ limit: int = 50,
30
+ offset: int = 0,
31
+ ) -> 'tuple[list[dict[str, Any]], bool]':
32
+ """Read a bounded catalog window without per-function metadata GETs.
33
+
34
+ Args:
35
+ project: Function project, or the configured project.
36
+ schema: Explicit function schema, when applicable.
37
+ prefix: Server-side alias prefix filter.
38
+ limit: Maximum returned functions.
39
+ offset: Number of earlier matching catalog entries to skip.
40
+
41
+ Returns:
42
+ Parsed collection rows and whether another matching entry exists.
43
+ """
44
+ try:
45
+ iterator = iter(self.client.list_functions(
46
+ project=project or self.project, schema=schema, prefix=prefix,
47
+ ))
48
+ window = list(islice(iterator, offset, offset + limit + 1))
49
+ # PyODPS models lazily reload missing fields. Read only parsed
50
+ # collection fields here so List does not also require Read.
51
+ rows = [{
52
+ "function_name": object.__getattribute__(item, "name"),
53
+ "class_type": object.__getattribute__(item, "class_type"),
54
+ "owner": object.__getattribute__(item, "_owner"),
55
+ } for item in window[:limit]]
56
+ return rows, len(window) > limit
57
+ except Exception as exc:
58
+ raise translate_odps_error(exc, "list_functions") from exc
59
+
60
+ def describe_function(
61
+ self,
62
+ function_name: str,
63
+ *,
64
+ project: 'str | None' = None,
65
+ schema: 'str | None' = None,
66
+ ) -> 'dict[str, Any]':
67
+ """Read registration metadata without loading resource contents.
68
+
69
+ Args:
70
+ function_name: Bare registered function alias.
71
+ project: Function project, or the configured project.
72
+ schema: Explicit function schema, when applicable.
73
+
74
+ Returns:
75
+ Registration metadata with exact resource references and unknown
76
+ signature/runtime fields. Implementation code is excluded.
77
+ """
78
+ try:
79
+ function = self.client.get_function(
80
+ function_name, project=project or self.project, schema=schema,
81
+ )
82
+ # get_function is lazy: reload explicitly so missing aliases fail
83
+ # even when the SDK already knows the requested name.
84
+ function.reload()
85
+
86
+ def read(key: str) -> Any:
87
+ try:
88
+ return object.__getattribute__(function, key)
89
+ except AttributeError:
90
+ # Older supported PyODPS releases omit optional flags.
91
+ return None
92
+
93
+ return {
94
+ "function_name": read("name"),
95
+ "class_type": read("class_type"),
96
+ "resource_names": list(read("_resources") or []),
97
+ "owner": read("_owner"),
98
+ "creation_time": _dt_to_iso(read("creation_time")),
99
+ "program_language": read("program_language"),
100
+ "is_sql_function": self._function_boolean(read("is_sql_function")),
101
+ "is_embedded_function": self._function_boolean(read("is_embedded_function")),
102
+ "file_name": read("file_name"),
103
+ "signature": None,
104
+ "runtime_version": None,
105
+ }
106
+ except Exception as exc:
107
+ raise translate_odps_error(exc, "describe_function") from exc
108
+
109
+ @staticmethod
110
+ def _function_boolean(value: Any) -> 'bool | None':
111
+ if value is True or str(value).lower() == "true":
112
+ return True
113
+ if value is False or str(value).lower() == "false":
114
+ return False
115
+ return None
116
+
23
117
  def list_tables(
24
118
  self,
25
119
  *,
@@ -455,6 +455,22 @@ def build_parser() -> argparse.ArgumentParser:
455
455
  meta_describe.add_argument("--full", action="store_true", help="Show full column list (default is summary mode)")
456
456
  meta_describe.set_defaults(handler=_handle_meta_describe)
457
457
 
458
+ meta_functions = _make_parser(meta_subparsers, "list-functions", "meta.list-functions", help="List registered functions")
459
+ meta_functions.add_argument("--project", help="Target MaxCompute project")
460
+ meta_functions.add_argument("--schema", help="Schema name (overrides session default)")
461
+ meta_functions.add_argument("--prefix", help="Function alias prefix filter")
462
+ meta_functions.add_argument("--limit", type=positive_int, default=50, help="Maximum functions to return (1-1000; default 50)")
463
+ meta_functions.add_argument("--cursor", help="Scope-bound cursor returned by the previous function list")
464
+ meta_functions.add_argument("--json", action="store_true", help="Output as JSON envelope")
465
+ meta_functions.set_defaults(handler=_handle_meta_list_functions)
466
+
467
+ meta_function = _make_parser(meta_subparsers, "describe-function", "meta.describe-function", help="Describe a registered function")
468
+ meta_function.add_argument("function_name", help="Bare registered alias; select scope with --project / --schema")
469
+ meta_function.add_argument("--project", help="Target MaxCompute project")
470
+ meta_function.add_argument("--schema", help="Schema name (overrides session default)")
471
+ meta_function.add_argument("--json", action="store_true", help="Output as JSON envelope")
472
+ meta_function.set_defaults(handler=_handle_meta_describe_function)
473
+
458
474
  meta_search = _make_parser(meta_subparsers, "search", "meta.search", help="Search tables")
459
475
  meta_search.add_argument("keyword", help="Search keyword")
460
476
  meta_search.add_argument("--schema", help="Schema name (overrides session default)")
@@ -2121,6 +2137,18 @@ def _handle_meta_list_tables(app: MaxCApp, args: argparse.Namespace, stdout: Tex
2121
2137
  _emit_envelope(envelope, args=args, stdout=stdout, default_format="table")
2122
2138
 
2123
2139
 
2140
+ def _handle_meta_list_functions(app: MaxCApp, args: argparse.Namespace, stdout: TextIO) -> None:
2141
+ envelope = app.meta_list_functions(
2142
+ project=args.project, schema=args.schema, prefix=args.prefix, limit=args.limit, cursor=args.cursor,
2143
+ )
2144
+ _emit_envelope(envelope, args=args, stdout=stdout, default_format="table")
2145
+
2146
+
2147
+ def _handle_meta_describe_function(app: MaxCApp, args: argparse.Namespace, stdout: TextIO) -> None:
2148
+ envelope = app.meta_describe_function(args.function_name, project=args.project, schema=args.schema)
2149
+ _emit_envelope(envelope, args=args, stdout=stdout, default_format="table")
2150
+
2151
+
2124
2152
  def _handle_meta_describe(app: MaxCApp, args: argparse.Namespace, stdout: TextIO) -> None:
2125
2153
  # When --json is used, always return full schema (agents need all columns)
2126
2154
  full = args.full or _is_json_mode(args)
@@ -2966,6 +2994,8 @@ def _manifest_effects(command: str) -> list[dict[str, Any]]:
2966
2994
  ),
2967
2995
  ]
2968
2996
  effects_by_command: dict[str, list[dict[str, Any]]] = {
2997
+ "meta.list-functions": [_manifest_effect("remote", "read", "maxcompute_function_catalog")],
2998
+ "meta.describe-function": [_manifest_effect("remote", "read", "maxcompute_function_metadata")],
2969
2999
  "agent.context": [
2970
3000
  _manifest_effect("local", "read", "config_and_runtime_readiness"),
2971
3001
  ],
@@ -4527,6 +4557,12 @@ def _render_human(envelope: Envelope) -> str:
4527
4557
  if command == "meta.list-tables":
4528
4558
  return render_table(data.get("tables", []))
4529
4559
 
4560
+ if command == "meta.list-functions":
4561
+ return render_table(data.get("functions", []))
4562
+
4563
+ if command == "meta.describe-function":
4564
+ return render_key_values(data.get("function", {}))
4565
+
4530
4566
  if command == "meta.describe":
4531
4567
  # Render schema/partition_columns as nested sub-tables instead of
4532
4568
  # JSON-stringifying them into a single cell.
@@ -4593,6 +4629,7 @@ _RECORD_COLLECTION_KEYS: dict[str, tuple[str, str]] = {
4593
4629
  "meta.list-projects": ("projects", "project"),
4594
4630
  "meta.list-schemas": ("schemas", "schema"),
4595
4631
  "meta.list-tables": ("tables", "table"),
4632
+ "meta.list-functions": ("functions", "function"),
4596
4633
  "meta.partitions": ("partitions", "partition"),
4597
4634
  "meta.search": ("matches", "match"),
4598
4635
  "meta.search-columns": ("matches", "match"),
@@ -4605,6 +4642,7 @@ _RECORD_DEFAULT_COLUMNS: dict[str, list[str]] = {
4605
4642
  "job.list": ["job_id", "status", "progress", "project", "submitted_at"],
4606
4643
  "meta.list-projects": ["name"],
4607
4644
  "meta.list-schemas": ["name"],
4645
+ "meta.list-functions": ["function_name", "project", "schema_name", "class_type", "owner"],
4608
4646
  "meta.list-tables": [
4609
4647
  "table_name",
4610
4648
  "schema_name",
@@ -101,6 +101,20 @@ class MaxCError(Exception):
101
101
  prefix += f" --user-agent {rendered_user_agent}"
102
102
  return f"{prefix} {' '.join(parts)}"
103
103
 
104
+ if (self.context or {}).get("metadata_kind") == "function":
105
+ if self.error_code == "PERMISSION_DENIED":
106
+ return [
107
+ "Verify the selected project, schema, alias, and the operation named in the server error.",
108
+ "Use a known function contract for SQL execution only when that task is authorized.",
109
+ "Ask the function owner for missing metadata access; keep the current credentials.",
110
+ ]
111
+ if self.error_code == "NOT_FOUND":
112
+ return [
113
+ "List functions in the verified scope: " + command("meta", "list-functions", "--project", "<project>", "--json"),
114
+ "Add --schema only when the verified function scope requires it.",
115
+ "Verify local project and schema: " + command("session", "show", "--json", cloud=False),
116
+ ]
117
+
104
118
  steps: dict[str, list[str]] = {
105
119
  "PERMISSION_DENIED": [
106
120
  "Check the exact object and operation: "
@@ -0,0 +1,36 @@
1
+ """Scope-bound pagination for registered function discovery."""
2
+
3
+ import base64
4
+ import json
5
+ from typing import Any
6
+
7
+ from .exceptions import ValidationError
8
+
9
+
10
+ def function_cursor(offset: int, *, project: str, schema: 'str | None', prefix: 'str | None') -> str:
11
+ payload = {"v": 1, "offset": offset, "project": project, "schema": schema, "prefix": prefix}
12
+ return base64.urlsafe_b64encode(json.dumps(payload, separators=(",", ":")).encode()).decode()
13
+
14
+
15
+ def function_offset(cursor: 'str | None', *, project: str, schema: 'str | None', prefix: 'str | None') -> int:
16
+ if not cursor:
17
+ return 0
18
+ try:
19
+ if len(cursor) > 8192:
20
+ raise ValueError("oversized cursor")
21
+ payload: Any = json.loads(base64.b64decode(cursor, altchars=b"-_", validate=True))
22
+ if not isinstance(payload, dict) or payload.get("v") != 1:
23
+ raise ValueError("invalid version")
24
+ offset = payload.get("offset")
25
+ if type(offset) is not int or offset < 0:
26
+ raise ValueError("invalid offset")
27
+ if any(payload.get(key) != value for key, value in {
28
+ "project": project, "schema": schema, "prefix": prefix,
29
+ }.items()):
30
+ raise ValueError("scope mismatch")
31
+ return offset
32
+ except (ValueError, TypeError, UnicodeError) as exc:
33
+ raise ValidationError(
34
+ "Invalid function cursor or cursor scope mismatch.",
35
+ suggestion="Use the returned next_cursor with the same project, schema, and prefix.",
36
+ ) from exc
@@ -978,9 +978,14 @@ def translate_odps_error(exc: Exception, context: str = "") -> MaxCError:
978
978
  return err
979
979
  if context in {"job", "get_instance", "instance"} or "instance" in message.lower():
980
980
  suggestion = "Verify the job ID with `maxc job list`. Job history may have aged out of the server window."
981
+ elif context in {"list_functions", "describe_function"}:
982
+ suggestion = "Verify the registered alias and exact project/schema with `maxc meta list-functions --json`."
981
983
  else:
982
984
  suggestion = "Run `maxc meta list-tables` or `maxc meta search` to verify the object exists."
983
- err = NotFoundError(message, suggestion=suggestion)
985
+ err = NotFoundError(
986
+ message, suggestion=suggestion,
987
+ context={"metadata_kind": "function"} if context in {"list_functions", "describe_function"} else None,
988
+ )
984
989
  err.suggestion = _append_request_id(err.suggestion, exc)
985
990
  return err
986
991
 
@@ -1134,6 +1139,17 @@ def _build_permission_error(
1134
1139
  """
1135
1140
  dev_hint = _dev_project_hint(project_name)
1136
1141
 
1142
+ if context in {"list_functions", "describe_function"}:
1143
+ operation = "project List" if context == "list_functions" else "Function Read"
1144
+ return PermissionDeniedError(
1145
+ message,
1146
+ suggestion=(
1147
+ f"Verify {operation} permission in the selected project/schema. "
1148
+ "Metadata access and Function Execute are separate; keep the current identity."
1149
+ ),
1150
+ context={"metadata_kind": "function", "operation": operation},
1151
+ )
1152
+
1137
1153
  if context == "list_projects" and project_name:
1138
1154
  suggestion = f"Verify that your account has `odps:Read` on project {project_name}, or contact the project owner."
1139
1155
  if dev_hint:
@@ -22,11 +22,13 @@ _CLOUD_ACTION_IDS = frozenset({
22
22
  "auth.whoami",
23
23
  "cache.build",
24
24
  "meta.describe",
25
+ "meta.describe-function",
25
26
  "meta.freshness",
26
27
  "meta.latest-partition",
27
28
  "meta.list-projects",
28
29
  "meta.list-schemas",
29
30
  "meta.list-tables",
31
+ "meta.list-functions",
30
32
  "meta.partitions",
31
33
  "meta.search",
32
34
  "meta.search-columns",
@@ -498,9 +500,11 @@ _ACTION_TITLES: 'dict[str, str]' = {
498
500
  "job.diagnose": "Diagnose job",
499
501
  "job.list": "List jobs",
500
502
  "meta.describe": "Describe table",
503
+ "meta.describe-function": "Describe registered function",
501
504
  "meta.search": "Search tables",
502
505
  "meta.search-columns": "Search columns",
503
506
  "meta.list-tables": "List tables",
507
+ "meta.list-functions": "List registered functions",
504
508
  "meta.list-projects": "List projects",
505
509
  "meta.list-schemas": "List schemas",
506
510
  "meta.partitions": "List partitions",
@@ -15,7 +15,7 @@ standalone console script is unavailable<!-- @endif -->.
15
15
 
16
16
  Use this Skill when the user wants to:
17
17
 
18
- - find a project, schema, table, column, or partition;
18
+ - find a project, schema, table, column, partition, or registered function;
19
19
  - look up documented MaxCompute behaviour, SQL syntax, hints, flags, or error-code
20
20
  meaning with a citable source;
21
21
  - inspect or profile table data;
@@ -156,6 +156,7 @@ Important data paths:
156
156
  | auth whoami | `data.identity` |
157
157
  | auth can-i | `data.authorization` |
158
158
  | meta describe | `data.table` |
159
+ | meta list-functions / describe-function | `data.functions`, `data.pagination` / `data.function` |
159
160
  | data sample / profile | `data.sample` / `data.profile` |
160
161
  | async submission | `metadata.job_id` |
161
162
 
@@ -236,6 +237,16 @@ only with values verified from the user, context, or prior command output.
236
237
  {{cli}} data profile <table> --partition <spec> --user-agent "$UA" --json
237
238
  ```
238
239
 
240
+ ### Use An Already Registered Function
241
+
242
+ Read [registered-functions.md](references/registered-functions.md) when a SQL
243
+ task references a user UDF, UDAF, or UDTF. Use `meta list-functions` for scoped
244
+ discovery and `meta describe-function` for registration metadata. Obtain the
245
+ call signature, function kind, and runtime requirements from verified user
246
+ context or the owner's documentation before composing SQL. Execute through
247
+ the existing `query cost`, `query`, and `job` workflow with the same SQL and
248
+ settings; this workflow does not upload resources or register functions.
249
+
239
250
  ### Knowledge Base Lookup
240
251
 
241
252
  `kb ask` answers a natural-language MaxCompute question; `kb search` returns
@@ -338,7 +349,8 @@ For substantial SQL generation, load only the reference matching the task:
338
349
  ## Capability Boundaries
339
350
 
340
351
  - The CLI does not grant permissions or enumerate a complete permission graph.
341
- - It does not provide lineage, resource artifact upload, dedicated UDF
352
+ - It provides registered function metadata, but not automatic signature or
353
+ runtime-version discovery, lineage, resource artifact upload, dedicated UDF
342
354
  lifecycle commands, or an active mock data backend. A supported, exact SQL
343
355
  function DDL remains subject to the one-statement `--force` boundary.
344
356
  - Tunnel upload/download is single-process and primitive-type oriented; use a
@@ -74,6 +74,8 @@ pipelines, redirections, and shell substitutions are not supported.
74
74
  {{cli}} meta freshness your_table --json
75
75
  {{cli}} meta list-projects --json
76
76
  {{cli}} meta list-schemas --project your_project --json
77
+ {{cli}} meta list-functions --project your_project --prefix normalize_ --limit 20 --json
78
+ {{cli}} meta describe-function normalize_phone --project your_project --json
77
79
  {{cli}} data sample your_table --rows 5 --partition ds=2026-03-20 --columns id,ds --json
78
80
  {{cli}} data profile your_table --partition ds=2026-03-20 --json
79
81
  {{cli}} data upload your_table --file ./rows.csv --partition ds=2026-03-20 --overwrite --json
@@ -83,6 +85,11 @@ pipelines, redirections, and shell substitutions are not supported.
83
85
  - All meta and data commands accept `--project` for one-off cross-project access without switching session.
84
86
  - Most meta commands support `--schema` to override the session default.
85
87
 
88
+ For function references in SQL, follow [registered-functions.md](registered-functions.md).
89
+ `meta list-functions` is bounded and accepts a scope-bound `--cursor`;
90
+ `meta describe-function` takes a bare alias with explicit project/schema flags.
91
+ Neither reads resource contents or infers a call signature.
92
+
86
93
  ### Bulk CSV Upload / Download
87
94
 
88
95
  `data upload` and `data download` move CSV/TSV between local files and an existing table or partition via the PyODPS Tunnel API (no SQL CU consumed).
@@ -0,0 +1,78 @@
1
+ # Reference already registered functions
2
+
3
+ Use this workflow for an existing MaxCompute function. Registration and resource
4
+ publication belong to a separately authorized task. Commands require MaxC 0.9.0
5
+ or later. Reuse the session User-Agent from SKILL.md.
6
+
7
+ ## Resolve the function and its contract
8
+
9
+ 1. Establish the SQL execution project separately from the function's project.
10
+ Use verified project/schema values. `meta list-schemas` can establish the
11
+ namespace model; a denied schema lookup is not evidence of a two-tier project.
12
+ 2. If the registered alias is known, describe it directly. Otherwise, list a
13
+ bounded page in the relevant scope with an optional alias prefix:
14
+
15
+ ```bash
16
+ {{cli}} meta list-functions --project <function-project> --prefix <prefix> --limit 20 --user-agent "$UA" --json
17
+ {{cli}} meta describe-function <function-alias> --project <function-project> --user-agent "$UA" --json
18
+ ```
19
+
20
+ Add `--schema <verified-schema>` for a schema-scoped function. Describe takes
21
+ a bare alias, not a SQL-qualified reference. List returns `data.functions`
22
+ and `data.pagination`; reuse `next_cursor` with the same project, schema,
23
+ and prefix. Limit is 1–1000 (default 50). Pages are live catalog windows,
24
+ not a snapshot; offset pagination can rescan earlier entries. A list reads
25
+ collection metadata only, without per-function Read requests or resource
26
+ downloads. Missing fields remain null. An empty page establishes no visible
27
+ matching functions in that scope, not global absence.
28
+ 3. Describe returns `data.function`: alias, implementation class, exact resource
29
+ references, owner, creation time, and additional registration flags if the
30
+ service supplies them. It excludes implementation code and resource contents.
31
+ `signature` and `runtime_version` remain null: class names, filenames, and
32
+ creation times do not establish a signature, Python version, function kind,
33
+ business definition, or immutable implementation version.
34
+ 4. Obtain UDF/UDAF/UDTF kind, overload/input/output types, output column names
35
+ for UDTF, NULL behavior when relevant, and required settings from the owner's
36
+ documentation, a trusted semantic definition, or verified user SQL. Ask only
37
+ for missing facts that affect the call. Disambiguate same-name candidates.
38
+ A user with known SQL and Function Execute can proceed even if Function Read
39
+ or project List is denied. Preserve the permission error; do not relabel it
40
+ as an absent function or switch credentials.
41
+
42
+ ## Generate and execute the SQL
43
+
44
+ The function's namespace creation mode must match the execution mode.
45
+ Two-tier cross-project calls use `project:function(...)`. Three-tier objects
46
+ use `project.schema.function(...)`, or `schema.function(...)` within one
47
+ project. Verify the syntax in the actual project; do not mechanically rewrite
48
+ colon references into dotted references. Never enable schema mode merely
49
+ because the default schema is named `default`.
50
+
51
+ Preserve author-required leading SET statements in the SQL file for both cost
52
+ and execution. For example, a Python 3.11 function can require
53
+ `SET odps.sql.python.version=cp311;`. Do not apply that version to every Python
54
+ function. Conflicting Python or namespace requirements need resolution before
55
+ submission. See the official [schema operations](https://help.aliyun.com/zh/maxcompute/schema-related-operations)
56
+ and [Python 3 UDF documentation](https://help.aliyun.com/zh/maxcompute/python-3-udfs).
57
+
58
+ Use a scalar UDF as an expression, UDAF in the correct aggregation grain, and
59
+ UDTF with the correct output shape (often `LATERAL VIEW`). Use explicit casts
60
+ only when the contract supports them. For a newly integrated function, prefer
61
+ constant inputs or a known small partition for a first check; `LIMIT` alone
62
+ does not bound scan work or UDF invocations.
63
+
64
+ ```bash
65
+ {{cli}} query cost --file <verified-query.sql> --project <execution-project> --user-agent "$UA" --json
66
+ {{cli}} query --file <verified-query.sql> --project <execution-project> --wait 0 --user-agent "$UA" --json
67
+ {{cli}} job wait <job-id> --project <execution-project> --user-agent "$UA" --json
68
+ ```
69
+
70
+ Apply the existing cost/authorization gates. Cost or EXPLAIN success is a
71
+ planning check, not proof that the UDF runs correctly. Wait for the job's
72
+ terminal state and verify result values, types, and cardinality. If another
73
+ result page is needed, use the returned cursor without resubmitting the SQL.
74
+ Retain the submitted SQL, settings, function scope, contract source, instance
75
+ ID, and actual result in the requested delivery. Function Execute and dependent
76
+ resource access are decided by MaxCompute under the configured caller identity.
77
+ Report permission, signature, missing dependency, namespace, and runtime errors
78
+ as distinct failures; do not repair them by registering a replacement function.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxc-cli
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: Agent-native MaxCompute CLI for external coding agents
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.9
@@ -70,7 +70,7 @@ maxc agent doctor --online --json
70
70
  |------|------|------|
71
71
  | **query** | `query [run]`, `query cost`, `query explain` | SQL 执行、成本估算、执行计划 |
72
72
  | **job** | `submit`, `status`, `wait`, `result`, `cancel`, `diagnose`, `list` | 异步任务全生命周期 |
73
- | **meta** | `list-tables`, `describe`, `search`, `search-columns`, `partitions`, `latest-partition`, `freshness`, `list-projects`, `list-schemas`, `semantic set/get/clear/list-missing` | 元数据发现与语义管理 |
73
+ | **meta** | `list-tables`, `describe`, `list-functions`, `describe-function`, `search`, `search-columns`, `partitions`, `latest-partition`, `freshness`, `list-projects`, `list-schemas`, `semantic set/get/clear/list-missing` | 元数据发现与语义管理 |
74
74
  | **data** | `sample`, `profile`, `upload`, `download` | 数据采样、画像与 CSV/TSV 传输 |
75
75
  | **auth** | `login`, `login-external`, `logout`, `whoami`, `can-i` | 认证与权限 |
76
76
  | **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
@@ -81,6 +81,18 @@ maxc agent doctor --online --json
81
81
  使用 `--json`;CSV/NDJSON 行流和 `job wait --stream` 生命周期流是明确例外,
82
82
  不会为每条记录重复封装 Envelope。
83
83
 
84
+ 0.9.0 起支持已注册函数的目录与详情查询:
85
+
86
+ ```bash
87
+ aliyun maxc meta list-functions --project <project> --prefix <prefix> --limit 20 --json
88
+ aliyun maxc meta describe-function <alias> --project <project> --json
89
+ ```
90
+
91
+ 三层项目可加 `--schema`;详情参数使用裸函数别名。列表有界分页,详情保留
92
+ 类名和原始资源引用,不下载资源内容,也不推断签名或 Python 版本。
93
+ 按作者文档确定调用契约后,复用现有 SQL 成本、执行和作业命令引用函数。
94
+ 具体流程见 [已注册函数](src/maxc_cli/skills/references/registered-functions.md)。
95
+
84
96
  ## Agent 集成
85
97
 
86
98
  ### 方式 1:公共 Skill(主路径)