maxc-cli 0.4.2__tar.gz → 0.4.3__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.4.2 → maxc_cli-0.4.3}/PKG-INFO +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/setup.py +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/__init__.py +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/agent_platforms.py +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/app.py +4 -2
- maxc_cli-0.4.3/src/maxc_cli/backend/auth.py +164 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/cli.py +51 -9
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/helpers.py +5 -5
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/SKILL.md +20 -24
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -4
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/bootstrap-flow.md +4 -13
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/command-patterns.md +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/red-lines.md +3 -3
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli.egg-info/PKG-INFO +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli.egg-info/SOURCES.txt +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_agent_skill_commands_context.py +1 -1
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_integration_real.py +2 -2
- maxc_cli-0.4.3/tests/test_skill_eval.py +193 -0
- maxc_cli-0.4.2/src/maxc_cli/backend/auth.py +0 -175
- maxc_cli-0.4.2/src/maxc_cli/skills/references/migrate-from-odpscmd.md +0 -133
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/MANIFEST.in +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/README.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/pyproject.toml +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/scripts/pyinstaller_entry.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/scripts/regression_test.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/setup.cfg +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/__main__.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/_samples.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/audit.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/auth_providers.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/__init__.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/catalog.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/data.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/job.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/meta.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/odps.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/backend/query.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/cache.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/catalog_bootstrap.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/config.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/exceptions.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/help_format.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/masking.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/models.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/output.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/setting_parser.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/agents/openai.yaml +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/json-output-format.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/maxcompute-select-guide.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/partition-guide.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/setup-install.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/sql-common-errors.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/sql-query-patterns.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/skills/references/text2sql-principles.md +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/store.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli/utils.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli.egg-info/entry_points.txt +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli.egg-info/requires.txt +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/src/maxc_cli.egg-info/top_level.txt +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_agent_hints_and_cli.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_agent_platforms.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_agent_skill_commands.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_backend_data.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_backend_data_serialization.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_backend_meta.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_build_release_script.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_cache.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_catalog.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_catalog_bootstrap.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_cli_arg_validation.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_cli_mock.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_cli_query_parse_and_sanitize.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_compat.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_e2e_smoke.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_envelope_shape.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_error_self_correction.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_error_translation.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_exit_codes.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_external_auth.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_flag_hoist.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_help_format.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_help_version_e2e.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_helpers.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_helpers_csv.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_integration.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_job_improvements.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_masking.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_meta_schema_and_partition_cols.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_phase1_improvements.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_pyinstaller_bundle.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_query_auto_promote.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_query_result_csv_fallback.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_setting_parser.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_skill_cli_consistency.py +0 -0
- {maxc_cli-0.4.2 → maxc_cli-0.4.3}/tests/test_skill_renderer.py +0 -0
|
@@ -9,7 +9,7 @@ README = ROOT / "README.md"
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="maxc-cli",
|
|
12
|
-
version="0.4.
|
|
12
|
+
version="0.4.3",
|
|
13
13
|
description="Agent-native MaxCompute CLI for external coding agents",
|
|
14
14
|
long_description=README.read_text(encoding="utf-8"),
|
|
15
15
|
long_description_content_type="text/markdown",
|
|
@@ -118,7 +118,7 @@ def _build_registry() -> tuple[Platform, ...]:
|
|
|
118
118
|
install_root=_claude_root(),
|
|
119
119
|
skill_subpath=None,
|
|
120
120
|
extra_files=(),
|
|
121
|
-
next_step_hint="
|
|
121
|
+
next_step_hint="Skill is auto-discovered — start a new Claude Code session to activate",
|
|
122
122
|
),
|
|
123
123
|
Platform(name="cursor", install_root=_simple_root(".cursor"),
|
|
124
124
|
next_step_hint="Restart Cursor to activate"),
|
|
@@ -2940,13 +2940,15 @@ class MaxCApp:
|
|
|
2940
2940
|
def auth_can_i(
|
|
2941
2941
|
self,
|
|
2942
2942
|
*,
|
|
2943
|
-
|
|
2943
|
+
object_name: 'str',
|
|
2944
|
+
object_type: 'str' = "Table",
|
|
2944
2945
|
operation: 'str',
|
|
2945
2946
|
project: 'str | None' = None,
|
|
2946
2947
|
) -> 'Envelope':
|
|
2947
2948
|
target_project = project or self.config.default_project
|
|
2948
2949
|
payload, warnings = self.backend.can_i_info(
|
|
2949
|
-
|
|
2950
|
+
object_name=object_name,
|
|
2951
|
+
object_type=object_type,
|
|
2950
2952
|
operation=operation,
|
|
2951
2953
|
project=target_project,
|
|
2952
2954
|
)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Auth-related mixin for OdpsBackend."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from xml.etree import ElementTree
|
|
5
|
+
|
|
6
|
+
from ..exceptions import BackendConnectionError, MaxCError
|
|
7
|
+
from ..helpers import (
|
|
8
|
+
build_odps_identity_payload,
|
|
9
|
+
odps_identity_source,
|
|
10
|
+
quote_table_name,
|
|
11
|
+
translate_odps_error,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AuthMixin:
|
|
16
|
+
"""Mixin providing authentication and authorization methods."""
|
|
17
|
+
|
|
18
|
+
def whoami_info(self, *, project: 'str | None' = None) -> 'tuple[dict[str, Any], list[str]]':
|
|
19
|
+
"""Get current identity info by executing ``whoami`` security query.
|
|
20
|
+
|
|
21
|
+
Calls ``client.execute_security_query("whoami")`` to verify the
|
|
22
|
+
connection and return desensitized identity information.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
project: Optional project override.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Tuple of (identity payload dict, warnings list).
|
|
29
|
+
"""
|
|
30
|
+
target_project = project or self.project
|
|
31
|
+
try:
|
|
32
|
+
result = self.client.execute_security_query("whoami", project=target_project)
|
|
33
|
+
except Exception as exc:
|
|
34
|
+
raise translate_odps_error(exc, "whoami") from exc
|
|
35
|
+
|
|
36
|
+
owner_display_name = result.get("DisplayName") if isinstance(result, dict) else None
|
|
37
|
+
if owner_display_name:
|
|
38
|
+
self._owner_display_name = owner_display_name
|
|
39
|
+
return build_odps_identity_payload(
|
|
40
|
+
client=self.client,
|
|
41
|
+
settings=self.settings,
|
|
42
|
+
allowed_operations=self.config.allowed_operations,
|
|
43
|
+
identity_source=odps_identity_source(self.setting_sources),
|
|
44
|
+
auth_type=getattr(self.resolved_auth, "auth_type", "access_key"),
|
|
45
|
+
token_expires_at=getattr(self.resolved_auth, "token_expires_at", None),
|
|
46
|
+
project=target_project,
|
|
47
|
+
owner_display_name=owner_display_name,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _check_permission(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
object_name: 'str',
|
|
54
|
+
object_type: 'str',
|
|
55
|
+
action: 'str',
|
|
56
|
+
project: 'str',
|
|
57
|
+
) -> 'tuple[bool, str]':
|
|
58
|
+
"""Call the MaxCompute checkPermission REST API.
|
|
59
|
+
|
|
60
|
+
Uses GET /projects/{project}/auth/?type={type}&name={name}&grantee={action}
|
|
61
|
+
(same endpoint as Java SDK's SecurityManager#checkPermission).
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Tuple of (allowed: bool, message: str).
|
|
65
|
+
"""
|
|
66
|
+
import json as _json
|
|
67
|
+
|
|
68
|
+
rest = self.client.rest
|
|
69
|
+
endpoint = rest.endpoint
|
|
70
|
+
url = f"{endpoint}/projects/{project}/auth/"
|
|
71
|
+
params = {
|
|
72
|
+
"type": object_type,
|
|
73
|
+
"name": object_name,
|
|
74
|
+
"grantee": action,
|
|
75
|
+
}
|
|
76
|
+
resp = rest.get(url, params=params)
|
|
77
|
+
root = ElementTree.fromstring(resp.content)
|
|
78
|
+
result = (root.findtext("Result") or "").strip()
|
|
79
|
+
raw_message = (root.findtext("Message") or "").strip()
|
|
80
|
+
try:
|
|
81
|
+
parsed = _json.loads(raw_message)
|
|
82
|
+
message = parsed.get("message", "") if isinstance(parsed, dict) else raw_message
|
|
83
|
+
except (ValueError, TypeError):
|
|
84
|
+
message = raw_message
|
|
85
|
+
return result.upper() == "ALLOW", message
|
|
86
|
+
|
|
87
|
+
def can_i_info(
|
|
88
|
+
self,
|
|
89
|
+
*,
|
|
90
|
+
object_name: 'str',
|
|
91
|
+
object_type: 'str' = "Table",
|
|
92
|
+
operation: 'str',
|
|
93
|
+
project: 'str | None' = None,
|
|
94
|
+
) -> 'tuple[dict[str, Any], list[str]]':
|
|
95
|
+
"""Check if a specific operation is allowed on an object.
|
|
96
|
+
|
|
97
|
+
Calls the MaxCompute checkPermission REST API directly.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
object_name: Object name to check (table name, function name, etc.).
|
|
101
|
+
object_type: Object type (Table, Project, Function, Resource, Instance).
|
|
102
|
+
operation: ODPS ActionType (e.g. "Select", "CreateInstance").
|
|
103
|
+
project: Optional project override.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Tuple of (permission payload dict, warnings list).
|
|
107
|
+
"""
|
|
108
|
+
target_project = project or self.project
|
|
109
|
+
if object_type == "Table":
|
|
110
|
+
quote_table_name(object_name)
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
allowed, message = self._check_permission(
|
|
114
|
+
object_name=object_name,
|
|
115
|
+
object_type=object_type,
|
|
116
|
+
action=operation,
|
|
117
|
+
project=target_project,
|
|
118
|
+
)
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
translated = translate_odps_error(exc)
|
|
121
|
+
if isinstance(translated, BackendConnectionError):
|
|
122
|
+
raise translated
|
|
123
|
+
return (
|
|
124
|
+
{
|
|
125
|
+
"object_type": object_type,
|
|
126
|
+
"object_name": object_name,
|
|
127
|
+
"project": target_project,
|
|
128
|
+
"operation": operation,
|
|
129
|
+
"allowed": False,
|
|
130
|
+
"check_mode": "odps_check_permission_api",
|
|
131
|
+
"reason": translated.message,
|
|
132
|
+
"check_error_code": translated.error_code,
|
|
133
|
+
},
|
|
134
|
+
[],
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
{
|
|
139
|
+
"object_type": object_type,
|
|
140
|
+
"object_name": object_name,
|
|
141
|
+
"project": target_project,
|
|
142
|
+
"operation": operation,
|
|
143
|
+
"allowed": allowed,
|
|
144
|
+
"check_mode": "odps_check_permission_api",
|
|
145
|
+
"reason": message if message else ("Allowed." if allowed else "Denied."),
|
|
146
|
+
"check_error_code": None if allowed else "PERMISSION_DENIED",
|
|
147
|
+
},
|
|
148
|
+
[],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def _get_owner_display_name(self) -> 'str | None':
|
|
152
|
+
"""Get the current user's display name (e.g., 'ALIYUN$xxx' or 'RAM$xxx')."""
|
|
153
|
+
if self._owner_display_name is not None:
|
|
154
|
+
return self._owner_display_name
|
|
155
|
+
try:
|
|
156
|
+
result = self.client.execute_security_query("whoami", project=self.project)
|
|
157
|
+
display_name = result.get("DisplayName") if isinstance(result, dict) else None
|
|
158
|
+
if display_name:
|
|
159
|
+
self._owner_display_name = display_name
|
|
160
|
+
return display_name
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
return None
|
|
@@ -532,16 +532,57 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
532
532
|
auth_whoami.add_argument("--json", action="store_true", help="Output as JSON envelope")
|
|
533
533
|
auth_whoami.set_defaults(handler=_handle_auth_whoami)
|
|
534
534
|
|
|
535
|
-
|
|
536
|
-
|
|
535
|
+
_CAN_I_ACTIONS = [
|
|
536
|
+
"Select", "Describe", "Alter", "Update", "Drop", "Download", "All",
|
|
537
|
+
"Read", "Write", "List",
|
|
538
|
+
"CreateTable", "CreateInstance", "CreateFunction", "CreateResource",
|
|
539
|
+
"Execute", "Delete",
|
|
540
|
+
]
|
|
541
|
+
_CAN_I_ACTIONS_LOWER = {a.lower(): a for a in _CAN_I_ACTIONS}
|
|
542
|
+
_CAN_I_OBJECT_TYPES = ["Table", "Project", "Function", "Resource", "Instance"]
|
|
543
|
+
_CAN_I_OBJECT_TYPES_LOWER = {t.lower(): t for t in _CAN_I_OBJECT_TYPES}
|
|
544
|
+
|
|
545
|
+
auth_can_i = _make_parser(
|
|
546
|
+
auth_subparsers, "can-i", "auth.can-i",
|
|
547
|
+
help="Check whether current user has a specific permission on an object",
|
|
548
|
+
description=(
|
|
549
|
+
"Check whether the current user has a specific permission on an ODPS object.\n"
|
|
550
|
+
"\n"
|
|
551
|
+
"Examples:\n"
|
|
552
|
+
" maxc auth can-i --table my_table --operation Select\n"
|
|
553
|
+
" maxc auth can-i --table my_table --operation Update --project other_proj\n"
|
|
554
|
+
" maxc auth can-i --object my_proj --type Project --operation CreateTable\n"
|
|
555
|
+
" maxc auth can-i --object my_proj --type Project --operation CreateInstance\n"
|
|
556
|
+
),
|
|
557
|
+
formatter_class=AliyunRawTextFormatter,
|
|
558
|
+
)
|
|
559
|
+
auth_can_i.add_argument("--object", "--table", required=True, dest="object_name",
|
|
560
|
+
help="Object name to check (table name, or project name when --type=Project)")
|
|
561
|
+
auth_can_i.add_argument(
|
|
562
|
+
"--type",
|
|
563
|
+
default="Table",
|
|
564
|
+
type=lambda s: _CAN_I_OBJECT_TYPES_LOWER.get(s.lower(), s),
|
|
565
|
+
choices=_CAN_I_OBJECT_TYPES,
|
|
566
|
+
help=(
|
|
567
|
+
"Object type (case-insensitive, default: Table). "
|
|
568
|
+
"Table | Project | Function | Resource | Instance."
|
|
569
|
+
),
|
|
570
|
+
)
|
|
537
571
|
auth_can_i.add_argument(
|
|
538
572
|
"--operation",
|
|
539
573
|
required=True,
|
|
540
|
-
type=lambda s: s.
|
|
541
|
-
choices=
|
|
542
|
-
help=
|
|
574
|
+
type=lambda s: _CAN_I_ACTIONS_LOWER.get(s.lower(), s),
|
|
575
|
+
choices=_CAN_I_ACTIONS,
|
|
576
|
+
help=(
|
|
577
|
+
"Permission to check (case-insensitive). "
|
|
578
|
+
"Table: Select, Describe, Alter, Update, Drop, Download, All. "
|
|
579
|
+
"Project: CreateTable, CreateInstance, CreateFunction, CreateResource, List, Read, Write, All. "
|
|
580
|
+
"Function: Read, Write, Delete, Execute, All. "
|
|
581
|
+
"Resource: Read, Write, Delete, All. "
|
|
582
|
+
"Instance: Read, Write, All."
|
|
583
|
+
),
|
|
543
584
|
)
|
|
544
|
-
auth_can_i.add_argument("--project", help="
|
|
585
|
+
auth_can_i.add_argument("--project", help="Project where the object lives (default: current project)")
|
|
545
586
|
auth_can_i.add_argument("--json", action="store_true", help="Output as JSON envelope")
|
|
546
587
|
auth_can_i.set_defaults(handler=_handle_auth_can_i)
|
|
547
588
|
|
|
@@ -802,13 +843,13 @@ def _is_json_mode(args: argparse.Namespace) -> bool:
|
|
|
802
843
|
|
|
803
844
|
|
|
804
845
|
def _build_permission_denied_hints(app: MaxCApp | None) -> AgentHints:
|
|
805
|
-
"""Build PERMISSION_DENIED agent hints, suggesting _dev
|
|
846
|
+
"""Build PERMISSION_DENIED agent hints, suggesting _dev project switch when appropriate."""
|
|
806
847
|
actions = []
|
|
807
848
|
project = app.config.default_project if app else None
|
|
808
849
|
if project and not project.endswith("_dev"):
|
|
809
850
|
actions.append(SuggestedAction(
|
|
810
851
|
id="session.set",
|
|
811
|
-
title="Switch to dev
|
|
852
|
+
title="Switch to dev project",
|
|
812
853
|
command=f"maxc session set --project {project}_dev --json",
|
|
813
854
|
))
|
|
814
855
|
actions.append(action("query", metadata={"sql_executed": "SELECT 1"}))
|
|
@@ -1454,7 +1495,8 @@ def _handle_auth_whoami(app: MaxCApp, args: argparse.Namespace, stdout: TextIO)
|
|
|
1454
1495
|
|
|
1455
1496
|
def _handle_auth_can_i(app: MaxCApp, args: argparse.Namespace, stdout: TextIO) -> None:
|
|
1456
1497
|
envelope = app.auth_can_i(
|
|
1457
|
-
|
|
1498
|
+
object_name=args.object_name,
|
|
1499
|
+
object_type=args.type,
|
|
1458
1500
|
operation=args.operation,
|
|
1459
1501
|
project=args.project,
|
|
1460
1502
|
)
|
|
@@ -1039,12 +1039,12 @@ def classify_sql_error(message: str) -> dict[str, Any]:
|
|
|
1039
1039
|
return {"error_type": "unknown"}
|
|
1040
1040
|
|
|
1041
1041
|
|
|
1042
|
-
def
|
|
1043
|
-
"""Return a hint about switching to the _dev
|
|
1042
|
+
def _dev_project_hint(project: str | None) -> str:
|
|
1043
|
+
"""Return a hint about switching to the _dev project if the current project is production."""
|
|
1044
1044
|
if project and not project.endswith("_dev"):
|
|
1045
1045
|
return (
|
|
1046
|
-
f"Current project '{project}' is a production
|
|
1047
|
-
f"Personal accounts usually only have access to the dev
|
|
1046
|
+
f"Current project '{project}' is a production project. "
|
|
1047
|
+
f"Personal accounts usually only have access to the dev project (_dev). "
|
|
1048
1048
|
f"Try switching: maxc session set --project {project}_dev"
|
|
1049
1049
|
)
|
|
1050
1050
|
return ""
|
|
@@ -1063,7 +1063,7 @@ def _build_permission_error(
|
|
|
1063
1063
|
preserved as the error message for diagnostics. Context-specific
|
|
1064
1064
|
guidance goes into the suggestion field.
|
|
1065
1065
|
"""
|
|
1066
|
-
dev_hint =
|
|
1066
|
+
dev_hint = _dev_project_hint(project_name)
|
|
1067
1067
|
|
|
1068
1068
|
if context == "list_projects" and project_name:
|
|
1069
1069
|
suggestion = f"Verify that your account has `odps:Read` on project {project_name}, or contact the project owner."
|
|
@@ -11,7 +11,6 @@ Use the live CLI instead of inventing a separate MaxCompute adapter. Prefer `{{c
|
|
|
11
11
|
|
|
12
12
|
- First-time setup or repair of Python or `maxc-cli`
|
|
13
13
|
- Auth bootstrap or identity inspection (AK/SK or env vars)
|
|
14
|
-
- Migrating from odpscmd (reusing existing ODPS Console credentials)
|
|
15
14
|
- Session project or schema overrides
|
|
16
15
|
- Metadata discovery, schema inspection, fast table/column search
|
|
17
16
|
- Read-only query execution or job tracking
|
|
@@ -43,9 +42,9 @@ These are non-negotiable. See [references/red-lines.md](references/red-lines.md)
|
|
|
43
42
|
|
|
44
43
|
1. **Always use `--json`** for machine work. Use `--format markdown` for user-facing output, `--format brief` in token-tight contexts. `--json` is shorthand for `--format json`. **`--format` is a top-level flag — it must come before the subcommand**: `{{cli}} --format markdown query "SELECT 1"` (✓), not `{{cli}} query "SELECT 1" --format markdown` (✗). `--json` may appear anywhere because each subcommand also accepts it.
|
|
45
44
|
2. **Never invent names** — table, schema, project, or endpoint. Verify with `meta` commands and `auth whoami`.
|
|
46
|
-
3. **Default to `--project` for the user's target
|
|
47
|
-
4. **
|
|
48
|
-
5. **Never re-prompt for credentials** when `auth whoami` shows `authenticated=true`. Permission errors are almost always a
|
|
45
|
+
3. **Default to `--project` for the user's target project.** The configured project (in `~/.maxc/config.yaml`) is the user's **dev project** — the data they actually want to query usually lives in a *different* project (often the corresponding production one). When the user mentions a table/project without specifying which environment, **ask first**, then pass `--project <name>` on every meta/data command and use `project.table` in SQL.
|
|
46
|
+
4. **Project naming convention is a fixed pair:** `<name>_dev` is the dev project; the same `<name>` **without** the `_dev` suffix is its corresponding **production** project. Together they form one DataWorks workspace. They share metadata structure but hold different data and different permissions. See Dev vs Production Projects below.
|
|
47
|
+
5. **Never re-prompt for credentials** when `auth whoami` shows `authenticated=true`. Permission errors are almost always a project environment issue (dev vs prod), not a credential issue.
|
|
49
48
|
6. **Always discover partitions** via `meta latest-partition` before querying partitioned tables. Format varies per table.
|
|
50
49
|
7. **Always read `error.suggestion`** before retrying a failed command. Same input → same error.
|
|
51
50
|
8. **Never install or upgrade Python** without explicit user confirmation.
|
|
@@ -56,8 +55,7 @@ These are non-negotiable. See [references/red-lines.md](references/red-lines.md)
|
|
|
56
55
|
When `auth whoami --json` returns `configured=false` (no auth set up), follow [references/bootstrap-flow.md](references/bootstrap-flow.md) step by step. Three principles:
|
|
57
56
|
|
|
58
57
|
1. **Never pick the auth method yourself** — always ask the user to choose between AK/SK and environment variables.
|
|
59
|
-
2. **If the user already
|
|
60
|
-
3. **If `auth whoami` shows `auth_type=external`, the user is on an externally-managed credential provider — do NOT modify the auth config.** Treat the bootstrap as already done. Only `project`/`endpoint`/`schema` are safe to change (via `session set` or by re-running `auth login-external` with the same `--process-command`).
|
|
58
|
+
2. **If `auth whoami` shows `auth_type=external`, the user is on an externally-managed credential provider — do NOT modify the auth config.** Treat the bootstrap as already done. Only `project`/`endpoint`/`schema` are safe to change (via `session set` or by re-running `auth login-external` with the same `--process-command`).
|
|
61
59
|
|
|
62
60
|
## First Pass
|
|
63
61
|
|
|
@@ -87,38 +85,38 @@ Key paths:
|
|
|
87
85
|
|
|
88
86
|
See [references/json-output-format.md](references/json-output-format.md) for full examples and [references/command-patterns.md](references/command-patterns.md) §JSON Contract for all data shapes.
|
|
89
87
|
|
|
90
|
-
## Dev vs Production
|
|
88
|
+
## Dev vs Production Projects
|
|
91
89
|
|
|
92
|
-
|
|
90
|
+
A single **DataWorks workspace** corresponds to **two MaxCompute projects**: a dev project and a production project. Confusing the two is the #1 source of permission errors.
|
|
93
91
|
|
|
94
92
|
### The naming pair (memorize this)
|
|
95
93
|
|
|
96
|
-
|
|
|
97
|
-
|
|
94
|
+
| Project type | Name pattern | Example | Who can access | What lives there |
|
|
95
|
+
|----------------|--------------|------------------|----------------|------------------|
|
|
98
96
|
| **Dev** | `<name>_dev` | `my_project_dev` | Personal accounts (the user themselves) | Test data, scratch tables, the user's own work |
|
|
99
97
|
| **Production** | `<name>` | `my_project` | Usually only service accounts / DataWorks pipelines | The real business data the user actually wants to query |
|
|
100
98
|
|
|
101
|
-
|
|
99
|
+
Both projects belong to the same DataWorks workspace (`my_project`). They **share metadata structure but hold different data**. A table that exists in `my_project_dev` almost always exists in `my_project` too — but the rows, partitions, and freshness will differ.
|
|
102
100
|
|
|
103
101
|
### Other key facts
|
|
104
102
|
|
|
105
|
-
- The project configured in `~/.maxc/config.yaml` or env vars is always the **dev**
|
|
106
|
-
- Personal accounts usually only have *write* access to dev and *read* access to production (varies by org policy). Pointing a session directly at production often results in `PERMISSION_DENIED`.
|
|
107
|
-
- `--project` is the canonical way to access **another project's** tables — most often the corresponding production
|
|
108
|
-
- When the user asks about a table without naming the
|
|
103
|
+
- The project configured in `~/.maxc/config.yaml` or env vars is always the **dev project** — this is the user's home project.
|
|
104
|
+
- Personal accounts usually only have *write* access to dev and *read* access to production (varies by org policy). Pointing a session directly at the production project often results in `PERMISSION_DENIED`.
|
|
105
|
+
- `--project` is the canonical way to access **another project's** tables — most often the corresponding production project, occasionally a different team's project.
|
|
106
|
+
- When the user asks about a table without naming the project, **ask whether they mean the dev or production copy** before guessing.
|
|
109
107
|
|
|
110
|
-
### How to tell which
|
|
108
|
+
### How to tell which project you are in
|
|
111
109
|
|
|
112
110
|
```bash
|
|
113
111
|
{{cli}} auth whoami --json # check data.identity.project — ends with _dev?
|
|
114
112
|
{{cli}} session show --json # check current session project
|
|
115
113
|
```
|
|
116
114
|
|
|
117
|
-
If the project name does NOT end with `_dev`, you may be pointed at
|
|
115
|
+
If the project name does NOT end with `_dev`, you may be pointed at the production project by mistake.
|
|
118
116
|
|
|
119
|
-
### Accessing production tables from dev
|
|
117
|
+
### Accessing production tables from dev project
|
|
120
118
|
|
|
121
|
-
Use `--project` to read metadata from the production
|
|
119
|
+
Use `--project` to read metadata from the production project without switching session:
|
|
122
120
|
|
|
123
121
|
```bash
|
|
124
122
|
{{cli}} meta list-tables --project my_project --json
|
|
@@ -126,10 +124,10 @@ Use `--project` to read metadata from the production workspace without switching
|
|
|
126
124
|
{{cli}} data sample my_table --project my_project --json
|
|
127
125
|
```
|
|
128
126
|
|
|
129
|
-
When writing SQL, use `project.table` format to reference tables in another
|
|
127
|
+
When writing SQL, use `project.table` format to reference tables in another project:
|
|
130
128
|
|
|
131
129
|
```sql
|
|
132
|
-
-- From dev
|
|
130
|
+
-- From dev project, query a production table
|
|
133
131
|
SELECT * FROM my_project.my_table WHERE ds = '20260418' LIMIT 100
|
|
134
132
|
```
|
|
135
133
|
|
|
@@ -139,7 +137,7 @@ Do NOT use bare table names (`FROM my_table`) when the target table lives in a d
|
|
|
139
137
|
|
|
140
138
|
| Scenario | Symptom | Fix |
|
|
141
139
|
|----------|---------|-----|
|
|
142
|
-
| Config points to production
|
|
140
|
+
| Config points to production project | `PERMISSION_DENIED` on most operations | `{{cli}} session set --project my_project_dev` |
|
|
143
141
|
| Need to read production table metadata | `PERMISSION_DENIED` on `meta describe` | `{{cli}} meta describe my_table --project my_project --json` |
|
|
144
142
|
| SQL references a table in another project without project prefix | `TABLE_NOT_FOUND` | Use `project.table` format in SQL |
|
|
145
143
|
| Mixed access: dev metadata + production data | Confusing results | Be explicit: use `--project` for metadata commands, `project.table` in SQL |
|
|
@@ -227,7 +225,6 @@ For full command syntax and options, see [references/command-patterns.md](refere
|
|
|
227
225
|
| Check permission for an op | `{{cli}} auth can-i --table T --operation SELECT --json` |
|
|
228
226
|
| Diagnose a failed job | `{{cli}} job diagnose <id> --json` |
|
|
229
227
|
| Add semantic metadata to a table | `{{cli}} meta semantic set T ... --json` (see command-patterns.md §Semantic Metadata) |
|
|
230
|
-
| Migrate from odpscmd | See [references/migrate-from-odpscmd.md](references/migrate-from-odpscmd.md) |
|
|
231
228
|
| Plan NL→SQL before writing | See [references/text2sql-principles.md](references/text2sql-principles.md) |
|
|
232
229
|
| Look up MaxCompute SQL dialect rule | See [references/maxcompute-select-guide.md](references/maxcompute-select-guide.md) |
|
|
233
230
|
| Pick a query template (Top-N, PIVOT, retention, …) | See [references/sql-query-patterns.md](references/sql-query-patterns.md) |
|
|
@@ -260,7 +257,6 @@ For full command syntax and options, see [references/command-patterns.md](refere
|
|
|
260
257
|
| [bootstrap-flow.md](references/bootstrap-flow.md) | First-time setup or `configured=false` |
|
|
261
258
|
| [setup-install.md](references/setup-install.md) | Python / maxc-cli install detail |
|
|
262
259
|
| [bootstrap-auth.md](references/bootstrap-auth.md) | Per-method auth setup (AK/SK, env vars) |
|
|
263
|
-
| [migrate-from-odpscmd.md](references/migrate-from-odpscmd.md) | User has `odpscmd` configured |
|
|
264
260
|
| [command-patterns.md](references/command-patterns.md) | Full command syntax, output shapes, semantic, multi-project, schema, async |
|
|
265
261
|
| [json-output-format.md](references/json-output-format.md) | JSON envelope examples |
|
|
266
262
|
| [partition-guide.md](references/partition-guide.md) | Partition naming, MAX_PT() guidance, ambiguity |
|
|
@@ -28,10 +28,6 @@ If the console script is not on `PATH`, use:
|
|
|
28
28
|
|
|
29
29
|
In sandboxes or CI, make sure those paths are writable.
|
|
30
30
|
|
|
31
|
-
### Migrating From odpscmd
|
|
32
|
-
|
|
33
|
-
If the user already has `odpscmd` configured, reuse their credentials — see [migrate-from-odpscmd.md](migrate-from-odpscmd.md) for the field mapping and config conversion steps.
|
|
34
|
-
|
|
35
31
|
---
|
|
36
32
|
|
|
37
33
|
## Step 1: Check Current Auth Status
|
|
@@ -7,7 +7,7 @@ When `auth whoami --json` returns `configured=false`, follow the three phases be
|
|
|
7
7
|
```
|
|
8
8
|
Phase 1: Prerequisites → setup-install.md
|
|
9
9
|
↓
|
|
10
|
-
Phase 2: Auth → bootstrap-auth.md
|
|
10
|
+
Phase 2: Auth → bootstrap-auth.md
|
|
11
11
|
↓
|
|
12
12
|
Phase 3: Verify → {{cli}} auth whoami --json
|
|
13
13
|
```
|
|
@@ -51,25 +51,17 @@ Then jump to the matching path in [bootstrap-auth.md](bootstrap-auth.md):
|
|
|
51
51
|
|
|
52
52
|
If `auth whoami --json` shows `auth_type=external` (or `provider: external` in the saved config), the user is on an externally-managed credential provider. **Do not run Phase 2.** The auth is already set up — only `project`/`endpoint`/`schema` are safe to change via `session set` or by re-running the original `auth login-external` with updated `--project`/`--endpoint`. Treat bootstrap as complete and move to Phase 3.
|
|
53
53
|
|
|
54
|
-
### Shortcut for users with `odpscmd` already configured
|
|
55
|
-
|
|
56
|
-
Before asking the question above, check whether the user already has `odpscmd` set up. If yes:
|
|
57
|
-
|
|
58
|
-
> "Do you already have `odpscmd` configured? We can migrate the existing credentials without you re-entering anything."
|
|
59
|
-
|
|
60
|
-
Then follow [migrate-from-odpscmd.md](migrate-from-odpscmd.md).
|
|
61
|
-
|
|
62
54
|
### Always confirm project and endpoint
|
|
63
55
|
|
|
64
56
|
Regardless of method, ask the user explicitly for `project` and `endpoint`. If a value is already in the config or env, present it as a default but require confirmation. See [bootstrap-auth.md](bootstrap-auth.md) §"Always ask for project and endpoint".
|
|
65
57
|
|
|
66
|
-
### Dev vs production
|
|
58
|
+
### Dev vs production project check
|
|
67
59
|
|
|
68
60
|
If the project name does **not** end with `_dev`, warn the user:
|
|
69
61
|
|
|
70
|
-
> "Project `<project>` does not end with `_dev`. Personal accounts usually only have access to dev
|
|
62
|
+
> "Project `<project>` does not end with `_dev`. Personal accounts usually only have access to dev projects — would you like to switch to `<project>_dev`?"
|
|
71
63
|
|
|
72
|
-
See SKILL.md §"Dev vs Production
|
|
64
|
+
See SKILL.md §"Dev vs Production Projects" for the full rationale.
|
|
73
65
|
|
|
74
66
|
---
|
|
75
67
|
|
|
@@ -100,5 +92,4 @@ Expected: `data.identity.authenticated=true`. `validation_status` interpretation
|
|
|
100
92
|
|
|
101
93
|
- Step-by-step Python / maxc-cli installation — see [setup-install.md](setup-install.md).
|
|
102
94
|
- Each auth method's exact CLI flags and saved YAML — see [bootstrap-auth.md](bootstrap-auth.md).
|
|
103
|
-
- odpscmd field-by-field migration — see [migrate-from-odpscmd.md](migrate-from-odpscmd.md).
|
|
104
95
|
- Public cloud endpoint catalog — present in [bootstrap-auth.md](bootstrap-auth.md) Path A.
|
|
@@ -343,7 +343,7 @@ fi
|
|
|
343
343
|
# NOT_FOUND → search for the correct name
|
|
344
344
|
{{cli}} meta search "partial_name" --json
|
|
345
345
|
|
|
346
|
-
# PERMISSION_DENIED → check permissions (often a
|
|
346
|
+
# PERMISSION_DENIED → check permissions (often a dev vs prod project issue)
|
|
347
347
|
{{cli}} auth can-i --table your_table --operation SELECT --json
|
|
348
348
|
|
|
349
349
|
# JOB_TIMEOUT → check status and continue waiting
|
|
@@ -13,7 +13,7 @@ Cross-referenced with SKILL.md §Core Principles (which lists the highest-priori
|
|
|
13
13
|
| 1 | Always use `--json` for machine-driven work | Plain output is for humans; agents must parse the envelope |
|
|
14
14
|
| 2 | Never invent table, schema, project, or endpoint names | Always verify with `meta search` / `meta list-tables` / `auth whoami` |
|
|
15
15
|
| 3 | Never install or upgrade Python without explicit user confirmation | System-level change with broad impact |
|
|
16
|
-
| 4 | Never re-prompt for credentials when `auth whoami` shows `authenticated=true` | Permission errors are almost always a
|
|
16
|
+
| 4 | Never re-prompt for credentials when `auth whoami` shows `authenticated=true` | Permission errors are almost always a dev vs production project issue, not a credential issue — see SKILL.md §Dev vs Production Projects |
|
|
17
17
|
| 5 | Always check partition value via `meta latest-partition` before querying partitioned tables | Hardcoded partitions go stale; format varies per table |
|
|
18
18
|
| 6 | Always use schema-qualified (`schema.table`) or `project.table` names in SQL | Bare names fail across schemas/projects |
|
|
19
19
|
| 7 | Never log, echo, or include AK/SK in output | Even in error context |
|
|
@@ -37,7 +37,7 @@ Cross-referenced with SKILL.md §Core Principles (which lists the highest-priori
|
|
|
37
37
|
| Running a query without checking cost first | Use `query cost`, or `--cost-check N` to auto-abort |
|
|
38
38
|
| Ignoring `agent_hints.warnings` in the response | They surface backend issues, cache staleness, cost alerts |
|
|
39
39
|
| Assuming `meta describe` data is live | Cache may be stale; check `metadata.source` and warnings |
|
|
40
|
-
| Using a production project name as default | See SKILL.md §Dev vs Production
|
|
40
|
+
| Using a production project name as default | See SKILL.md §Dev vs Production Projects |
|
|
41
41
|
| Querying partitioned table without partition filter | Always run `meta latest-partition` first; use the exact returned value in WHERE |
|
|
42
42
|
|
|
43
43
|
## Agent Anti-Patterns
|
|
@@ -64,7 +64,7 @@ When `status=failure`, inspect `error.code` and follow the recovery action. Alwa
|
|
|
64
64
|
| `COLUMN_NOT_FOUND` | Column reference does not exist | Check `error.available`; run `meta describe <table> --json` |
|
|
65
65
|
| `WRITE_OPERATION_REQUIRES_FORCE` | SQL DDL/DML blocked by read-only mode | Read-only is by design; use `--force` only if authorized. Does NOT apply to `data upload` (Tunnel-based write, no `--force` needed). |
|
|
66
66
|
| `CSV_PARSE_ERROR` | A CSV cell could not be parsed against the column type during `data upload` | Read `error.context.line` (1-based row number, including header if present) and `error.context.column` (the column **name** as a string, not a position index) to find the bad cell; fix the source CSV and retry. The Tunnel session is aborted, nothing is committed. |
|
|
67
|
-
| `PERMISSION_DENIED` | No access to the resource | Almost always dev vs production
|
|
67
|
+
| `PERMISSION_DENIED` | No access to the resource | Almost always dev vs production project issue — see SKILL.md §Dev vs Production Projects |
|
|
68
68
|
| `SQL_ERROR` | SQL syntax or execution error | Fix the SQL; use `query explain` to validate first |
|
|
69
69
|
| `COST_LIMIT_EXCEEDED` | Cost exceeds `--cost-check` threshold | Add partition filters, reduce columns, or raise the threshold |
|
|
70
70
|
| `BACKEND_CONNECTION_ERROR` | Network or service unavailable | Retry after a delay; check endpoint with `auth whoami --json` |
|
|
@@ -46,7 +46,6 @@ src/maxc_cli/skills/references/command-patterns.md
|
|
|
46
46
|
src/maxc_cli/skills/references/json-output-format.md
|
|
47
47
|
src/maxc_cli/skills/references/maxcompute-select-guide.md
|
|
48
48
|
src/maxc_cli/skills/references/maxcompute-sql-notes.md
|
|
49
|
-
src/maxc_cli/skills/references/migrate-from-odpscmd.md
|
|
50
49
|
src/maxc_cli/skills/references/partition-guide.md
|
|
51
50
|
src/maxc_cli/skills/references/red-lines.md
|
|
52
51
|
src/maxc_cli/skills/references/setup-install.md
|
|
@@ -90,4 +89,5 @@ tests/test_query_auto_promote.py
|
|
|
90
89
|
tests/test_query_result_csv_fallback.py
|
|
91
90
|
tests/test_setting_parser.py
|
|
92
91
|
tests/test_skill_cli_consistency.py
|
|
92
|
+
tests/test_skill_eval.py
|
|
93
93
|
tests/test_skill_renderer.py
|
|
@@ -333,7 +333,7 @@ class TestAgentInstallSkill:
|
|
|
333
333
|
def test_install_skill_next_step_hint(self, tmp_path):
|
|
334
334
|
config = _make_config(tmp_path)
|
|
335
335
|
_, payload, _ = _run_cmd(config, ["agent", "skill", "install", "claude-code", "--json"])
|
|
336
|
-
assert "
|
|
336
|
+
assert "auto-discovered" in payload["data"]["next_step"]
|
|
337
337
|
|
|
338
338
|
_, payload, _ = _run_cmd(config, ["agent", "skill", "install", "cursor", "--json"])
|
|
339
339
|
assert "Restart" in payload["data"]["next_step"]
|
|
@@ -245,14 +245,14 @@ class TestAuth:
|
|
|
245
245
|
"--table",
|
|
246
246
|
test_table,
|
|
247
247
|
"--operation",
|
|
248
|
-
"
|
|
248
|
+
"Select",
|
|
249
249
|
"--json",
|
|
250
250
|
]
|
|
251
251
|
)
|
|
252
252
|
assert code == 0, f"命令失败: {stderr}"
|
|
253
253
|
assert data["status"] == "success"
|
|
254
254
|
authorization = _payload_data(data)["authorization"]
|
|
255
|
-
assert authorization["
|
|
255
|
+
assert authorization["object_name"] == test_table
|
|
256
256
|
assert "allowed" in authorization
|
|
257
257
|
|
|
258
258
|
|