coze_lab 0.1.18 → 0.1.20
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/index.js +58 -23
- package/package.json +1 -1
- package/scripts/codex/cozeloop_hook.py +14 -1
package/index.js
CHANGED
|
@@ -4464,6 +4464,10 @@ function mergeJson(filepath, mergeFn) {
|
|
|
4464
4464
|
return mergeFn(existing);
|
|
4465
4465
|
}
|
|
4466
4466
|
|
|
4467
|
+
function shellEnvLine(key, value) {
|
|
4468
|
+
return `${key}='${String(value).replace(/'/g, `'\\''`)}'`;
|
|
4469
|
+
}
|
|
4470
|
+
|
|
4467
4471
|
// writeClaudeCodeHook 配置 Claude Code 的 hook。
|
|
4468
4472
|
// configBaseDir 缺省 process.cwd()(全局/项目级);传入 agent 的 workspace 则 per-agent:
|
|
4469
4473
|
// settings.json + 凭证写进 <configBaseDir>/.claude,仅该 agent(以此为 cwd 启动)生效。
|
|
@@ -4565,24 +4569,29 @@ function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud) {
|
|
|
4565
4569
|
writeHookScript(refreshScript, readScript('shared/cozeloop_refresh.py'));
|
|
4566
4570
|
|
|
4567
4571
|
// 2. Write env file with chmod 600
|
|
4568
|
-
// 云端(cloud):不落明文 token,hook 运行时从环境变量 COZE_API_TOKEN 读取。
|
|
4569
4572
|
const envLines = [
|
|
4570
|
-
|
|
4573
|
+
shellEnvLine('COZELOOP_WORKSPACE_ID', workspaceId),
|
|
4571
4574
|
];
|
|
4572
|
-
if (
|
|
4573
|
-
|
|
4575
|
+
if (cloud) {
|
|
4576
|
+
if (process.env.COZELOOP_API_TOKEN) {
|
|
4577
|
+
envLines.push(shellEnvLine('COZELOOP_API_TOKEN', process.env.COZELOOP_API_TOKEN));
|
|
4578
|
+
} else if (process.env.COZE_API_TOKEN) {
|
|
4579
|
+
envLines.push(shellEnvLine('COZE_API_TOKEN', process.env.COZE_API_TOKEN));
|
|
4580
|
+
}
|
|
4581
|
+
} else {
|
|
4582
|
+
envLines.push(shellEnvLine('COZELOOP_API_TOKEN', token));
|
|
4574
4583
|
}
|
|
4575
|
-
envLines.push(
|
|
4576
|
-
envLines.push(
|
|
4584
|
+
envLines.push(shellEnvLine('CODEX_HOME', home));
|
|
4585
|
+
envLines.push(shellEnvLine('COZELOOP_HOOK_LOG', logFile));
|
|
4577
4586
|
envLines.push('TRACE_TO_COZELOOP=true');
|
|
4578
4587
|
if (process.env.COZELOOP_API_BASE_URL) {
|
|
4579
|
-
envLines.push(
|
|
4588
|
+
envLines.push(shellEnvLine('COZELOOP_API_BASE_URL', process.env.COZELOOP_API_BASE_URL));
|
|
4580
4589
|
} else if (process.env.OTEL_ENDPOINT) {
|
|
4581
|
-
envLines.push(
|
|
4590
|
+
envLines.push(shellEnvLine('OTEL_ENDPOINT', process.env.OTEL_ENDPOINT));
|
|
4582
4591
|
}
|
|
4583
4592
|
// PPE 泳道:cozeloop SDK 读这两个环境变量,自动注入 x-tt-env / x-use-ppe header
|
|
4584
|
-
envLines.push(
|
|
4585
|
-
envLines.push(
|
|
4593
|
+
envLines.push(shellEnvLine('x_tt_env', PPE_TT_ENV));
|
|
4594
|
+
envLines.push(shellEnvLine('x_use_ppe', PPE_USE_PPE));
|
|
4586
4595
|
const envContent = envLines.join('\n') + '\n';
|
|
4587
4596
|
try {
|
|
4588
4597
|
fs.writeFileSync(envFile, envContent, { mode: 0o600 });
|
|
@@ -4660,10 +4669,38 @@ function normalizeTraceAgentIds(ids) {
|
|
|
4660
4669
|
.filter(Boolean);
|
|
4661
4670
|
}
|
|
4662
4671
|
|
|
4663
|
-
function
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4672
|
+
function getCloudCozeloopApiBaseUrl() {
|
|
4673
|
+
const raw = process.env.COZELOOP_API_BASE_URL || process.env.OTEL_ENDPOINT || '';
|
|
4674
|
+
const base = raw.trim().replace(/\/+$/, '');
|
|
4675
|
+
if (!base) return '';
|
|
4676
|
+
if (base.endsWith('/v1/loop/opentelemetry/v1/traces')) {
|
|
4677
|
+
return base.slice(0, -'/v1/loop/opentelemetry/v1/traces'.length).replace(/\/+$/, '');
|
|
4678
|
+
}
|
|
4679
|
+
if (base.endsWith('/api/v1/loop/opentelemetry/v1/traces')) {
|
|
4680
|
+
return base.slice(0, -'/v1/loop/opentelemetry/v1/traces'.length).replace(/\/+$/, '');
|
|
4681
|
+
}
|
|
4682
|
+
if (base.endsWith('/v1/loop/opentelemetry')) {
|
|
4683
|
+
return base.slice(0, -'/v1/loop/opentelemetry'.length).replace(/\/+$/, '');
|
|
4684
|
+
}
|
|
4685
|
+
if (base.endsWith('/api/v1/loop/opentelemetry')) {
|
|
4686
|
+
return base.slice(0, -'/loop/opentelemetry'.length).replace(/\/+$/, '');
|
|
4687
|
+
}
|
|
4688
|
+
if (base.endsWith('/api/v1')) {
|
|
4689
|
+
return base.slice(0, -'/v1'.length).replace(/\/+$/, '');
|
|
4690
|
+
}
|
|
4691
|
+
return base;
|
|
4692
|
+
}
|
|
4693
|
+
|
|
4694
|
+
function getCozeloopApiBaseUrl(cloud) {
|
|
4695
|
+
return cloud ? (getCloudCozeloopApiBaseUrl() || COZE_API) : COZE_API;
|
|
4696
|
+
}
|
|
4697
|
+
|
|
4698
|
+
function getOtelEndpointBase(cloud) {
|
|
4699
|
+
return `${getCozeloopApiBaseUrl(cloud).replace(/\/+$/, '')}/v1/loop/opentelemetry`;
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4702
|
+
function getOtelTracesUrl(cloud) {
|
|
4703
|
+
return `${getOtelEndpointBase(cloud).replace(/\/+$/, '')}/v1/traces`;
|
|
4667
4704
|
}
|
|
4668
4705
|
|
|
4669
4706
|
function applyOpenClawPluginConfig(existing, token, workspaceId, agentId, cloud) {
|
|
@@ -4685,7 +4722,7 @@ function applyOpenClawPluginConfig(existing, token, workspaceId, agentId, cloud)
|
|
|
4685
4722
|
if (!existing.plugins.entries[PLUGIN].config) existing.plugins.entries[PLUGIN].config = {};
|
|
4686
4723
|
const pcfg = existing.plugins.entries[PLUGIN].config;
|
|
4687
4724
|
pcfg.authorization = `Bearer ${token}`;
|
|
4688
|
-
pcfg.endpoint =
|
|
4725
|
+
pcfg.endpoint = getOtelEndpointBase(cloud);
|
|
4689
4726
|
pcfg.workspaceId = workspaceId;
|
|
4690
4727
|
pcfg.debug = true;
|
|
4691
4728
|
// per-agent trace 放行:把当前 agentId 并入 traceAgentIds(去重、归一为小写,
|
|
@@ -4820,7 +4857,7 @@ function httpsPost(url, body, extraHeaders) {
|
|
|
4820
4857
|
const data = JSON.stringify(body);
|
|
4821
4858
|
const u = new URL(url);
|
|
4822
4859
|
const req = https.request(
|
|
4823
|
-
{ hostname: u.hostname, path: u.pathname + u.search, method: 'POST',
|
|
4860
|
+
{ hostname: u.hostname, port: u.port || undefined, path: u.pathname + u.search, method: 'POST',
|
|
4824
4861
|
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data),
|
|
4825
4862
|
'x-tt-env': PPE_TT_ENV, 'x-use-ppe': PPE_USE_PPE,
|
|
4826
4863
|
...(extraHeaders || {}) } },
|
|
@@ -4841,7 +4878,7 @@ function httpsPost(url, body, extraHeaders) {
|
|
|
4841
4878
|
// 只看 HTTP 状态码(2xx=通),不回查 trace 是否落库——回查由外部查询方完成。
|
|
4842
4879
|
// pairCode 写进 span 的 pair_code attribute,供查询方按该字段过滤回查;缺省自动生成。
|
|
4843
4880
|
// 不在函数内退出,退出行为交给调用方(主流程 Step 6 / 独立命令 --verify)。
|
|
4844
|
-
async function verifyTraceReport(token, workspaceId, pairCode,
|
|
4881
|
+
async function verifyTraceReport(token, workspaceId, pairCode, tracesUrl) {
|
|
4845
4882
|
const traceId = crypto.randomBytes(16).toString('hex'); // 32 hex chars
|
|
4846
4883
|
const spanId = crypto.randomBytes(8).toString('hex'); // 16 hex chars
|
|
4847
4884
|
const nowNs = String(Date.now() * 1_000_000); // OTLP 要求纳秒 unix 时间(字符串)
|
|
@@ -4874,12 +4911,10 @@ async function verifyTraceReport(token, workspaceId, pairCode, baseUrl) {
|
|
|
4874
4911
|
};
|
|
4875
4912
|
|
|
4876
4913
|
let res;
|
|
4877
|
-
|
|
4878
|
-
// 缺省回退到 api.coze.cn 直连。base 已去尾部斜杠。
|
|
4879
|
-
const apiBase = (baseUrl || COZE_API).replace(/\/+$/, '');
|
|
4914
|
+
const url = tracesUrl || getOtelTracesUrl(false);
|
|
4880
4915
|
try {
|
|
4881
4916
|
res = await httpsPost(
|
|
4882
|
-
|
|
4917
|
+
url,
|
|
4883
4918
|
otlpBody,
|
|
4884
4919
|
{ Authorization: `Bearer ${token}`, 'cozeloop-workspace-id': workspaceId },
|
|
4885
4920
|
);
|
|
@@ -5230,7 +5265,7 @@ async function main() {
|
|
|
5230
5265
|
info('验证 trace 上报链路...');
|
|
5231
5266
|
const token = await getValidToken(); // 无凭证会自动走登录/刷新
|
|
5232
5267
|
console.log('');
|
|
5233
|
-
const result = await verifyTraceReport(token, WORKSPACE_ID, args.pairCode,
|
|
5268
|
+
const result = await verifyTraceReport(token, WORKSPACE_ID, args.pairCode, getOtelTracesUrl(false));
|
|
5234
5269
|
process.exit(result.success ? 0 : 1);
|
|
5235
5270
|
}
|
|
5236
5271
|
|
|
@@ -5352,7 +5387,7 @@ async function main() {
|
|
|
5352
5387
|
|
|
5353
5388
|
// Step 5: Verify trace reporting end-to-end
|
|
5354
5389
|
info('Step 5/5: 验证 trace 上报链路...');
|
|
5355
|
-
const verifyResult = await verifyTraceReport(token, WORKSPACE_ID, args.pairCode, args.cloud
|
|
5390
|
+
const verifyResult = await verifyTraceReport(token, WORKSPACE_ID, args.pairCode, getOtelTracesUrl(args.cloud));
|
|
5356
5391
|
if (verifyResult.success) {
|
|
5357
5392
|
cloudResult.verify = 'ok';
|
|
5358
5393
|
} else if (CLOUD_MODE) {
|
package/package.json
CHANGED
|
@@ -210,8 +210,16 @@ def _refresh_token(refresh_tok: str):
|
|
|
210
210
|
|
|
211
211
|
def _normalize_api_base_url(url: str) -> str:
|
|
212
212
|
base = (url or "").strip().rstrip("/")
|
|
213
|
+
if base.endswith(_OTEL_SUFFIX + "/v1/traces"):
|
|
214
|
+
return base[:-len(_OTEL_SUFFIX + "/v1/traces")].rstrip("/")
|
|
215
|
+
if base.endswith("/api/v1/loop/opentelemetry/v1/traces"):
|
|
216
|
+
return base[:-len("/v1/loop/opentelemetry/v1/traces")].rstrip("/")
|
|
213
217
|
if base.endswith(_OTEL_SUFFIX):
|
|
214
218
|
return base[:-len(_OTEL_SUFFIX)].rstrip("/")
|
|
219
|
+
if base.endswith("/api/v1/loop/opentelemetry"):
|
|
220
|
+
return base[:-len("/v1/loop/opentelemetry")].rstrip("/")
|
|
221
|
+
if base.endswith("/api/v1"):
|
|
222
|
+
return base[:-len("/v1")].rstrip("/")
|
|
215
223
|
return base
|
|
216
224
|
|
|
217
225
|
|
|
@@ -850,7 +858,12 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
850
858
|
hook_log(f"token resolved prefix={token[:12]}...")
|
|
851
859
|
print(f"[CozeLoop] Token 获取成功 ({token[:12]}...)", file=sys.stderr)
|
|
852
860
|
else:
|
|
853
|
-
hook_log(
|
|
861
|
+
hook_log(
|
|
862
|
+
"token missing "
|
|
863
|
+
f"has_cozeloop_token={bool(os.environ.get('COZELOOP_API_TOKEN'))} "
|
|
864
|
+
f"has_coze_token={bool(os.environ.get('COZE_API_TOKEN'))} "
|
|
865
|
+
f"api_base_url={bool(get_api_base_url())}"
|
|
866
|
+
)
|
|
854
867
|
print("[CozeLoop] 警告: 未找到有效 Token,上报可能失败", file=sys.stderr)
|
|
855
868
|
creds = _load_credentials()
|
|
856
869
|
workspace_id = (creds or {}).get("workspace_id") or os.environ.get("COZELOOP_WORKSPACE_ID", "") or _DEFAULT_WORKSPACE_ID
|