@shulex/cxclawctl 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -22
- package/capabilities.lock.json +535 -131
- package/dist/cli.js +2203 -127
- package/dist/cli.js.map +1 -1
- package/package.json +20 -6
- package/requirements-cli.txt +0 -2
- package/skills/__lib__/__init__.py +0 -0
- package/skills/__lib__/skill-runtime/bootstrap.py +0 -134
- package/skills/__lib__/solvea-api/__init__.py +0 -0
- package/skills/__lib__/solvea-api/client.py +0 -1944
- package/skills/__lib__/solvea-api/dashboard.py +0 -100
- package/skills/__lib__/solvea-api/solvea_api.py +0 -1341
- package/skills/agent-analytics/SKILL.md +0 -74
- package/skills/agent-analytics/references/chat-report-fields.md +0 -36
- package/skills/agent-analytics/references/report-fields.md +0 -130
- package/skills/agent-analytics/references/transcript.md +0 -110
- package/skills/agent-builder/SKILL.md +0 -42
- package/skills/agent-builder/interfaces/fallback-chatflow.md +0 -47
- package/skills/agent-builder/references/chatflow/graph-spec.md +0 -259
- package/skills/agent-builder/references/chatflow/node-selection.md +0 -89
- package/skills/agent-builder/references/chatflow/nodes/agent.md +0 -350
- package/skills/agent-builder/references/chatflow/nodes/business-track.md +0 -43
- package/skills/agent-builder/references/chatflow/nodes/code.md +0 -157
- package/skills/agent-builder/references/chatflow/nodes/collect-order.md +0 -47
- package/skills/agent-builder/references/chatflow/nodes/finish.md +0 -34
- package/skills/agent-builder/references/chatflow/nodes/human-review.md +0 -60
- package/skills/agent-builder/references/chatflow/nodes/if-else.md +0 -100
- package/skills/agent-builder/references/chatflow/nodes/intent.md +0 -79
- package/skills/agent-builder/references/chatflow/nodes/knowledge-reply.md +0 -43
- package/skills/agent-builder/references/chatflow/nodes/llm.md +0 -117
- package/skills/agent-builder/references/chatflow/nodes/note.md +0 -38
- package/skills/agent-builder/references/chatflow/nodes/question.md +0 -97
- package/skills/agent-builder/references/chatflow/nodes/rag-reply.md +0 -68
- package/skills/agent-builder/references/chatflow/nodes/reply.md +0 -97
- package/skills/agent-builder/references/chatflow/nodes/start.md +0 -35
- package/skills/agent-builder/references/chatflow/nodes/sub-graph.md +0 -48
- package/skills/agent-builder/references/chatflow/nodes/tool.md +0 -138
- package/skills/agent-builder/references/chatflow/nodes/transfer-to-human.md +0 -63
- package/skills/agent-builder/references/chatflow/nodes/variable-assignment.md +0 -52
- package/skills/agent-builder/references/chatflow/nodes/variable-extract.md +0 -50
- package/skills/agent-builder/references/chatflow/nodes/variable-process.md +0 -70
- package/skills/agent-builder/references/chatflow/overview.md +0 -53
- package/skills/agent-builder/references/chatflow/visual-translation.md +0 -64
- package/skills/agent-builder/references/chatflow/workflow.md +0 -134
- package/skills/agent-builder/references/multi-agent/create-update.md +0 -185
- package/skills/agent-builder/references/multi-agent/graph-spec.md +0 -246
- package/skills/agent-builder/references/multi-agent/graph-structure.md +0 -12
- package/skills/agent-builder/references/multi-agent/migrate-to-chatflow.md +0 -13
- package/skills/agent-builder/references/multi-agent/migration/large.md +0 -554
- package/skills/agent-builder/references/multi-agent/migration/principles.md +0 -446
- package/skills/agent-builder/references/multi-agent/migration/small.md +0 -225
- package/skills/agent-builder/references/multi-agent/orchestration.md +0 -11
- package/skills/agent-builder/references/multi-agent/overview.md +0 -48
- package/skills/agent-builder/references/multi-agent/update.md +0 -128
- package/skills/agent-builder/references/multi-agent/workflow.md +0 -87
- package/skills/agent-builder/references/single-agent/overview.md +0 -48
- package/skills/agent-builder/references/single-agent/prompt.md +0 -16
- package/skills/agent-builder/references/single-agent/tools.md +0 -36
- package/skills/agent-builder/references/single-agent/voice.md +0 -46
- package/skills/agent-builder/references/tools/http-tool.md +0 -315
- package/skills/agent-builder/references/tools/mock-tool.md +0 -279
- package/skills/agent-builder/references/tools/overview.md +0 -46
- package/skills/agent-builder/references/tools/tool-config.md +0 -43
- package/skills/agent-builder/references/tools/x-token.md +0 -34
- package/skills/agent-builder/scripts/chatflow/chatflow_builder.py +0 -3458
- package/skills/agent-builder/scripts/chatflow/validate_graph.py +0 -1697
- package/skills/agent-builder/scripts/copy_marketplace_agent.py +0 -61
- package/skills/agent-builder/scripts/multi-agent/legacy_graph_analyzer.py +0 -5974
- package/skills/agent-builder/scripts/multi-agent/replay_compare.py +0 -679
- package/skills/agent-builder/scripts/patch_agent_tools.py +0 -135
- package/skills/agent-builder/scripts/tools/validate_tool_config.py +0 -384
- package/skills/agent-tester/SKILL.md +0 -35
- package/skills/agent-tester/batch-test/overview.md +0 -86
- package/skills/agent-tester/scripts/batch_test_sheet.py +0 -213
- package/skills/agent-tester/single-test/badcase-retest.md +0 -85
- package/skills/agent-tester/single-test/direct-test.md +0 -24
- package/skills/agent-tester/single-test/overview.md +0 -142
- package/skills/delivery/SKILL.md +0 -112
- package/skills/delivery/interfaces/classify.md +0 -38
- package/skills/delivery/interfaces/create-agents.md +0 -89
- package/skills/delivery/interfaces/delivery-test.md +0 -67
- package/skills/delivery/references/flow.md +0 -40
- package/skills/delivery/references/validation.md +0 -15
- package/skills/delivery/scripts/batch_test.py +0 -704
- package/skills/delivery/scripts/classify.py +0 -22
- package/skills/delivery/scripts/create_agents.py +0 -695
- package/skills/delivery/scripts/gen_delivery_report.py +0 -335
- package/skills/delivery/scripts/gen_excel.py +0 -234
- package/skills/delivery/scripts/gen_report.py +0 -208
- package/skills/delivery/scripts/plan.py +0 -163
- package/skills/delivery/scripts/render.py +0 -713
- package/skills/delivery/scripts/render_summary.py +0 -38
- package/skills/delivery/scripts/sample_history_cases.py +0 -109
- package/skills/kb-builder/SKILL.md +0 -80
- package/skills/kb-builder/interfaces/qa-upload.md +0 -55
- package/skills/kb-builder/interfaces/web-kb.md +0 -84
- package/skills/kb-builder/references/directory.md +0 -373
- package/skills/kb-builder/references/export.md +0 -282
- package/skills/kb-builder/references/isolation-keywords.md +0 -113
- package/skills/kb-builder/references/knowledge.md +0 -153
- package/skills/kb-builder/references/product.md +0 -52
- package/skills/kb-builder/references/qa-from-help-center.md +0 -58
- package/skills/kb-builder/references/qa-from-pdf.md +0 -94
- package/skills/kb-builder/references/qa-from-tickets.md +0 -168
- package/skills/kb-builder/references/qa-from-web.md +0 -98
- package/skills/kb-builder/references/qa-quality.md +0 -73
- package/skills/kb-builder/references/reply-template.md +0 -237
- package/skills/kb-builder/references/tag-isolation-migration.md +0 -61
- package/skills/kb-builder/references/tags.md +0 -45
- package/skills/kb-builder/references/upload.md +0 -130
- package/skills/kb-builder/scripts/batch_extract_ticket_qa.py +0 -333
- package/skills/kb-builder/scripts/build_directory_plan.py +0 -144
- package/skills/kb-builder/scripts/crawl_site.py +0 -433
- package/skills/kb-builder/scripts/create_dirs.py +0 -146
- package/skills/kb-builder/scripts/export_knowledge.py +0 -454
- package/skills/kb-builder/scripts/extract_ticket_qa.py +0 -441
- package/skills/kb-builder/scripts/extract_web_qa.py +0 -118
- package/skills/kb-builder/scripts/import_qa.py +0 -219
- package/skills/kb-builder/scripts/import_source_documents.py +0 -143
- package/skills/kb-builder/scripts/import_templates.py +0 -409
- package/skills/poc/SKILL.md +0 -125
- package/skills/poc/assets/default_manual.css +0 -279
- package/skills/poc/assets/notice_escalate.png +0 -0
- package/skills/poc/assets/notice_escalate2.png +0 -0
- package/skills/poc/assets/notice_new_chat.png +0 -0
- package/skills/poc/assets/poc_test_template.html +0 -645
- package/skills/poc/assets/poc_test_template.md +0 -104
- package/skills/poc/assets/step3_preview.png +0 -0
- package/skills/poc/assets/step4_order_note.png +0 -0
- package/skills/poc/interfaces/classify.md +0 -38
- package/skills/poc/interfaces/create-agents.md +0 -108
- package/skills/poc/interfaces/poc-test.md +0 -94
- package/skills/poc/references/flow.md +0 -47
- package/skills/poc/references/kb-directory.md +0 -22
- package/skills/poc/references/validation.md +0 -17
- package/skills/poc/scripts/classify.py +0 -22
- package/skills/poc/scripts/create_agents.py +0 -818
- package/skills/poc/scripts/gen_excel.py +0 -234
- package/skills/poc/scripts/gen_poc_manual.py +0 -249
- package/skills/poc/scripts/gen_report.py +0 -208
- package/skills/poc/scripts/inspect_chatflow.py +0 -209
- package/skills/poc/scripts/plan.py +0 -178
- package/skills/poc/scripts/poc_test.py +0 -495
- package/skills/poc/scripts/render.py +0 -713
- package/skills/poc/scripts/render_summary.py +0 -41
- package/skills/runtime-inspector/SKILL.md +0 -51
- package/skills/runtime-inspector/references/frontend-rendering.md +0 -126
- package/skills/runtime-inspector/references/model.md +0 -59
- package/skills/runtime-inspector/references/sandbox.md +0 -131
- package/skills/runtime-inspector/scripts/model_inspect.py +0 -68
- package/skills/runtime-inspector/scripts/prepare.py +0 -98
- package/skills/runtime-inspector/scripts/process.py +0 -84
- package/skills/runtime-inspector/scripts/read_state.py +0 -32
- package/skills/runtime-inspector/scripts/sandbox_inspect.py +0 -463
- package/skills/runtime-inspector/scripts/test_claude_proxy.py +0 -49
- package/skills/runtime-inspector/scripts/test_openai_proxy.py +0 -41
- package/skills/runtime-inspector/scripts/verify_persistence.py +0 -101
- package/skills/runtime-inspector/scripts/write_test.py +0 -41
- package/skills/setup/SKILL.md +0 -17
- package/skills/shulex-support/SKILL.md +0 -39
- package/skills/shulex-support/references/badcase-analysis/overview.md +0 -34
- package/skills/shulex-support/references/badcase-analysis/text/conversation-record.md +0 -105
- package/skills/shulex-support/references/badcase-analysis/text/core-flow.md +0 -89
- package/skills/shulex-support/references/badcase-analysis/text/log-query-rules.md +0 -36
- package/skills/shulex-support/references/badcase-analysis/text/m1-entry.md +0 -53
- package/skills/shulex-support/references/badcase-analysis/text/m10-chatflow-service.md +0 -259
- package/skills/shulex-support/references/badcase-analysis/text/m11-algorithm-service.md +0 -148
- package/skills/shulex-support/references/badcase-analysis/text/m12-quality-inspection.md +0 -57
- package/skills/shulex-support/references/badcase-analysis/text/m2-intent.md +0 -120
- package/skills/shulex-support/references/badcase-analysis/text/m3-strategy.md +0 -126
- package/skills/shulex-support/references/badcase-analysis/text/m4-aifield.md +0 -121
- package/skills/shulex-support/references/badcase-analysis/text/m5-agent.md +0 -208
- package/skills/shulex-support/references/badcase-analysis/text/m6-tool.md +0 -115
- package/skills/shulex-support/references/badcase-analysis/text/m7-reply.md +0 -72
- package/skills/shulex-support/references/badcase-analysis/text/m8-error.md +0 -92
- package/skills/shulex-support/references/badcase-analysis/text/m9-knowledge-search.md +0 -147
- package/skills/shulex-support/references/badcase-analysis/text/sls-query.md +0 -283
- package/skills/shulex-support/references/badcase-analysis/voice/core-flow.md +0 -77
- package/skills/shulex-support/references/badcase-analysis/voice/dependencies.md +0 -19
- package/skills/shulex-support/references/badcase-analysis/voice/platform-api.md +0 -23
- package/skills/shulex-support/references/badcase-analysis/voice/sls-playbook.md +0 -31
- package/skills/shulex-support/references/product-docs.md +0 -70
- package/skills/shulex-support/references/tickets.md +0 -184
- package/skills/shulex-support/scripts/support.py +0 -427
- package/skills/solvea-desk/SKILL.md +0 -39
- package/skills/solvea-desk/references/report/fields.md +0 -82
- package/skills/solvea-desk/references/report/report-design.md +0 -200
- package/skills/solvea-desk/references/report/report-template.html +0 -44
- package/skills/solvea-desk/references/report/report.css +0 -517
- package/skills/solvea-desk/references/report/workflow.md +0 -464
- package/skills/solvea-desk/references/tickets/query.md +0 -103
- package/skills/solvea-desk/references/tickets/tags.md +0 -83
- package/skills/solvea-desk/scripts/report/fetch_sources.py +0 -580
- package/skills/solvea-desk/scripts/report/render_report.py +0 -763
- package/skills/solvea-desk/scripts/tickets/desk_api.py +0 -73
- package/skills/solvea-desk/scripts/tickets/fetch_tickets.py +0 -403
- package/skills/solvea-desk/scripts/tickets/ticket_tags.py +0 -305
- package/skills/solvea-kit/SKILL.md +0 -84
- package/skills/solvea-kit/references/agent.md +0 -145
- package/skills/solvea-kit/references/chat.md +0 -244
- package/skills/solvea-kit/references/intent.md +0 -69
- package/skills/solvea-kit/references/plugin.md +0 -53
- package/skills/solvea-kit/references/quality-inspection.md +0 -75
- package/skills/solvea-kit/references/strategy.md +0 -62
- package/skills/solvea-kit/references/tagging.md +0 -231
- package/skills/solvea-kit/references/variables.md +0 -53
- package/skills/solvea-kit/scripts/generate_label_import_file.py +0 -151
- package/skills/solvea-kit/scripts/intent_classify.py +0 -843
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""验证跨轮 state 持久化机制:上一轮写入的 state 是否被正确还原到 workspace/state/。"""
|
|
3
|
-
import os, sys
|
|
4
|
-
sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
|
|
5
|
-
from bootstrap import *
|
|
6
|
-
import json
|
|
7
|
-
|
|
8
|
-
print("=" * 60)
|
|
9
|
-
print("CROSS-ROUND STATE VERIFICATION")
|
|
10
|
-
print("=" * 60)
|
|
11
|
-
|
|
12
|
-
state_file = STATE_DIR / "inspect.json"
|
|
13
|
-
|
|
14
|
-
print(f"\n{'✅' if state_file.exists() else '❌'} workspace/state ({state_file})")
|
|
15
|
-
if state_file.exists():
|
|
16
|
-
try:
|
|
17
|
-
data = json.loads(state_file.read_text())
|
|
18
|
-
print(f" content: {json.dumps(data, indent=4, ensure_ascii=False)}")
|
|
19
|
-
except Exception as e:
|
|
20
|
-
print(f" [error reading] {e}")
|
|
21
|
-
else:
|
|
22
|
-
print(" (not found — 可能是第一轮,或上一轮未写入 state)")
|
|
23
|
-
|
|
24
|
-
print("\n" + "=" * 60)
|
|
25
|
-
if state_file.exists():
|
|
26
|
-
data = json.loads(state_file.read_text())
|
|
27
|
-
count = data.get("write_count", 0)
|
|
28
|
-
print(f"✅ 跨轮持久化验证通过!累计写入 {count} 次")
|
|
29
|
-
print(f" 上一轮 run_id: {data.get('last_run_id', 'N/A')}")
|
|
30
|
-
else:
|
|
31
|
-
print("ℹ️ state 不存在,这是第一轮或 state 尚未上传")
|
|
32
|
-
print("=" * 60)
|
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""沙箱状态检查入口。支持全量检查或单项场景。
|
|
3
|
-
|
|
4
|
-
用法:
|
|
5
|
-
python3 /skills/runtime-inspector/scripts/sandbox_inspect.py # 全量
|
|
6
|
-
python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario env # 仅环境变量
|
|
7
|
-
python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario fs # 仅目录结构
|
|
8
|
-
python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario execute # 仅 bash execute 路径行为
|
|
9
|
-
python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario paths # 虚拟路径行为验证
|
|
10
|
-
python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario deps # 仅依赖库
|
|
11
|
-
"""
|
|
12
|
-
import json, os, sys, argparse, importlib, subprocess, shutil
|
|
13
|
-
sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
|
|
14
|
-
from bootstrap import *
|
|
15
|
-
import json
|
|
16
|
-
|
|
17
|
-
_SEC = "=" * 64
|
|
18
|
-
_failures: list[str] = []
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _ok(label: str, detail: str = "") -> None:
|
|
22
|
-
suffix = f" ({detail})" if detail else ""
|
|
23
|
-
print(f" ✅ {label}{suffix}")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _fail(label: str, detail: str = "") -> None:
|
|
27
|
-
suffix = f" — {detail}" if detail else ""
|
|
28
|
-
print(f" ❌ {label}{suffix}")
|
|
29
|
-
_failures.append(label + (f": {detail}" if detail else ""))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _info(label: str, detail: str = "") -> None:
|
|
33
|
-
suffix = f" ({detail})" if detail else ""
|
|
34
|
-
print(f" ⬜ {label}{suffix}")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def check(label: str, ok: bool, detail: str = "") -> None:
|
|
38
|
-
(_ok if ok else _fail)(label, detail)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
_SENSITIVE_SUFFIXES = ("KEY", "SECRET", "PASSWORD", "TOKEN", "PASS")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def _is_sensitive(name: str) -> bool:
|
|
45
|
-
upper = name.upper()
|
|
46
|
-
return any(upper.endswith(s) or f"_{s}_" in upper for s in _SENSITIVE_SUFFIXES)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _mask(val: str, show: int = 4) -> str:
|
|
50
|
-
"""Keep first/last `show` chars, replace middle with ***."""
|
|
51
|
-
if len(val) <= show * 2:
|
|
52
|
-
return "*" * len(val)
|
|
53
|
-
return f"{val[:show]}***{val[-show:]}"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
-
# SCENARIO: env
|
|
58
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
def check_env() -> None:
|
|
61
|
-
print(_SEC)
|
|
62
|
-
print("ENV — 预期注入变量")
|
|
63
|
-
|
|
64
|
-
# WORKSPACE_DIR:必须存在且可写,本地=root_dir/workspace,Docker=/sandbox/workspace
|
|
65
|
-
workspace_dir = os.environ.get("WORKSPACE_DIR", "")
|
|
66
|
-
if workspace_dir:
|
|
67
|
-
p = Path(workspace_dir)
|
|
68
|
-
if not p.exists():
|
|
69
|
-
_fail("WORKSPACE_DIR", f"路径不存在: {workspace_dir}")
|
|
70
|
-
elif not os.access(p, os.W_OK):
|
|
71
|
-
_fail("WORKSPACE_DIR", f"路径不可写: {workspace_dir}")
|
|
72
|
-
else:
|
|
73
|
-
_ok("WORKSPACE_DIR", workspace_dir)
|
|
74
|
-
else:
|
|
75
|
-
_fail("WORKSPACE_DIR", "未注入")
|
|
76
|
-
|
|
77
|
-
# SOLVEA_LIB_DIR:必须存在且有内容
|
|
78
|
-
lib_dir = os.environ.get("SOLVEA_LIB_DIR", "")
|
|
79
|
-
if lib_dir:
|
|
80
|
-
p = Path(lib_dir)
|
|
81
|
-
if not p.exists():
|
|
82
|
-
_fail("SOLVEA_LIB_DIR", f"路径不存在: {lib_dir}")
|
|
83
|
-
else:
|
|
84
|
-
subdirs = [d.name for d in p.iterdir() if d.is_dir()]
|
|
85
|
-
_ok("SOLVEA_LIB_DIR", f"{lib_dir} ({len(subdirs)} 个子包: {', '.join(sorted(subdirs)[:4])})")
|
|
86
|
-
else:
|
|
87
|
-
_fail("SOLVEA_LIB_DIR", "未注入")
|
|
88
|
-
|
|
89
|
-
# SOLVEA_ENV
|
|
90
|
-
env_val = os.environ.get("SOLVEA_ENV", "")
|
|
91
|
-
check("SOLVEA_ENV", bool(env_val), env_val or "未注入")
|
|
92
|
-
|
|
93
|
-
# ANTHROPIC_BASE_URL:不能指向真实 api.anthropic.com
|
|
94
|
-
base_url = os.environ.get("ANTHROPIC_BASE_URL", "")
|
|
95
|
-
if not base_url:
|
|
96
|
-
_fail("ANTHROPIC_BASE_URL", "未注入")
|
|
97
|
-
elif "api.anthropic.com" in base_url:
|
|
98
|
-
_fail("ANTHROPIC_BASE_URL", f"直接暴露真实端点: {base_url}")
|
|
99
|
-
else:
|
|
100
|
-
_ok("ANTHROPIC_BASE_URL", base_url)
|
|
101
|
-
|
|
102
|
-
openai_base_url = os.environ.get("OPENAI_BASE_URL", "")
|
|
103
|
-
if not openai_base_url:
|
|
104
|
-
_fail("OPENAI_BASE_URL", "未注入")
|
|
105
|
-
elif "api.openai.com" in openai_base_url:
|
|
106
|
-
_fail("OPENAI_BASE_URL", f"直接暴露真实端点: {openai_base_url}")
|
|
107
|
-
else:
|
|
108
|
-
_ok("OPENAI_BASE_URL", openai_base_url)
|
|
109
|
-
|
|
110
|
-
print()
|
|
111
|
-
print("ENV — 可选变量")
|
|
112
|
-
for key, desc in [
|
|
113
|
-
("SOLVEA_TOKEN", "staging JWT token"),
|
|
114
|
-
("SOLVEA_PROD_TOKEN", "prod JWT token"),
|
|
115
|
-
("SOLVEA_PROJECT_ID", "项目 ID"),
|
|
116
|
-
]:
|
|
117
|
-
val = os.environ.get(key, "")
|
|
118
|
-
print(f" {'✅' if val else '⬜'} {key}={'(set)' if val else '(not set)'} {desc}")
|
|
119
|
-
|
|
120
|
-
print()
|
|
121
|
-
print("CXClaw — 本地 CLI")
|
|
122
|
-
cli_path = shutil.which("npx")
|
|
123
|
-
if cli_path:
|
|
124
|
-
_ok("npx --yes @shulex/cxclawctl@latest", cli_path)
|
|
125
|
-
else:
|
|
126
|
-
_fail("npx --yes @shulex/cxclawctl@latest", "未找到 Node.js/npm;业务命令和 SLS 直查不可用")
|
|
127
|
-
|
|
128
|
-
print()
|
|
129
|
-
print("ENV — 安全校验(敏感变量不应泄露)")
|
|
130
|
-
for key, desc in [
|
|
131
|
-
("ANTHROPIC_API_KEY", "应为哑键 sk-sandbox-proxy,或不存在"),
|
|
132
|
-
("OPENAI_API_KEY", "应为哑键 sk-sandbox-proxy,或不存在"),
|
|
133
|
-
("GPT_BASE_URL", "Java 内部地址不应暴露到沙箱"),
|
|
134
|
-
("GPT_X_TOKEN", "Java 内部鉴权 token 不应暴露到沙箱"),
|
|
135
|
-
("SOLVEA_SLS_ACCESS_KEY_ID", "SLS AK 不应暴露到沙箱"),
|
|
136
|
-
("SOLVEA_SLS_ACCESS_KEY_SECRET", "SLS SK 不应暴露到沙箱"),
|
|
137
|
-
("DATABASE_URL", "数据库 URL 不应暴露"),
|
|
138
|
-
("POSTGRES_PASSWORD", "PG 密码不应暴露"),
|
|
139
|
-
("AWS_SECRET_ACCESS_KEY", "AWS 密钥不应暴露"),
|
|
140
|
-
]:
|
|
141
|
-
val = os.environ.get(key, "")
|
|
142
|
-
if not val:
|
|
143
|
-
_ok(f"{key} 未注入", desc)
|
|
144
|
-
elif key in {"ANTHROPIC_API_KEY", "OPENAI_API_KEY"} and val == "sk-sandbox-proxy":
|
|
145
|
-
_ok(f"{key}=sk-sandbox-proxy", "哑键,正常")
|
|
146
|
-
else:
|
|
147
|
-
print(f" ⚠️ {key} 已注入(值已隐藏) ← {desc}")
|
|
148
|
-
|
|
149
|
-
print()
|
|
150
|
-
print("ENV — 所有注入变量(敏感值已脱敏)")
|
|
151
|
-
all_env = sorted(os.environ.items())
|
|
152
|
-
for k, v in all_env:
|
|
153
|
-
display = _mask(v) if _is_sensitive(k) else v
|
|
154
|
-
# 超长值截断显示
|
|
155
|
-
if len(display) > 120:
|
|
156
|
-
display = display[:117] + "..."
|
|
157
|
-
print(f" {k}={display}")
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
161
|
-
# SCENARIO: fs
|
|
162
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
163
|
-
|
|
164
|
-
def _check_path(rel: str, label: str, expect_writable: bool) -> None:
|
|
165
|
-
p = Path(rel)
|
|
166
|
-
exists = p.exists()
|
|
167
|
-
writable = os.access(p, os.W_OK) if exists else False
|
|
168
|
-
status = "✅" if exists else "⬜"
|
|
169
|
-
rw = "[rw]" if writable else "[ro]"
|
|
170
|
-
warn = " ⚠️ 权限异常" if exists and (writable != expect_writable) else ""
|
|
171
|
-
extra = ""
|
|
172
|
-
if exists and p.is_dir():
|
|
173
|
-
count = sum(1 for _ in p.rglob("*") if _.is_file())
|
|
174
|
-
extra = f" {count} files"
|
|
175
|
-
elif exists and p.is_file():
|
|
176
|
-
extra = f" {p.stat().st_size} bytes"
|
|
177
|
-
print(f" {status} {label}")
|
|
178
|
-
print(f" {rel}{extra} {rw}{warn}")
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def check_fs() -> None:
|
|
182
|
-
print(_SEC)
|
|
183
|
-
print("FS — 目录结构")
|
|
184
|
-
_state_file = STATE_DIR / "inspect.json"
|
|
185
|
-
state = {}
|
|
186
|
-
if _state_file.exists():
|
|
187
|
-
try:
|
|
188
|
-
state = json.loads(_state_file.read_text(encoding="utf-8"))
|
|
189
|
-
except Exception:
|
|
190
|
-
pass
|
|
191
|
-
results_path = state.get("results_path")
|
|
192
|
-
|
|
193
|
-
# workspace 子目录:SandboxManager 创建,expect_writable=True
|
|
194
|
-
for rel, label in [
|
|
195
|
-
("workspace", "workspace 根目录(可写)"),
|
|
196
|
-
("workspace/outputs", "outputs(每轮新文件)"),
|
|
197
|
-
("workspace/state", "state(固定文件名跨轮覆盖)"),
|
|
198
|
-
("workspace/tmp", "tmp(当轮临时)"),
|
|
199
|
-
]:
|
|
200
|
-
_check_path(rel, label, expect_writable=True)
|
|
201
|
-
|
|
202
|
-
# mounted 目录:只读性由 ManifestBackend 保证,OS 层面目录本身可写属正常,不检查权限
|
|
203
|
-
# eager 模型下 chat-files/agent 与 chat-files/state 已不再使用
|
|
204
|
-
for rel, label in [
|
|
205
|
-
("mounted", "mounted 根目录"),
|
|
206
|
-
("mounted/chat-files/user", "用户上传文件"),
|
|
207
|
-
("mounted/tenant-files", "租户级共享文件"),
|
|
208
|
-
]:
|
|
209
|
-
p = Path(rel)
|
|
210
|
-
status = "✅" if p.exists() else "⬜"
|
|
211
|
-
count = sum(1 for _ in p.rglob("*") if _.is_file()) if p.exists() else 0
|
|
212
|
-
print(f" {status} {label}")
|
|
213
|
-
print(f" {rel} {count} files [逻辑只读,OS 可写属正常]")
|
|
214
|
-
|
|
215
|
-
# 上一轮 agent outputs:eager 还原到 workspace/outputs/{skill}/,首次运行时空属正常
|
|
216
|
-
skill_outputs = Path("workspace/outputs") / SKILL_NAME
|
|
217
|
-
if skill_outputs.exists():
|
|
218
|
-
files = sorted(f for f in skill_outputs.rglob("*") if f.is_file())
|
|
219
|
-
_info("上一轮 agent outputs", f"{skill_outputs} ({len(files)} files)")
|
|
220
|
-
else:
|
|
221
|
-
_info("上一轮 agent outputs", f"{skill_outputs} (首次运行,尚无历史输出)")
|
|
222
|
-
|
|
223
|
-
# skills / skill-lib:文件级只读(OS chmod 0o444),目录本身可写(允许 rmtree),检查文件权限
|
|
224
|
-
for rel, label in [
|
|
225
|
-
("skills/" + SKILL_NAME, "skill 目录"),
|
|
226
|
-
]:
|
|
227
|
-
p = Path(rel)
|
|
228
|
-
if p.exists():
|
|
229
|
-
files = list(p.rglob("*"))
|
|
230
|
-
file_list = [f for f in files if f.is_file()]
|
|
231
|
-
readonly_files = [f for f in file_list if not os.access(f, os.W_OK)]
|
|
232
|
-
all_ro = len(readonly_files) == len(file_list) and len(file_list) > 0
|
|
233
|
-
status = "✅" if all_ro else ("⬜" if not file_list else "⚠️ ")
|
|
234
|
-
detail = f"{len(readonly_files)}/{len(file_list)} 个文件只读"
|
|
235
|
-
print(f" {status} {label} ({detail})")
|
|
236
|
-
print(f" {rel}")
|
|
237
|
-
else:
|
|
238
|
-
print(f" ⬜ {label}")
|
|
239
|
-
print(f" {rel} (不存在)")
|
|
240
|
-
|
|
241
|
-
lib_dir = os.environ.get("SOLVEA_LIB_DIR", "")
|
|
242
|
-
if lib_dir:
|
|
243
|
-
p = Path(lib_dir)
|
|
244
|
-
if p.exists():
|
|
245
|
-
file_list = [f for f in p.rglob("*") if f.is_file()]
|
|
246
|
-
readonly_files = [f for f in file_list if not os.access(f, os.W_OK)]
|
|
247
|
-
all_ro = len(readonly_files) == len(file_list) and len(file_list) > 0
|
|
248
|
-
status = "✅" if all_ro else ("⬜" if not file_list else "⚠️ ")
|
|
249
|
-
detail = f"{len(readonly_files)}/{len(file_list)} 个文件只读"
|
|
250
|
-
print(f" {status} skill-lib 目录 ({detail})")
|
|
251
|
-
print(f" {lib_dir}")
|
|
252
|
-
else:
|
|
253
|
-
_fail("skill-lib 目录", f"路径不存在: {lib_dir}")
|
|
254
|
-
|
|
255
|
-
if results_path:
|
|
256
|
-
_check_path(results_path, "本轮处理结果", True)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
260
|
-
# SCENARIO: execute
|
|
261
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
262
|
-
|
|
263
|
-
def _bash(cmd: str) -> tuple[int, str]:
|
|
264
|
-
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
|
|
265
|
-
return r.returncode, (r.stdout.strip() or r.stderr.strip())
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def check_execute() -> None:
|
|
269
|
-
print(_SEC)
|
|
270
|
-
print("EXECUTE — bash 执行环境验证")
|
|
271
|
-
|
|
272
|
-
# 1. CWD
|
|
273
|
-
cwd = os.getcwd()
|
|
274
|
-
print(f"\n CWD: {cwd}")
|
|
275
|
-
check("CWD 可访问", os.path.isdir(cwd))
|
|
276
|
-
check("workspace/ 相对路径可达",
|
|
277
|
-
os.path.isdir("workspace"),
|
|
278
|
-
"Path('workspace') 应等价于 CWD/workspace")
|
|
279
|
-
|
|
280
|
-
# 2. $WORKSPACE_DIR 从 bash 里读
|
|
281
|
-
print()
|
|
282
|
-
rc, out = _bash("echo $WORKSPACE_DIR")
|
|
283
|
-
check("$WORKSPACE_DIR 在 bash 中已设置", bool(out) and out != "", out or "(empty)")
|
|
284
|
-
if out:
|
|
285
|
-
p = Path(out)
|
|
286
|
-
check("$WORKSPACE_DIR 路径存在", p.exists(), out)
|
|
287
|
-
check("$WORKSPACE_DIR 可写", p.exists() and os.access(p, os.W_OK), out)
|
|
288
|
-
|
|
289
|
-
# 3. $SOLVEA_LIB_DIR 从 bash 里读
|
|
290
|
-
rc2, out2 = _bash("echo $SOLVEA_LIB_DIR")
|
|
291
|
-
check("$SOLVEA_LIB_DIR 在 bash 中已设置", bool(out2) and out2 != "", out2 or "(empty)")
|
|
292
|
-
if out2:
|
|
293
|
-
check("$SOLVEA_LIB_DIR 路径存在", Path(out2).exists(), out2)
|
|
294
|
-
|
|
295
|
-
# 4. 相对路径写文件再读回(验证 CWD 对齐)
|
|
296
|
-
print()
|
|
297
|
-
test_file = "workspace/tmp/execute_path_test.txt"
|
|
298
|
-
rc3, _ = _bash(f"mkdir -p workspace/tmp && echo 'execute-ok' > {test_file}")
|
|
299
|
-
if rc3 == 0:
|
|
300
|
-
content = Path(test_file).read_text().strip() if Path(test_file).exists() else ""
|
|
301
|
-
check("bash 相对路径写文件可被 Python 读到", content == "execute-ok",
|
|
302
|
-
f"actual={content!r}" if content != "execute-ok" else "")
|
|
303
|
-
Path(test_file).unlink(missing_ok=True)
|
|
304
|
-
else:
|
|
305
|
-
_fail("bash 相对路径写文件", "命令失败")
|
|
306
|
-
|
|
307
|
-
# 5. python3 解释器路径
|
|
308
|
-
rc4, python_path = _bash("which python3")
|
|
309
|
-
check("python3 可找到", rc4 == 0, python_path)
|
|
310
|
-
if python_path:
|
|
311
|
-
is_venv = ".venv" in python_path or "envs" in python_path
|
|
312
|
-
print(f" {'✅' if is_venv else '⚠️ '} python3 路径: {python_path}"
|
|
313
|
-
+ (" (venv)" if is_venv else " ← 可能是系统 python,非 venv"))
|
|
314
|
-
|
|
315
|
-
# 6. PATH 包含 venv bin
|
|
316
|
-
rc5, path_val = _bash("echo $PATH")
|
|
317
|
-
has_venv_in_path = ".venv" in path_val or "envs" in path_val
|
|
318
|
-
check("PATH 包含 venv bin 目录", has_venv_in_path,
|
|
319
|
-
"(确保 python3 解释器和依赖库来自 venv)")
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
323
|
-
# SCENARIO: paths
|
|
324
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
325
|
-
|
|
326
|
-
def check_paths() -> None:
|
|
327
|
-
print(_SEC)
|
|
328
|
-
print("PATHS — 虚拟路径行为(deepagent 工具 / bash execute / 脚本内部)")
|
|
329
|
-
|
|
330
|
-
# 1. Python bootstrap 路径常量(脚本内部访问)
|
|
331
|
-
print("\n [1] Python 路径常量(bootstrap 注入)")
|
|
332
|
-
|
|
333
|
-
# 必须存在的父目录(SandboxManager 创建)
|
|
334
|
-
for name, path in [
|
|
335
|
-
("WORKSPACE", WORKSPACE),
|
|
336
|
-
("MOUNTED_USER", MOUNTED_USER),
|
|
337
|
-
("MOUNTED_TENANT", MOUNTED_TENANT),
|
|
338
|
-
]:
|
|
339
|
-
p = Path(str(path))
|
|
340
|
-
check(f"{name}", p.exists(), str(p))
|
|
341
|
-
|
|
342
|
-
# skill 子目录:脚本首次写文件时自行 mkdir,此处只验证路径常量值合理(父目录存在)
|
|
343
|
-
for name, path in [
|
|
344
|
-
("OUTPUTS_DIR", OUTPUTS_DIR),
|
|
345
|
-
("STATE_DIR", STATE_DIR),
|
|
346
|
-
("TMP_DIR", TMP_DIR),
|
|
347
|
-
]:
|
|
348
|
-
p = Path(str(path))
|
|
349
|
-
parent_ok = p.parent.exists()
|
|
350
|
-
status = "✅" if parent_ok else "❌"
|
|
351
|
-
exist_hint = "已存在" if p.exists() else "未创建(首次写时 mkdir)"
|
|
352
|
-
print(f" {status} {name} ({p} {exist_hint})")
|
|
353
|
-
if not parent_ok:
|
|
354
|
-
_failures.append(f"{name}: 父目录不存在 {p.parent}")
|
|
355
|
-
|
|
356
|
-
# 2. bash execute 路径可达性(bash execute 工具)
|
|
357
|
-
print("\n [2] bash 虚拟路径可达(ls)")
|
|
358
|
-
for vpath in ["/workspace", "/mounted", "/skills", "/skill-lib"]:
|
|
359
|
-
rc, out = _bash(f"ls {vpath} 2>&1 | head -3")
|
|
360
|
-
check(f"bash: ls {vpath}", rc == 0, out[:80] if out else "(no output)")
|
|
361
|
-
|
|
362
|
-
# 3. bash write → Python read(CWD / 路径映射对齐)
|
|
363
|
-
print("\n [3] bash 写 workspace/tmp → Python 读回")
|
|
364
|
-
probe = "workspace/tmp/path_probe.txt"
|
|
365
|
-
rc, _ = _bash(f"mkdir -p workspace/tmp && echo 'pathok' > {probe}")
|
|
366
|
-
if rc == 0:
|
|
367
|
-
py_path = Path(probe)
|
|
368
|
-
content = py_path.read_text().strip() if py_path.exists() else ""
|
|
369
|
-
check("bash 写 → Python 读对齐", content == "pathok",
|
|
370
|
-
f"actual={content!r}" if content != "pathok" else "")
|
|
371
|
-
py_path.unlink(missing_ok=True)
|
|
372
|
-
else:
|
|
373
|
-
_fail("bash 写 workspace/tmp", "mkdir 或 echo 失败")
|
|
374
|
-
|
|
375
|
-
# 4. Python write → bash read(反向验证)
|
|
376
|
-
print("\n [4] Python 写 workspace/tmp → bash 读回")
|
|
377
|
-
TMP_DIR.mkdir(parents=True, exist_ok=True)
|
|
378
|
-
probe2 = TMP_DIR / "path_probe2.txt"
|
|
379
|
-
probe2.write_text("pyok", encoding="utf-8")
|
|
380
|
-
rc2, out2 = _bash(f"cat {probe2}")
|
|
381
|
-
check("Python 写 → bash 读对齐", out2.strip() == "pyok",
|
|
382
|
-
f"actual={out2.strip()!r}" if out2.strip() != "pyok" else "")
|
|
383
|
-
probe2.unlink(missing_ok=True)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
387
|
-
# SCENARIO: deps
|
|
388
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
389
|
-
|
|
390
|
-
def check_deps() -> None:
|
|
391
|
-
print(_SEC)
|
|
392
|
-
print("DEPS — 依赖库可用性")
|
|
393
|
-
|
|
394
|
-
libs = [
|
|
395
|
-
("anthropic", "Claude API"),
|
|
396
|
-
("httpx", "HTTP 客户端"),
|
|
397
|
-
("yaml", "YAML (pyyaml)"),
|
|
398
|
-
("openpyxl", "Excel 读写"),
|
|
399
|
-
("pandas", "数据处理"),
|
|
400
|
-
("bs4", "HTML 解析 (beautifulsoup4)"),
|
|
401
|
-
("pypdf", "PDF 解析"),
|
|
402
|
-
("pymupdf4llm", "PDF → Markdown"),
|
|
403
|
-
("markdown", "Markdown → HTML(poc/scripts/gen_poc_manual.py 渲染报告)"),
|
|
404
|
-
("docx", "Word 文档 (python-docx)"),
|
|
405
|
-
("markitdown", "多格式转 Markdown"),
|
|
406
|
-
("aiohttp", "异步 HTTP"),
|
|
407
|
-
("aliyun.log", "阿里云 SLS (aliyun-log-python-sdk)"),
|
|
408
|
-
]
|
|
409
|
-
for module, desc in libs:
|
|
410
|
-
try:
|
|
411
|
-
importlib.import_module(module)
|
|
412
|
-
_ok(f"{module:<16} {desc}")
|
|
413
|
-
except ImportError:
|
|
414
|
-
_info(f"{module:<16} {desc} ← 不可用(sandbox 组依赖未安装属正常)")
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
418
|
-
# main
|
|
419
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
420
|
-
|
|
421
|
-
SCENARIOS: dict[str, tuple[str, object]] = {
|
|
422
|
-
"env": ("ENV — 环境变量注入与安全", check_env),
|
|
423
|
-
"fs": ("FS — 目录结构与读写权限", check_fs),
|
|
424
|
-
"execute": ("EXECUTE — bash 执行路径行为", check_execute),
|
|
425
|
-
"paths": ("PATHS — 虚拟路径行为(bash/Python对齐)", check_paths),
|
|
426
|
-
"deps": ("DEPS — 依赖库可用性", check_deps),
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
def main() -> None:
|
|
431
|
-
parser = argparse.ArgumentParser(description="沙箱状态检查")
|
|
432
|
-
parser.add_argument(
|
|
433
|
-
"--scenario",
|
|
434
|
-
choices=list(SCENARIOS) + ["all"],
|
|
435
|
-
default="all",
|
|
436
|
-
help="单项场景或全量(默认 all)",
|
|
437
|
-
)
|
|
438
|
-
args = parser.parse_args()
|
|
439
|
-
|
|
440
|
-
print(_SEC)
|
|
441
|
-
print("SANDBOX INSPECTOR")
|
|
442
|
-
print(_SEC)
|
|
443
|
-
|
|
444
|
-
if args.scenario == "all":
|
|
445
|
-
for name, (title, fn) in SCENARIOS.items():
|
|
446
|
-
fn()
|
|
447
|
-
else:
|
|
448
|
-
_, fn = SCENARIOS[args.scenario]
|
|
449
|
-
fn()
|
|
450
|
-
|
|
451
|
-
print()
|
|
452
|
-
print(_SEC)
|
|
453
|
-
if _failures:
|
|
454
|
-
print(f"RESULT: ❌ FAIL ({len(_failures)} 项)")
|
|
455
|
-
for f in _failures:
|
|
456
|
-
print(f" - {f}")
|
|
457
|
-
else:
|
|
458
|
-
print("RESULT: ✅ PASS")
|
|
459
|
-
print(_SEC)
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if __name__ == "__main__":
|
|
463
|
-
main()
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""验证沙箱内 internal Claude proxy 是否可用。独立运行,不依赖 sandbox_inspect。"""
|
|
3
|
-
import os, sys
|
|
4
|
-
sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
|
|
5
|
-
from bootstrap import *
|
|
6
|
-
|
|
7
|
-
import time
|
|
8
|
-
|
|
9
|
-
def main():
|
|
10
|
-
base_url = os.environ.get("ANTHROPIC_BASE_URL", "")
|
|
11
|
-
api_key = os.environ.get("ANTHROPIC_API_KEY", "")
|
|
12
|
-
print(f"ANTHROPIC_BASE_URL = {base_url or '(not set)'}")
|
|
13
|
-
print(f"ANTHROPIC_API_KEY = {api_key or '(not set)'}")
|
|
14
|
-
|
|
15
|
-
if not base_url:
|
|
16
|
-
print("FAIL: ANTHROPIC_BASE_URL 未设置")
|
|
17
|
-
sys.exit(1)
|
|
18
|
-
if "api.anthropic.com" in base_url:
|
|
19
|
-
print("WARN: ANTHROPIC_BASE_URL 直接指向真实端点,非 internal proxy")
|
|
20
|
-
|
|
21
|
-
import anthropic
|
|
22
|
-
client = anthropic.Anthropic(timeout=30.0)
|
|
23
|
-
|
|
24
|
-
model = "claude-haiku-4-5-20251001"
|
|
25
|
-
prompt = "reply with the single word: ok"
|
|
26
|
-
print(f"\n>>> model : {model}")
|
|
27
|
-
print(f">>> prompt: {prompt}")
|
|
28
|
-
print(f"calling {base_url} ...")
|
|
29
|
-
t0 = time.time()
|
|
30
|
-
try:
|
|
31
|
-
resp = client.messages.create(
|
|
32
|
-
model=model,
|
|
33
|
-
max_tokens=16,
|
|
34
|
-
messages=[{"role": "user", "content": prompt}],
|
|
35
|
-
)
|
|
36
|
-
elapsed = time.time() - t0
|
|
37
|
-
reply = resp.content[0].text.strip() if resp.content else ""
|
|
38
|
-
print(f"<<< reply : {reply!r}")
|
|
39
|
-
print(f"PASS model={resp.model}"
|
|
40
|
-
f" in={resp.usage.input_tokens} out={resp.usage.output_tokens}"
|
|
41
|
-
f" elapsed={elapsed:.2f}s")
|
|
42
|
-
except Exception as e:
|
|
43
|
-
elapsed = time.time() - t0
|
|
44
|
-
print(f"<<< reply : (none)")
|
|
45
|
-
print(f"FAIL elapsed={elapsed:.2f}s error={e}")
|
|
46
|
-
sys.exit(1)
|
|
47
|
-
|
|
48
|
-
if __name__ == "__main__":
|
|
49
|
-
main()
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""验证沙箱内 internal OpenAI proxy 是否可用。独立运行。"""
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
import time
|
|
6
|
-
|
|
7
|
-
sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
|
|
8
|
-
from bootstrap import *
|
|
9
|
-
|
|
10
|
-
from openai import OpenAI
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def main():
|
|
14
|
-
base_url = os.environ.get("OPENAI_BASE_URL", "")
|
|
15
|
-
api_key = os.environ.get("OPENAI_API_KEY", "")
|
|
16
|
-
print(f"OPENAI_BASE_URL = {base_url or '(not set)'}")
|
|
17
|
-
print(f"OPENAI_API_KEY = {api_key or '(not set)'}")
|
|
18
|
-
if not base_url:
|
|
19
|
-
print("FAIL: OPENAI_BASE_URL 未设置")
|
|
20
|
-
sys.exit(1)
|
|
21
|
-
if "api.openai.com" in base_url:
|
|
22
|
-
print("WARN: OPENAI_BASE_URL 直接指向真实端点,非 internal proxy")
|
|
23
|
-
|
|
24
|
-
client = OpenAI(timeout=30.0)
|
|
25
|
-
t0 = time.time()
|
|
26
|
-
try:
|
|
27
|
-
response = client.chat.completions.create(
|
|
28
|
-
model="server-managed",
|
|
29
|
-
max_tokens=16,
|
|
30
|
-
temperature=0,
|
|
31
|
-
messages=[{"role": "user", "content": "reply with the single word: ok"}],
|
|
32
|
-
)
|
|
33
|
-
reply = response.choices[0].message.content or ""
|
|
34
|
-
print(f"PASS model={response.model} reply={reply!r} elapsed={time.time() - t0:.2f}s")
|
|
35
|
-
except Exception as exc:
|
|
36
|
-
print(f"FAIL elapsed={time.time() - t0:.2f}s error={exc}")
|
|
37
|
-
sys.exit(1)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if __name__ == "__main__":
|
|
41
|
-
main()
|