kcode-pi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -0
- package/dist/cli/kcode.d.ts +15 -0
- package/dist/cli/kcode.js +153 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +7 -0
- package/docs/KCODE_DISTRIBUTION.md +91 -0
- package/extensions/kingdee-harness.ts +180 -0
- package/extensions/kingdee-header.ts +122 -0
- package/extensions/kingdee-tools.ts +379 -0
- package/knowledge/.backup/v1.0.0/version.json +10 -0
- package/knowledge/cangqiong/product-notes.md +15 -0
- package/knowledge/common/business-flows.md +115 -0
- package/knowledge/common/config-guides.md +110 -0
- package/knowledge/common/error-patterns.md +170 -0
- package/knowledge/common/implementation.md +144 -0
- package/knowledge/cosmic/hard-constraints.md +38 -0
- package/knowledge/cosmic/ksql-datafix.md +34 -0
- package/knowledge/cosmic/platform-baseline.md +32 -0
- package/knowledge/cosmic/plugin-decision-matrix.md +40 -0
- package/knowledge/cosmic/review-checklist.md +40 -0
- package/knowledge/cosmic/unittest.md +35 -0
- package/knowledge/enterprise/api-reference.md +186 -0
- package/knowledge/enterprise/code-patterns.md +217 -0
- package/knowledge/enterprise/plugin-lifecycle.md +188 -0
- package/knowledge/enterprise/tables.json +159 -0
- package/knowledge/flagship/api-reference.md +237 -0
- package/knowledge/flagship/code-patterns.md +246 -0
- package/knowledge/flagship/cosmic-platform-note.md +15 -0
- package/knowledge/flagship/plugin-lifecycle.md +248 -0
- package/knowledge/flagship/tables.json +159 -0
- package/knowledge/version.json +10 -0
- package/knowledge/xinghan/product-notes.md +15 -0
- package/package.json +71 -0
- package/prompts/kd-discuss.md +11 -0
- package/prompts/kd-execute.md +12 -0
- package/prompts/kd-plan.md +12 -0
- package/prompts/kd-ship.md +12 -0
- package/prompts/kd-spec.md +12 -0
- package/prompts/kd-verify.md +12 -0
- package/skills/kd-check/SKILL.md +26 -0
- package/skills/kd-cosmic-dev/SKILL.md +82 -0
- package/skills/kd-cosmic-review/SKILL.md +90 -0
- package/skills/kd-cosmic-unittest/SKILL.md +92 -0
- package/skills/kd-debug/SKILL.md +30 -0
- package/skills/kd-discuss/SKILL.md +24 -0
- package/skills/kd-execute/SKILL.md +22 -0
- package/skills/kd-gen/SKILL.md +34 -0
- package/skills/kd-ksql/SKILL.md +86 -0
- package/skills/kd-plan/SKILL.md +24 -0
- package/skills/kd-ship/SKILL.md +22 -0
- package/skills/kd-spec/SKILL.md +24 -0
- package/skills/kd-verify/SKILL.md +22 -0
- package/themes/kcode-dark.json +81 -0
- package/vendor/kingdee-skills/cosmic-unittest/SKILL.md +788 -0
- package/vendor/kingdee-skills/cosmic-unittest/author-cache.json +5 -0
- package/vendor/kingdee-skills/cosmic-unittest/cosmic-unittest-skill-overview.html +746 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/business-test.md +205 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/common-test.md +257 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/formplugin-test.md +560 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/op-plugin-test.md +231 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/validator-test.md +232 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/business-helper.md +184 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/common-module.md +355 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/convert-plugin.md +130 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/formplugin.md +235 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/op-plugin.md +226 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/validator.md +206 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/SKILL.md +674 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/advanced-scenario-checklist.md +307 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/algox-performance-checklist.md +129 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/coding-standard-checklist.md +491 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/cosmic-api-checklist.md +285 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-access-checklist.md +261 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-transaction-checklist.md +390 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/domain-logic-checklist.md +295 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/form-plugin-checklist.md +508 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/infra-checklist.md +254 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ksql-checklist.md +305 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/lifecycle-checklist.md +298 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/operation-plugin-checklist.md +442 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/test-mock-checklist.md +120 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ui-performance-checklist.md +320 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/pattern-matcher.py +336 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/review-score-calculator.py +121 -0
- package/vendor/kingdee-skills/ok-cosmic/CHANGELOG.md +295 -0
- package/vendor/kingdee-skills/ok-cosmic/README.md +460 -0
- package/vendor/kingdee-skills/ok-cosmic/SKILL.md +287 -0
- package/vendor/kingdee-skills/ok-cosmic/agents/openai.yaml +17 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BatchImportPluginTemplate.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BillPlugInTemplate.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ConvertPlugInTemplate.java +255 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/FormPluginTemplate.java +597 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/IWorkflowPluginTemplate.java +91 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ListPluginTemplate.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpPluginTemplate.java +201 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpenApiControllerTemplate.java +103 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/PrintPluginTemplate.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportFormPluginTemplate.java +257 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportListDataPluginTemplate.java +70 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/StandardTreeListPluginTemplate.java +130 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TaskTemplate.java +80 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TreeListPluginTemplate.java +152 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/WriteBackPlugInTemplate.java +286 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/attachment/AttachmentUploadBindSample.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/BotpTracePushSample.java +168 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/SampleConvertPlugin.java +223 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/cache/SampleCacheUsage.java +218 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/concurrent/SampleThreadPoolBatch.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectCrudSample.java +205 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectOpsSample.java +100 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/BeforeOperationConfirmSample.java +217 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ConfirmDialogSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/EntryRowCalculateSample.java +116 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/F7FilterSample.java +134 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/GetAndSetValueSample.java +176 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/HyperlinkJumpSample.java +124 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/OpenBillModalSample.java +253 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ReturnParentDataSample.java +295 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/TreeControlSample.java +140 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ViewControlOpsSample.java +132 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPluginBasicSample.java +170 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPreOpenFilterSample.java +68 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/message/MessageNotifySample.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/SampleMQConsumer.java +198 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/sample_mq.xml +15 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OpAddValidatorsSample.java +137 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OperationOptionBridgeSample.java +228 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/package-info.java +19 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BaseDataQuerySample.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BatchQuerySample.java +368 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/DataSetQueryStatSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportFormPlugin.java +179 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportListDataPlugin.java +616 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/snippets-guide.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/task/ScheduleTaskSample.java +160 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/workflow/SampleWorkflowPlugin.java +302 -0
- package/vendor/kingdee-skills/ok-cosmic/manifest.json +78 -0
- package/vendor/kingdee-skills/ok-cosmic/ok-cosmic-intro.html +903 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/attachment-api.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/botp-convert.md +98 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/dynamic-object.md +113 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/entity-metadata.md +123 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/event-lifecycle.md +184 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/flex-prop.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/form-utils.md +133 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/operate-chain.md +159 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/plugin-base.md +218 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/query-dataset.md +149 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/request-context.md +88 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/view-handler.md +157 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-bill.md +76 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-botp.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-form.md +165 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-import.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-list.md +227 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-openapi.md +112 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-operation.md +135 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-print.md +65 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-data.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-form.md +90 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-task.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-tree-list.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-workflow.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-writeback.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-algo.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-cache.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-model-svc.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-object.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-entity-model.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-exception.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-file.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-id.md +47 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-lock.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-log.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-network-control.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-orm-access.md +78 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-request-context.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-threadpool.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-tx.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-utils.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/requirements.txt +2 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/a-layer-rules.json +24 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/anti-patterns.md +48 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/cheat-sheet.md +256 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/coding-preferences.md +140 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/constraints.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/decision-matrix.md +222 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/intent-routing.md +94 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/platform-baseline.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/post-check.md +109 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/config_loader.py +204 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-api-knowledge.py +910 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-basedata-query.py +359 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-config-check.py +181 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-extpoints-query.py +389 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-form-metadata.py +856 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-check.py +262 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-lint.py +293 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/__init__.py +2 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/base.py +393 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/resource_check.py +176 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/scene_check.py +375 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/style_check.py +434 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/verify_check.py +36 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/route_client.py +186 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/script_utils.py +40 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/sqlite_cache.py +142 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-commons.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-features.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic-docs.db +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic.json +13 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-mac.sh +18 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-windows.bat +53 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup.jar +0 -0
- package/vendor/kingdee-skills/ok-ksql/SKILL.md +81 -0
- package/vendor/kingdee-skills/ok-ksql/agents/openai.yaml +7 -0
- package/vendor/kingdee-skills/ok-ksql/manifest.json +14 -0
- package/vendor/kingdee-skills/ok-ksql/references/ksql-datafix.md +452 -0
- package/vendor/kingdee-skills/ok-ksql/scripts/ksql_lint.py +363 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: NOASSERTION
|
|
3
|
+
"""
|
|
4
|
+
cosmic-basedata-query.py — Cosmic base data query and cache tool.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python3 cosmic-basedata-query.py --config ok-cosmic.json get --entity-id bd_supplier
|
|
8
|
+
python3 cosmic-basedata-query.py --config ok-cosmic.json get --entity-id bd_supplier --number-or-name 1001
|
|
9
|
+
python3 cosmic-basedata-query.py --config ok-cosmic.json get --entity-id bd_supplier --number-or-name 华东 --full
|
|
10
|
+
python3 cosmic-basedata-query.py --config ok-cosmic.json get --entity-id bd_supplier --refresh
|
|
11
|
+
python3 cosmic-basedata-query.py --config ok-cosmic.json get --entity-id bd_supplier --json
|
|
12
|
+
|
|
13
|
+
What it provides:
|
|
14
|
+
1. Query one base data result by entityId plus number/name keyword
|
|
15
|
+
2. Local SQLite cache in a dedicated table with a fixed 10-minute TTL
|
|
16
|
+
3. Config-first API resolution with environment-variable fallback
|
|
17
|
+
4. Human-readable summary output with optional JSON mode
|
|
18
|
+
|
|
19
|
+
What it does NOT do:
|
|
20
|
+
- It does not infer entityId from Chinese labels or form field names
|
|
21
|
+
- It only accepts entityId that is already confirmed by the user or by the metadata script
|
|
22
|
+
- It does not auto-query metadata; when entityId is unknown, query metadata for refType or look up the real English identifier by Chinese name first
|
|
23
|
+
- It does not batch query multiple entityIds in one request
|
|
24
|
+
|
|
25
|
+
Prerequisites:
|
|
26
|
+
- A valid ok-cosmic.json or equivalent project config
|
|
27
|
+
- A configured graph.dbPath for SQLite cache storage
|
|
28
|
+
- A reachable route.apiUrl (`runtime/route`) configured by config or environment
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import argparse
|
|
32
|
+
import json
|
|
33
|
+
import sys
|
|
34
|
+
from typing import Any, Dict, List, Optional
|
|
35
|
+
|
|
36
|
+
from config_loader import load_project_config
|
|
37
|
+
from route_client import RouteClient, unwrap_route_raw
|
|
38
|
+
from sqlite_cache import JsonSqliteCache, resolve_graph_db_path
|
|
39
|
+
from script_utils import FriendlyArgumentParser, run_cli
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
CACHE_TTL_SECONDS = 600
|
|
43
|
+
TABLE_NAME = "basedata_query_cache"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def contains_cjk(text: str) -> bool:
|
|
47
|
+
return any("\u4e00" <= ch <= "\u9fff" for ch in text)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _looks_like_basedata_payload(value: Any) -> bool:
|
|
51
|
+
return isinstance(value, dict) and (
|
|
52
|
+
"entityId" in value
|
|
53
|
+
or "numberOrName" in value
|
|
54
|
+
or "traceId" in value
|
|
55
|
+
or "data" in value
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _unwrap_route_raw(raw: Dict[str, Any]) -> Dict[str, Any]:
|
|
60
|
+
return unwrap_route_raw(raw, _looks_like_basedata_payload)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class BaseDataDbCache:
|
|
64
|
+
def __init__(self, db_path: str, ttl: int = CACHE_TTL_SECONDS):
|
|
65
|
+
self._cache = JsonSqliteCache(
|
|
66
|
+
db_path,
|
|
67
|
+
table_name=TABLE_NAME,
|
|
68
|
+
key_column="cache_key",
|
|
69
|
+
create_sql=f"""
|
|
70
|
+
CREATE TABLE IF NOT EXISTS {TABLE_NAME} (
|
|
71
|
+
cache_key TEXT PRIMARY KEY,
|
|
72
|
+
entity_id TEXT NOT NULL,
|
|
73
|
+
number_or_name TEXT NOT NULL,
|
|
74
|
+
full INTEGER NOT NULL,
|
|
75
|
+
payload TEXT NOT NULL,
|
|
76
|
+
updated_at INTEGER NOT NULL
|
|
77
|
+
)
|
|
78
|
+
""",
|
|
79
|
+
ttl=ttl,
|
|
80
|
+
init_error_message="初始化基础资料缓存表失败",
|
|
81
|
+
write_error_message="写入基础资料缓存失败",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get(self, cache_key: str) -> Optional[Dict[str, Any]]:
|
|
85
|
+
return self._cache.get(cache_key)
|
|
86
|
+
|
|
87
|
+
def set(
|
|
88
|
+
self,
|
|
89
|
+
cache_key: str,
|
|
90
|
+
entity_id: str,
|
|
91
|
+
number_or_name: str,
|
|
92
|
+
full: bool,
|
|
93
|
+
payload: Dict[str, Any],
|
|
94
|
+
):
|
|
95
|
+
self._cache.set_payload(
|
|
96
|
+
cache_key,
|
|
97
|
+
payload,
|
|
98
|
+
extra_columns=("entity_id", "number_or_name", "full"),
|
|
99
|
+
extra_values=(entity_id, number_or_name, 1 if full else 0),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def remove(self, cache_key: str):
|
|
103
|
+
self._cache.remove(cache_key)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class BaseDataQuery:
|
|
107
|
+
def __init__(self, config: Dict[str, Any], debug: bool = False):
|
|
108
|
+
self.debug = debug
|
|
109
|
+
|
|
110
|
+
route_config = config.get("route", {})
|
|
111
|
+
if not isinstance(route_config, dict):
|
|
112
|
+
route_config = {}
|
|
113
|
+
self.db_path = resolve_graph_db_path(
|
|
114
|
+
config,
|
|
115
|
+
"未配置 graph.dbPath,请在 ok-cosmic.json 中指定 SQLite 数据库路径。",
|
|
116
|
+
)
|
|
117
|
+
self.cache = BaseDataDbCache(self.db_path, ttl=CACHE_TTL_SECONDS)
|
|
118
|
+
self.route_client = RouteClient(
|
|
119
|
+
route_config,
|
|
120
|
+
debug=debug,
|
|
121
|
+
missing_message=(
|
|
122
|
+
"未配置基础资料查询 API。请在 ok-cosmic.json 的 route.apiUrl 中配置统一路由,"
|
|
123
|
+
"或设置 COSMIC_ROUTE_API / COSMIC_RUNTIME_ROUTE_API 环境变量。"
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def _log_debug(self, msg: str):
|
|
128
|
+
if self.debug:
|
|
129
|
+
print(f" (DEBUG) {msg}", file=sys.stderr)
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _cache_key(entity_id: str, number_or_name: str, full: bool) -> str:
|
|
133
|
+
return f"{entity_id}|{number_or_name}|{1 if full else 0}"
|
|
134
|
+
|
|
135
|
+
def _post(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
136
|
+
return self.route_client.post(payload)
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def _normalize_result(
|
|
140
|
+
raw: Dict[str, Any],
|
|
141
|
+
entity_id: str,
|
|
142
|
+
number_or_name: str,
|
|
143
|
+
full: bool,
|
|
144
|
+
) -> Dict[str, Any]:
|
|
145
|
+
payload_data = raw.get("data")
|
|
146
|
+
if not isinstance(payload_data, dict):
|
|
147
|
+
return {
|
|
148
|
+
"entityId": entity_id,
|
|
149
|
+
"numberOrName": number_or_name,
|
|
150
|
+
"full": full,
|
|
151
|
+
"traceId": None,
|
|
152
|
+
"data": payload_data,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
"entityId": payload_data.get("entityId", entity_id),
|
|
157
|
+
"numberOrName": payload_data.get("numberOrName", number_or_name),
|
|
158
|
+
"full": bool(payload_data.get("full", full)),
|
|
159
|
+
"traceId": payload_data.get("traceId"),
|
|
160
|
+
"data": payload_data.get("data"),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def query_one(self, entity_id: str, number_or_name: str = "", full: bool = False) -> Dict[str, Any]:
|
|
164
|
+
normalized_entity_id = (entity_id or "").strip()
|
|
165
|
+
normalized_keyword = (number_or_name or "").strip()
|
|
166
|
+
normalized_full = bool(full)
|
|
167
|
+
|
|
168
|
+
if not normalized_entity_id:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
"entityId 不能为空。只允许使用用户明确给出的准确英文标识或元数据脚本确认出的真实标识;如不清楚,请先用元数据脚本查 refType 或按中文名称查英文标识。"
|
|
171
|
+
)
|
|
172
|
+
if contains_cjk(normalized_entity_id):
|
|
173
|
+
raise ValueError(
|
|
174
|
+
"entityId 必须是已确认的英文基础资料标识;只允许使用用户明确给出的准确英文标识或元数据脚本确认出的真实标识,不能直接传中文名称。"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
cache_key = self._cache_key(normalized_entity_id, normalized_keyword, normalized_full)
|
|
178
|
+
cached = self.cache.get(cache_key)
|
|
179
|
+
if cached:
|
|
180
|
+
self._log_debug(f"命中基础资料缓存: {cache_key}")
|
|
181
|
+
return {"status": "ok", "source": "cache", **cached}
|
|
182
|
+
|
|
183
|
+
req_data = {
|
|
184
|
+
"entityId": normalized_entity_id,
|
|
185
|
+
"numberOrName": normalized_keyword,
|
|
186
|
+
"full": normalized_full,
|
|
187
|
+
}
|
|
188
|
+
payload = {
|
|
189
|
+
"data": {
|
|
190
|
+
"type": "basedata",
|
|
191
|
+
"reqData": req_data,
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
raw = _unwrap_route_raw(self._post(payload))
|
|
195
|
+
|
|
196
|
+
if raw.get("status") is False:
|
|
197
|
+
message = str(raw.get("message") or "remote api status=false")
|
|
198
|
+
error_code = raw.get("errorCode")
|
|
199
|
+
if " is not exist" in message:
|
|
200
|
+
return {
|
|
201
|
+
"status": "not_found",
|
|
202
|
+
"message": message,
|
|
203
|
+
"errorCode": error_code,
|
|
204
|
+
"entityId": normalized_entity_id,
|
|
205
|
+
"numberOrName": normalized_keyword,
|
|
206
|
+
"full": normalized_full,
|
|
207
|
+
}
|
|
208
|
+
raise RuntimeError(f"{message} (errorCode={error_code})")
|
|
209
|
+
|
|
210
|
+
result = self._normalize_result(raw, normalized_entity_id, normalized_keyword, normalized_full)
|
|
211
|
+
self.cache.set(cache_key, normalized_entity_id, normalized_keyword, normalized_full, result)
|
|
212
|
+
return {"status": "ok", "source": "api", **result}
|
|
213
|
+
|
|
214
|
+
def refresh(self, entity_id: str, number_or_name: str = "", full: bool = False):
|
|
215
|
+
cache_key = self._cache_key((entity_id or "").strip(), (number_or_name or "").strip(), bool(full))
|
|
216
|
+
self.cache.remove(cache_key)
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def _maybe_render_mapping_table(data: Any) -> List[str]:
|
|
220
|
+
if not isinstance(data, list) or not data:
|
|
221
|
+
return []
|
|
222
|
+
|
|
223
|
+
rows: List[Dict[str, str]] = []
|
|
224
|
+
for item in data:
|
|
225
|
+
if not isinstance(item, dict):
|
|
226
|
+
return []
|
|
227
|
+
number = item.get("number") or item.get("Number") or item.get("code") or item.get("Code")
|
|
228
|
+
name = item.get("name") or item.get("Name")
|
|
229
|
+
item_id = item.get("id") or item.get("Id") or item.get("pkId") or item.get("masterId")
|
|
230
|
+
if number is None and name is None and item_id is None:
|
|
231
|
+
return []
|
|
232
|
+
rows.append(
|
|
233
|
+
{
|
|
234
|
+
"number": str(number or "-"),
|
|
235
|
+
"name": str(name or "-"),
|
|
236
|
+
"id": str(item_id or "-"),
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
md = [
|
|
241
|
+
"### [List] 编码名称映射",
|
|
242
|
+
"| 编码 | 名称 | ID |",
|
|
243
|
+
"| :--- | :--- | :--- |",
|
|
244
|
+
]
|
|
245
|
+
for row in rows:
|
|
246
|
+
md.append(f"| `{row['number']}` | {row['name']} | `{row['id']}` |")
|
|
247
|
+
return md
|
|
248
|
+
|
|
249
|
+
def render(self, result: Dict[str, Any], json_mode: bool = False) -> str:
|
|
250
|
+
if json_mode:
|
|
251
|
+
return json.dumps(result, ensure_ascii=False, indent=2)
|
|
252
|
+
|
|
253
|
+
status = result.get("status")
|
|
254
|
+
if status == "not_found":
|
|
255
|
+
message = str(result.get("message") or "未找到基础资料")
|
|
256
|
+
error_code = result.get("errorCode")
|
|
257
|
+
lines = [
|
|
258
|
+
"## ✖️ 未找到基础资料",
|
|
259
|
+
f"**基础资料标识**: `{result.get('entityId') or '-'}`",
|
|
260
|
+
f"**查询词**: `{result.get('numberOrName') or '-'}`",
|
|
261
|
+
f"**完整模式**: `{bool(result.get('full'))}`",
|
|
262
|
+
f"**说明**: {message}",
|
|
263
|
+
]
|
|
264
|
+
if error_code is not None:
|
|
265
|
+
lines.append(f"**错误码**: `{error_code}`")
|
|
266
|
+
return "\n".join(lines)
|
|
267
|
+
|
|
268
|
+
lines = [
|
|
269
|
+
f"## [BaseData] 基础资料: `{result.get('entityId') or '-'}`",
|
|
270
|
+
f"**查询词**: `{result.get('numberOrName') or '(空,通常返回前50条映射)'}`",
|
|
271
|
+
f"**完整模式**: `{bool(result.get('full'))}`",
|
|
272
|
+
f"**结果来源**: `{result.get('source') or '-'}`",
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
trace_id = result.get("traceId")
|
|
276
|
+
if trace_id:
|
|
277
|
+
lines.append(f"**traceId**: `{trace_id}`")
|
|
278
|
+
|
|
279
|
+
data = result.get("data")
|
|
280
|
+
mapping_table = self._maybe_render_mapping_table(data)
|
|
281
|
+
if mapping_table:
|
|
282
|
+
lines.append("")
|
|
283
|
+
lines.extend(mapping_table)
|
|
284
|
+
else:
|
|
285
|
+
lines.append("\n### [Data] 返回数据")
|
|
286
|
+
lines.append("```json")
|
|
287
|
+
lines.append(json.dumps(data, ensure_ascii=False, indent=2))
|
|
288
|
+
lines.append("```")
|
|
289
|
+
|
|
290
|
+
return "\n".join(lines)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def main():
|
|
294
|
+
parser = FriendlyArgumentParser(
|
|
295
|
+
description="Cosmic BaseData Query CLI — 苍穹基础资料在线查询(带本地缓存)",
|
|
296
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
297
|
+
epilog="""\
|
|
298
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
299
|
+
推荐用法
|
|
300
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
301
|
+
# 按 entityId + 编码/名称查询
|
|
302
|
+
%(prog)s --config ok-cosmic.json get --entity-id <entityId> --number-or-name <keyword>
|
|
303
|
+
|
|
304
|
+
# 不传 --number-or-name 时返回前 50 条编码名称映射
|
|
305
|
+
%(prog)s --config ok-cosmic.json get --entity-id <entityId>
|
|
306
|
+
|
|
307
|
+
# 返回完整数据包
|
|
308
|
+
%(prog)s --config ok-cosmic.json get --entity-id <entityId> --full
|
|
309
|
+
|
|
310
|
+
# 清除缓存后重新请求
|
|
311
|
+
%(prog)s --config ok-cosmic.json get --entity-id <entityId> --refresh
|
|
312
|
+
|
|
313
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
314
|
+
AI 调用约束(摘要,完整约束见 SKILL.md 跨脚本硬约束)
|
|
315
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
316
|
+
- entityId 必须来自"用户明确给出"或"元数据脚本确认";来源不清时停止,先查元数据。
|
|
317
|
+
- 默认精确查询,先传编码或精确名称;--full 仅在需要完整结构时使用。
|
|
318
|
+
|
|
319
|
+
缓存说明:
|
|
320
|
+
- 使用 graph.dbPath 对应的 SQLite 文件,独立表 basedata_query_cache
|
|
321
|
+
- 固定缓存时长 10 分钟,可用 --refresh 手动刷新
|
|
322
|
+
""",
|
|
323
|
+
)
|
|
324
|
+
parser.add_argument("--config", help="Path to ok-cosmic.json")
|
|
325
|
+
parser.add_argument("--debug", action="store_true")
|
|
326
|
+
|
|
327
|
+
sub_parser = parser.add_subparsers(dest="command")
|
|
328
|
+
get_parser = sub_parser.add_parser("get")
|
|
329
|
+
get_parser.add_argument(
|
|
330
|
+
"--entity-id",
|
|
331
|
+
required=True,
|
|
332
|
+
help="基础资料标识(英文 entityId,且来源必须已确认;不确定时先用元数据脚本查 refType 或按中文名称查英文标识)",
|
|
333
|
+
)
|
|
334
|
+
get_parser.add_argument("--number-or-name", default="", help="编码或名称;为空时通常返回前50条映射")
|
|
335
|
+
get_parser.add_argument("--keyword", dest="number_or_name", help="--number-or-name 的别名")
|
|
336
|
+
get_parser.add_argument("--full", action="store_true", help="返回完整字段")
|
|
337
|
+
get_parser.add_argument("--json", action="store_true", help="直接输出 JSON")
|
|
338
|
+
get_parser.add_argument("--refresh", action="store_true", help="删除当前查询条件对应的本地缓存后再请求")
|
|
339
|
+
get_parser.add_argument("--debug", action="store_true")
|
|
340
|
+
|
|
341
|
+
args = parser.parse_args()
|
|
342
|
+
if args.command != "get":
|
|
343
|
+
parser.print_help()
|
|
344
|
+
return
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
config = load_project_config(args.config)
|
|
348
|
+
query = BaseDataQuery(config, debug=(args.debug or getattr(args, "debug", False)))
|
|
349
|
+
if args.refresh:
|
|
350
|
+
query.refresh(args.entity_id, args.number_or_name, args.full)
|
|
351
|
+
result = query.query_one(args.entity_id, args.number_or_name, args.full)
|
|
352
|
+
print(query.render(result, json_mode=args.json))
|
|
353
|
+
except Exception as e:
|
|
354
|
+
print(f"✖️ 错误: {str(e)}", file=sys.stderr)
|
|
355
|
+
sys.exit(1)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
if __name__ == "__main__":
|
|
359
|
+
sys.exit(run_cli(main))
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: NOASSERTION
|
|
3
|
+
"""
|
|
4
|
+
cosmic-config-check.py — Step 0 comprehensive preflight for ok-cosmic.
|
|
5
|
+
|
|
6
|
+
Checks performed:
|
|
7
|
+
1. Python version (>= 3.8)
|
|
8
|
+
2. Python dependencies — auto-install if missing (tree-sitter, tree-sitter-java)
|
|
9
|
+
3. ok-cosmic.json configuration validation
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python3 cosmic-config-check.py
|
|
13
|
+
python3 cosmic-config-check.py --config /path/to/ok-cosmic.json
|
|
14
|
+
python3 cosmic-config-check.py --config ok-cosmic.json --strict
|
|
15
|
+
python3 cosmic-config-check.py --config ok-cosmic.json --json
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
from typing import Any, Dict, List
|
|
24
|
+
|
|
25
|
+
from config_loader import read_project_config, validate_project_config
|
|
26
|
+
from script_utils import FriendlyArgumentParser, run_cli
|
|
27
|
+
|
|
28
|
+
# ──────────────────────────────────────────────
|
|
29
|
+
# Python 依赖包清单(module_name → pip_name)
|
|
30
|
+
# ──────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
REQUIRED_PACKAGES = {
|
|
33
|
+
"tree_sitter": "tree-sitter",
|
|
34
|
+
"tree_sitter_java": "tree-sitter-java",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# 进度输出流(--json 模式时切换到 stderr,避免污染 JSON 输出)
|
|
39
|
+
_progress_out = sys.stdout
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _progress(msg: str) -> None:
|
|
43
|
+
print(msg, file=_progress_out)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ──────────────────────────────────────────────
|
|
47
|
+
# Phase 1: 运行环境检查
|
|
48
|
+
# ──────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
def _check_python_version(issues: List[Dict[str, str]]) -> None:
|
|
51
|
+
"""检查 Python 版本 >= 3.8。"""
|
|
52
|
+
ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
53
|
+
if sys.version_info < (3, 8):
|
|
54
|
+
issues.append({"level": "ERROR", "key": "python.version",
|
|
55
|
+
"message": f"Python >= 3.8 required, current: {ver}"})
|
|
56
|
+
else:
|
|
57
|
+
_progress(f"[OK] Python {ver}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _check_and_install_dependencies(issues: List[Dict[str, str]]) -> None:
|
|
61
|
+
"""检测缺失的 Python 依赖包并自动安装。"""
|
|
62
|
+
missing = []
|
|
63
|
+
for module_name, pip_name in REQUIRED_PACKAGES.items():
|
|
64
|
+
try:
|
|
65
|
+
__import__(module_name)
|
|
66
|
+
except ImportError:
|
|
67
|
+
missing.append((module_name, pip_name))
|
|
68
|
+
|
|
69
|
+
if not missing:
|
|
70
|
+
names = ", ".join(REQUIRED_PACKAGES.values())
|
|
71
|
+
_progress(f"[OK] Python 依赖已就绪 ({names})")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
pip_names = [p for _, p in missing]
|
|
75
|
+
display = ", ".join(pip_names)
|
|
76
|
+
_progress(f"[AUTO] 正在安装缺失的 Python 依赖: {display} ...")
|
|
77
|
+
try:
|
|
78
|
+
result = subprocess.run(
|
|
79
|
+
[sys.executable, "-m", "pip", "install", "--quiet"] + pip_names,
|
|
80
|
+
capture_output=True, text=True, timeout=120,
|
|
81
|
+
)
|
|
82
|
+
if result.returncode == 0:
|
|
83
|
+
_progress(f"[OK] 已自动安装: {display}")
|
|
84
|
+
else:
|
|
85
|
+
err = (result.stderr or "").strip()[:200]
|
|
86
|
+
issues.append({"level": "ERROR", "key": "python.deps",
|
|
87
|
+
"message": f"自动安装失败: {err}。请手动执行: {sys.executable} -m pip install {' '.join(pip_names)}"})
|
|
88
|
+
except subprocess.TimeoutExpired:
|
|
89
|
+
issues.append({"level": "ERROR", "key": "python.deps",
|
|
90
|
+
"message": f"安装超时。请手动执行: {sys.executable} -m pip install {' '.join(pip_names)}"})
|
|
91
|
+
except Exception as e:
|
|
92
|
+
issues.append({"level": "ERROR", "key": "python.deps",
|
|
93
|
+
"message": f"安装异常: {e}。请手动执行: {sys.executable} -m pip install {' '.join(pip_names)}"})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ──────────────────────────────────────────────
|
|
99
|
+
# 报告输出
|
|
100
|
+
# ──────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
def _group_issues(issues: List[Dict[str, str]]) -> Dict[str, List[Dict[str, str]]]:
|
|
103
|
+
errors = [issue for issue in issues if issue.get("level") == "ERROR"]
|
|
104
|
+
warnings = [issue for issue in issues if issue.get("level") == "WARNING"]
|
|
105
|
+
return {"errors": errors, "warnings": warnings}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _print_text_report(issues: List[Dict[str, str]]) -> None:
|
|
109
|
+
"""打印环境+配置检查的文本报告([OK] 行已在检查过程中实时输出)。"""
|
|
110
|
+
for issue in issues:
|
|
111
|
+
level = issue.get("level", "INFO")
|
|
112
|
+
key = issue.get("key", "-")
|
|
113
|
+
message = issue.get("message", "")
|
|
114
|
+
print(f"[{level}] {key}: {message}")
|
|
115
|
+
|
|
116
|
+
grouped = _group_issues(issues)
|
|
117
|
+
errs, warns = len(grouped["errors"]), len(grouped["warnings"])
|
|
118
|
+
print(f"[SUMMARY] errors={errs} warnings={warns}")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ──────────────────────────────────────────────
|
|
122
|
+
# 主入口
|
|
123
|
+
# ──────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
def main() -> int:
|
|
126
|
+
parser = FriendlyArgumentParser(
|
|
127
|
+
description="Step 0 环境与配置预检:检查 Python 依赖(自动安装)、ok-cosmic.json 配置完整性。"
|
|
128
|
+
)
|
|
129
|
+
parser.add_argument("--config", help="Path to ok-cosmic.json")
|
|
130
|
+
parser.add_argument("--strict", action="store_true", help="Treat warnings as failures.")
|
|
131
|
+
parser.add_argument("--json", action="store_true", help="Output machine-readable JSON report.")
|
|
132
|
+
args = parser.parse_args()
|
|
133
|
+
|
|
134
|
+
issues: List[Dict[str, str]] = []
|
|
135
|
+
|
|
136
|
+
# --json 模式下,进度信息输出到 stderr,避免污染 JSON 输出
|
|
137
|
+
global _progress_out
|
|
138
|
+
if args.json:
|
|
139
|
+
_progress_out = sys.stderr
|
|
140
|
+
|
|
141
|
+
# Phase 1: 运行环境
|
|
142
|
+
_check_python_version(issues)
|
|
143
|
+
_check_and_install_dependencies(issues)
|
|
144
|
+
|
|
145
|
+
# Phase 2: 配置文件
|
|
146
|
+
config_path_str = args.config or "ok-cosmic.json"
|
|
147
|
+
try:
|
|
148
|
+
resolved_path, raw_config = read_project_config(args.config)
|
|
149
|
+
config: Dict[str, Any] = dict(raw_config)
|
|
150
|
+
config["__config_path__"] = str(resolved_path)
|
|
151
|
+
config["__config_dir__"] = str(resolved_path.parent)
|
|
152
|
+
_progress(f"[OK] 已找到配置文件: {resolved_path}")
|
|
153
|
+
issues.extend(validate_project_config(config, str(resolved_path)))
|
|
154
|
+
config_path_str = str(resolved_path)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
issues.append({"level": "ERROR", "key": "__file__", "message": str(e)})
|
|
157
|
+
|
|
158
|
+
# 汇总
|
|
159
|
+
grouped = _group_issues(issues)
|
|
160
|
+
ok = not grouped["errors"] and not (args.strict and grouped["warnings"])
|
|
161
|
+
|
|
162
|
+
if args.json:
|
|
163
|
+
print(json.dumps({
|
|
164
|
+
"ok": ok,
|
|
165
|
+
"configPath": config_path_str,
|
|
166
|
+
"errors": grouped["errors"],
|
|
167
|
+
"warnings": grouped["warnings"],
|
|
168
|
+
}, ensure_ascii=False, indent=2))
|
|
169
|
+
else:
|
|
170
|
+
if issues:
|
|
171
|
+
_print_text_report(issues)
|
|
172
|
+
else:
|
|
173
|
+
print("[OK] 全部检查通过,未发现问题。")
|
|
174
|
+
if args.strict and grouped["warnings"] and not grouped["errors"]:
|
|
175
|
+
print("[STRICT] strict 模式下 warning 也会导致失败。", file=sys.stderr)
|
|
176
|
+
|
|
177
|
+
return 0 if ok else 1
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
sys.exit(run_cli(main))
|