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.
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/PKG-INFO +14 -2
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/README.md +13 -1
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/__init__.py +1 -1
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/_samples.py +8 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/app.py +68 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/meta.py +94 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/cli.py +38 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/exceptions.py +14 -0
- maxc_cli-0.9.0/src/maxc_cli/function_metadata.py +36 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/helpers.py +17 -1
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/models.py +4 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/SKILL.md +14 -2
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/command-patterns.md +7 -0
- maxc_cli-0.9.0/src/maxc_cli/skills/references/registered-functions.md +78 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/PKG-INFO +14 -2
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/SOURCES.txt +3 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_help_version_e2e.py +8 -0
- maxc_cli-0.9.0/tests/test_registered_functions.py +207 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/MANIFEST.in +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/pyproject.toml +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/scripts/pyinstaller_entry.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/scripts/regression_test.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/setup.cfg +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/setup.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/__main__.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/agent_platforms.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/audit.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/auth_continuation.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/auth_providers.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/__init__.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/auth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/catalog.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/data.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/job.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/mcp.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/odps.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/query.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/backend/semantic.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/cache.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/catalog_bootstrap.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/config.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/enterprise_tls.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/help_format.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/job_ids.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/masking.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/mcp_serve.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/oauth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/odps_runtime.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/output.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/proxy_auth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/semantic.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/semantic_management.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/setting_parser.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/agents/openai.yaml +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/bootstrap-flow.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/json-output-format.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/maxcompute-select-guide.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/partition-guide.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/red-lines.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/semantic-packages.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/setup-install.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/sql-common-errors.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/sql-query-patterns.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/skills/references/text2sql-principles.md +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/state_permissions.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/store.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli/utils.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/entry_points.txt +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/requires.txt +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/src/maxc_cli.egg-info/top_level.txt +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_hints_and_cli.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_platforms.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_skill_commands.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_agent_skill_commands_context.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_ai_native_contract_regressions.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_auth_logout.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_auth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_data.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_data_serialization.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_mcp.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_backend_meta.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_build_release_archive_compat.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_build_release_script.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cache.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_catalog.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_catalog_bootstrap.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cli_arg_validation.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cli_mock.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_cli_query_parse_and_sanitize.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_compat.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_config_atomic_write.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_connection_bootstrap.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_e2e_smoke.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_effective_hints_contract.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_enterprise_tls.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_envelope_shape.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_error_self_correction.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_error_translation.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_exit_codes.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_external_auth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_flag_hoist.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_help_format.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_helpers.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_helpers_csv.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_installer_contracts.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_integration.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_integration_real.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_job_improvements.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_job_inspection.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_job_store_durability.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_kb_commands.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_manifest_runtime_contract.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_masking.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_mcp_serve.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_meta_schema_and_partition_cols.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_oauth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_odps_runtime.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_output_action_safety.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_output_format_contract.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_packaging_metadata.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_phase1_improvements.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_proxy_auth.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_pyinstaller_bundle.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_python39_compat.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_query_auto_promote.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_query_result_csv_fallback.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_semantic_management.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_semantic_scope.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_semantic_transport.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_setting_parser.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_skill_cli_consistency.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_skill_eval.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_skill_renderer.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_startup_imports.py +0 -0
- {maxc_cli-0.8.2 → maxc_cli-0.9.0}/tests/test_state_permissions.py +0 -0
- {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.
|
|
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(主路径)
|
|
@@ -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(
|
|
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
|
|
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
|
|
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.
|
|
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(主路径)
|