coze_lab 0.1.52 → 0.1.53
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 +80 -27
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -96,6 +96,7 @@ const C = {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
function log(prefix, color, msg) {
|
|
99
|
+
if (process.env.COZELAB_ONBOARD_VERBOSE !== '1') return;
|
|
99
100
|
console.log(`${color}${prefix}${C.reset} ${msg}`);
|
|
100
101
|
}
|
|
101
102
|
const ok = (msg) => log('[✓]', C.green, msg);
|
|
@@ -123,6 +124,7 @@ function errorBox(lines) {
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
function warnBox(lines) {
|
|
127
|
+
if (process.env.COZELAB_ONBOARD_VERBOSE !== '1') return;
|
|
126
128
|
box(lines, C.yellow);
|
|
127
129
|
}
|
|
128
130
|
|
|
@@ -549,6 +551,48 @@ function isVersionAtLeast(version, minVersion) {
|
|
|
549
551
|
return true;
|
|
550
552
|
}
|
|
551
553
|
|
|
554
|
+
// Codex docs now use [features].hooks as the canonical key; codex_hooks is a
|
|
555
|
+
// deprecated alias. Keep the alias only for explicitly detected older CLIs.
|
|
556
|
+
const CODEX_CANONICAL_HOOKS_FEATURE_MIN_VERSION = '0.134.0';
|
|
557
|
+
|
|
558
|
+
function codexHooksFeatureKey(version) {
|
|
559
|
+
const hasKnownVersion = parseVersionNumbers(version).length > 0;
|
|
560
|
+
if (hasKnownVersion && !isVersionAtLeast(version, CODEX_CANONICAL_HOOKS_FEATURE_MIN_VERSION)) {
|
|
561
|
+
return 'codex_hooks';
|
|
562
|
+
}
|
|
563
|
+
return 'hooks';
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function normalizeCodexHooksFeatureConfig(configToml, codexVersion) {
|
|
567
|
+
const featureKey = codexHooksFeatureKey(codexVersion);
|
|
568
|
+
const lines = String(configToml || '').replace(/\r\n/g, '\n').split('\n');
|
|
569
|
+
if (lines.length && lines[lines.length - 1] === '') lines.pop();
|
|
570
|
+
|
|
571
|
+
const featureHeaderRe = /^\s*\[features\]\s*(?:#.*)?$/;
|
|
572
|
+
const tableHeaderRe = /^\s*\[\[?[^\]]+\]\]?\s*(?:#.*)?$/;
|
|
573
|
+
const hookFeatureRe = /^\s*(hooks|codex_hooks)\s*=/;
|
|
574
|
+
const featureIdx = lines.findIndex((line) => featureHeaderRe.test(line));
|
|
575
|
+
|
|
576
|
+
if (featureIdx === -1) {
|
|
577
|
+
const out = [...lines];
|
|
578
|
+
if (out.length && out[out.length - 1].trim() !== '') out.push('');
|
|
579
|
+
out.push('[features]', `${featureKey} = true`);
|
|
580
|
+
return out.join('\n') + '\n';
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
let endIdx = featureIdx + 1;
|
|
584
|
+
while (endIdx < lines.length && !tableHeaderRe.test(lines[endIdx])) {
|
|
585
|
+
endIdx += 1;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const before = lines.slice(0, featureIdx);
|
|
589
|
+
const section = lines.slice(featureIdx, endIdx).filter((line, idx) => idx === 0 || !hookFeatureRe.test(line));
|
|
590
|
+
section.splice(1, 0, `${featureKey} = true`);
|
|
591
|
+
const after = lines.slice(endIdx);
|
|
592
|
+
|
|
593
|
+
return [...before, ...section, ...after].join('\n') + '\n';
|
|
594
|
+
}
|
|
595
|
+
|
|
552
596
|
// ─── 7. Hook writers ─────────────────────────────────────────────────────────
|
|
553
597
|
const fs = require('fs');
|
|
554
598
|
const path = require('path');
|
|
@@ -729,13 +773,14 @@ function writeClaudeCodeHook(token, workspaceId, pythonCmd, configBaseDir, cloud
|
|
|
729
773
|
// 传入动态目录(如 coze-bridge 的 /tmp/coze-bridge-codex-home-xxx)即可 per-agent 生效。
|
|
730
774
|
// 本地 --agent-id 从 config.patToken 写入 token,并用 COZELOOP_TOKEN_SOURCE 标记来源。
|
|
731
775
|
// cloud=true 时写 COZELAB_ONBOARD_CLOUD,并带入 sandbox 注入的 trace token。
|
|
732
|
-
function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud, tokenSource) {
|
|
776
|
+
function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud, tokenSource, codexVersion) {
|
|
733
777
|
const home = codexHome || path.join(os.homedir(), '.codex');
|
|
734
778
|
const hooksDir = path.join(home, 'hooks');
|
|
735
779
|
const hookScript = path.join(hooksDir, 'cozeloop_hook.py');
|
|
736
780
|
const envFile = path.join(hooksDir, 'cozeloop.env');
|
|
737
781
|
const logFile = path.join(hooksDir, 'cozeloop.log');
|
|
738
782
|
const hooksJson = path.join(home, 'hooks.json');
|
|
783
|
+
const configToml = path.join(home, 'config.toml');
|
|
739
784
|
|
|
740
785
|
// 1. Write Python hook script
|
|
741
786
|
ensureDir(hooksDir);
|
|
@@ -810,9 +855,22 @@ function writeCodexHook(token, workspaceId, pythonCmd, codexHome, cloud, tokenSo
|
|
|
810
855
|
errorBox([`ERROR: Cannot write ${hooksJson}`, '', e.message]);
|
|
811
856
|
}
|
|
812
857
|
ok(`Hook registered in ${hooksJson}`);
|
|
858
|
+
|
|
859
|
+
// 4. Normalize Codex hook feature flag. hooks.json carries hook definitions;
|
|
860
|
+
// config.toml carries the on/off feature gate. Re-running onboard should
|
|
861
|
+
// clean the deprecated codex_hooks alias for modern Codex versions.
|
|
862
|
+
const featureKey = codexHooksFeatureKey(codexVersion);
|
|
863
|
+
try {
|
|
864
|
+
const existingConfig = fs.existsSync(configToml) ? fs.readFileSync(configToml, 'utf8') : '';
|
|
865
|
+
const nextConfig = normalizeCodexHooksFeatureConfig(existingConfig, codexVersion);
|
|
866
|
+
atomicWriteFileSync(configToml, nextConfig);
|
|
867
|
+
} catch (e) {
|
|
868
|
+
errorBox([`ERROR: Cannot update ${configToml}`, '', e.message]);
|
|
869
|
+
}
|
|
870
|
+
ok(`Codex hooks feature enabled via [features].${featureKey} in ${configToml}`);
|
|
813
871
|
warn('Codex hook trust: 首次启动 Codex 会提示 "Hooks need review"。在该提示按 t(Trust all and continue),或在会话内运行 /hooks 后按 t,即可一次性信任全部 hook 启用 trace 上报。');
|
|
814
872
|
|
|
815
|
-
return { hookScript, envFile, hooksJson, logFile };
|
|
873
|
+
return { hookScript, envFile, hooksJson, configToml, logFile };
|
|
816
874
|
}
|
|
817
875
|
|
|
818
876
|
function resolveCodexHome(args) {
|
|
@@ -1291,8 +1349,6 @@ except Exception as e:
|
|
|
1291
1349
|
} else {
|
|
1292
1350
|
warn(`trace 上报失败: SDK selfcheck exit ${result.code}`);
|
|
1293
1351
|
info(`trace selfcheck api_base_url=${apiBase || COZE_API}, token_source=${tokenSource || 'unknown'}`);
|
|
1294
|
-
const snippet = String(body || '').slice(0, 300);
|
|
1295
|
-
if (snippet) console.log(snippet);
|
|
1296
1352
|
}
|
|
1297
1353
|
return { success, status: result.code || 0, body, traceId: '', pairCode: pair, apiBaseUrl: apiBase, tokenSource, logid: parsed?.logid || extractLogid(body) || '' };
|
|
1298
1354
|
}
|
|
@@ -1365,8 +1421,6 @@ async function verifyCloudTraceReport(token, workspaceId, pairCode, tokenSource)
|
|
|
1365
1421
|
info(`查询方可用 pair_code=${pair} 在 CozeLoop 回查确认该 trace 已落库。`);
|
|
1366
1422
|
} else {
|
|
1367
1423
|
warn(`trace 上报失败: HTTP ${res.status}${formatLogid(res.logid || '')}`);
|
|
1368
|
-
const snippet = (res.body || '').slice(0, 300);
|
|
1369
|
-
if (snippet) console.log(snippet);
|
|
1370
1424
|
}
|
|
1371
1425
|
return { success, status: res.status, body: res.body || '', traceId, pairCode: pair, apiBaseUrl: apiBase, tokenSource, logid: res.logid || extractLogid(res.body || '') };
|
|
1372
1426
|
} catch (e) {
|
|
@@ -1431,8 +1485,6 @@ async function verifyTraceReport(token, workspaceId, pairCode, tracesUrl) {
|
|
|
1431
1485
|
info(`查询方可用 pair_code=${pair} 在 CozeLoop 回查确认该 trace 已落库。`);
|
|
1432
1486
|
} else {
|
|
1433
1487
|
warn(`trace 上报失败: HTTP ${res.status}${formatLogid(res.logid || '')}`);
|
|
1434
|
-
const snippet = (res.body || '').slice(0, 300);
|
|
1435
|
-
if (snippet) console.log(snippet);
|
|
1436
1488
|
}
|
|
1437
1489
|
return { success, status: res.status, body: res.body, traceId, pairCode: pair, logid: res.logid || extractLogid(res.body || '') };
|
|
1438
1490
|
}
|
|
@@ -1534,8 +1586,6 @@ async function verifyOpenClawTraceLink(cloud, pairCode) {
|
|
|
1534
1586
|
ok(`openclaw 插件实际 token 上报正常 (token=${tokenPrefix}..., HTTP ${res.status}${formatLogid(res.logid || '')})`);
|
|
1535
1587
|
} else {
|
|
1536
1588
|
warn(`openclaw 插件实际 token 上报失败: HTTP ${res.status}${formatLogid(res.logid || '')} (token=${tokenPrefix}...)`);
|
|
1537
|
-
const snippet = (res.body || '').slice(0, 300);
|
|
1538
|
-
if (snippet) console.log(snippet);
|
|
1539
1589
|
// 4100/401 = 该 token 已失效。指出根因与修复方式。
|
|
1540
1590
|
if (res.status === 401 || /\b4100\b/.test(res.body || '')) {
|
|
1541
1591
|
info('插件配置的 token 已失效。运行时上报会 401,span 会丢失。');
|
|
@@ -1576,9 +1626,7 @@ function openClawNextStep(written) {
|
|
|
1576
1626
|
}
|
|
1577
1627
|
|
|
1578
1628
|
async function main() {
|
|
1579
|
-
console.log('');
|
|
1580
1629
|
info(`CozeLoop Onboard CLI starting... (coze_lab v${PACKAGE_VERSION})`);
|
|
1581
|
-
console.log('');
|
|
1582
1630
|
|
|
1583
1631
|
const args = validateArgs(parseArgs());
|
|
1584
1632
|
|
|
@@ -1607,7 +1655,6 @@ async function main() {
|
|
|
1607
1655
|
'请确认 ~/.coze/agents/<id>/config.json 包含 patToken 后重试。',
|
|
1608
1656
|
]);
|
|
1609
1657
|
}
|
|
1610
|
-
console.log('');
|
|
1611
1658
|
const result = await verifyTraceReport(token, WORKSPACE_ID, args.pairCode, getOtelTracesUrl(false));
|
|
1612
1659
|
// 若本机装了 openclaw 插件,额外校验插件【实际配置的静态 token】——通用 verify 用刚刷新的
|
|
1613
1660
|
// token 测不到它。任一失败则整体判失败(exit 1)。
|
|
@@ -1617,14 +1664,28 @@ async function main() {
|
|
|
1617
1664
|
try {
|
|
1618
1665
|
const cfg = JSON.parse(fs.readFileSync(ocConfigPath, 'utf8'));
|
|
1619
1666
|
if (cfg?.plugins?.entries?.['openclaw-cozeloop-trace']?.config?.authorization) {
|
|
1620
|
-
console.log('');
|
|
1621
1667
|
info('检测到 openclaw cozeloop-trace 插件,校验其实际 token...');
|
|
1622
1668
|
const ocRes = await verifyOpenClawTraceLink(args.cloud, args.pairCode);
|
|
1623
1669
|
ocOk = ocRes.success;
|
|
1624
1670
|
}
|
|
1625
1671
|
} catch { /* 读不了就跳过 openclaw 校验 */ }
|
|
1626
1672
|
}
|
|
1627
|
-
|
|
1673
|
+
if (result.success && ocOk) {
|
|
1674
|
+
successBox([
|
|
1675
|
+
`Verify: coze_lab v${PACKAGE_VERSION}`,
|
|
1676
|
+
`Trace: OK (pair_code=${result.pairCode})`,
|
|
1677
|
+
]);
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
errorBox([
|
|
1681
|
+
'ERROR: trace 上报自检失败',
|
|
1682
|
+
'',
|
|
1683
|
+
`HTTP ${result.status}`,
|
|
1684
|
+
`logid=${result.logid || extractLogid(result.body) || ''}`,
|
|
1685
|
+
(result.body || '').slice(0, 300),
|
|
1686
|
+
'',
|
|
1687
|
+
'请确认 CozeLoop Token 具备 Trace ingest 权限,再重试。',
|
|
1688
|
+
]);
|
|
1628
1689
|
}
|
|
1629
1690
|
|
|
1630
1691
|
const { agent } = args;
|
|
@@ -1638,7 +1699,6 @@ async function main() {
|
|
|
1638
1699
|
}
|
|
1639
1700
|
if (args.agentId) {
|
|
1640
1701
|
info(`目标 agent: ${args.agentId} (framework=${agent}, workspace=${agentWorkspace || 'N/A'}, deploy=${args.cloud ? 'cloud' : 'local'}, reason=${args.deployReason || 'n/a'})`);
|
|
1641
|
-
console.log('');
|
|
1642
1702
|
}
|
|
1643
1703
|
|
|
1644
1704
|
// Step 1: Resolve trace token.
|
|
@@ -1683,12 +1743,9 @@ async function main() {
|
|
|
1683
1743
|
]);
|
|
1684
1744
|
}
|
|
1685
1745
|
}
|
|
1686
|
-
console.log('');
|
|
1687
|
-
|
|
1688
1746
|
// Step 2: Detect agent binary
|
|
1689
1747
|
info('Step 2/5: Detecting agent...');
|
|
1690
1748
|
const version = detectAgent(agent, args.cloud);
|
|
1691
|
-
console.log('');
|
|
1692
1749
|
|
|
1693
1750
|
// Step 3: Environment checks
|
|
1694
1751
|
info('Step 3/5: Checking environment...');
|
|
@@ -1698,7 +1755,6 @@ async function main() {
|
|
|
1698
1755
|
checkCozeloopSdk(pythonCmd, { cloud: args.cloud });
|
|
1699
1756
|
}
|
|
1700
1757
|
checkVersionWhitelist(agent, version);
|
|
1701
|
-
console.log('');
|
|
1702
1758
|
|
|
1703
1759
|
// Step 4: Write hook configuration
|
|
1704
1760
|
info('Step 4/5: Writing hook configuration...');
|
|
@@ -1723,14 +1779,13 @@ async function main() {
|
|
|
1723
1779
|
info(`已创建云端 Codex 配置目录: ${codexHome}`);
|
|
1724
1780
|
}
|
|
1725
1781
|
if (codexHome) info(`Codex 配置目录: ${codexHome}`);
|
|
1726
|
-
written = writeCodexHook(token, WORKSPACE_ID, pythonCmd, codexHome, args.cloud, tokenSource);
|
|
1782
|
+
written = writeCodexHook(token, WORKSPACE_ID, pythonCmd, codexHome, args.cloud, tokenSource, version);
|
|
1727
1783
|
} else {
|
|
1728
1784
|
// openclaw:云端用 traceAgentIds allowlist 做 per-agent 放行。
|
|
1729
1785
|
written = writeOpenClawHook(token, WORKSPACE_ID, args.agentId, args.cloud, args.force, tokenSource) || {};
|
|
1730
1786
|
}
|
|
1731
1787
|
// 走到这里说明 detectAgent / 环境检查 / 写 hook 配置全部成功 → 注入成功。
|
|
1732
1788
|
cloudResult.inject = 'ok';
|
|
1733
|
-
console.log('');
|
|
1734
1789
|
|
|
1735
1790
|
// Success summary(用实际写入路径,per-agent / 自定义 CODEX_HOME 时也准确)
|
|
1736
1791
|
const summaryLines = [
|
|
@@ -1745,6 +1800,7 @@ async function main() {
|
|
|
1745
1800
|
} else if (agent === 'codex') {
|
|
1746
1801
|
summaryLines.push(`Hook script: ${written.hookScript || '~/.codex/hooks/cozeloop_hook.py'}`);
|
|
1747
1802
|
summaryLines.push(`Config: ${written.hooksJson || '~/.codex/hooks.json'}`);
|
|
1803
|
+
if (written.configToml) summaryLines.push(`Feature: ${written.configToml}`);
|
|
1748
1804
|
summaryLines.push(`Credentials: ${written.envFile || '~/.codex/hooks/cozeloop.env'}`);
|
|
1749
1805
|
if (written.logFile) summaryLines.push(`Hook log: ${written.logFile}`);
|
|
1750
1806
|
} else {
|
|
@@ -1755,13 +1811,10 @@ async function main() {
|
|
|
1755
1811
|
|
|
1756
1812
|
// Enterprise policy note for Claude Code
|
|
1757
1813
|
if (agent === 'claude-code') {
|
|
1758
|
-
console.log('');
|
|
1759
1814
|
info('注意:企业托管模式下若 allowManagedHooksOnly=true,需管理员在策略中放行本地 hook。');
|
|
1760
1815
|
}
|
|
1761
1816
|
// Trace permission reminder for all agents
|
|
1762
|
-
console.log('');
|
|
1763
1817
|
info('确保 CozeLoop Token 具备 Trace ingest 权限,否则 hook 触发但上报会失败。');
|
|
1764
|
-
console.log('');
|
|
1765
1818
|
|
|
1766
1819
|
// Step 5: Verify trace reporting end-to-end
|
|
1767
1820
|
info('Step 5/5: 验证 trace 上报链路...');
|
|
@@ -1783,7 +1836,6 @@ async function main() {
|
|
|
1783
1836
|
cloudResult.logid = verifyResult.logid || extractLogid(verifyResult.body) || cloudResult.logid;
|
|
1784
1837
|
cloudResult.message = `trace 上报自检失败 HTTP ${verifyResult.status}: ${(verifyResult.body || '').slice(0, 200)}`;
|
|
1785
1838
|
warn('trace 上报自检失败,但 hook 配置已写入(云端放行)。');
|
|
1786
|
-
console.log('');
|
|
1787
1839
|
emitCloudResult();
|
|
1788
1840
|
successBox(summaryLines);
|
|
1789
1841
|
return;
|
|
@@ -1799,7 +1851,6 @@ async function main() {
|
|
|
1799
1851
|
'请确认 CozeLoop Token 具备 Trace ingest 权限,再重试。',
|
|
1800
1852
|
]); // errorBox 内部 process.exit(1)
|
|
1801
1853
|
}
|
|
1802
|
-
console.log('');
|
|
1803
1854
|
|
|
1804
1855
|
emitCloudResult();
|
|
1805
1856
|
successBox(summaryLines);
|
|
@@ -1833,5 +1884,7 @@ module.exports = {
|
|
|
1833
1884
|
atomicWriteFileSync,
|
|
1834
1885
|
VERSION_WHITELIST,
|
|
1835
1886
|
VERSION_SUPPORT,
|
|
1887
|
+
codexHooksFeatureKey,
|
|
1888
|
+
normalizeCodexHooksFeatureConfig,
|
|
1836
1889
|
isVersionAtLeast,
|
|
1837
1890
|
};
|