{"_id":"@d2c2d/crikket-sdk","name":"@d2c2d/crikket-sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@d2c2d/crikket-sdk","version":"1.0.0","description":"Crikket anonymous oRPC client: export bugs, download artifacts, bulk status updates.","type":"module","engines":{"node":">=18"},"types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./rpc":{"types":"./dist/rpc.d.ts","default":"./dist/rpc.js"}},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"tsx --test tests/rpc.test.ts tests/bulk.test.ts tests/client.test.ts tests/export/normalizeSearch.test.ts tests/export/attachments.test.ts tests/export/render.test.ts"},"keywords":["crikket","bug-report","orpc"],"author":"","license":"ISC","devDependencies":{"@types/node":"^25.6.2","tsup":"^8.5.1","tsx":"^4.19.2","typescript":"^6.0.3"},"_id":"@d2c2d/crikket-sdk@1.0.0","gitHead":"219fde99702fbb7ce3dadc94453960c9cd099c00","_nodeVersion":"23.11.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-FUjwjv/L+pqjSzIj1pKOT4AmL3zXxzYBdmquI8FtGo81Njg/V9lABWFEvQ2JP1gVi9g8VZDe8eu21VXY2gsRPw==","shasum":"a5dbccff153ad2c9467dc9837a8fdc68ea1d4ee3","tarball":"https://registry.npmjs.org/@d2c2d/crikket-sdk/-/crikket-sdk-1.0.0.tgz","fileCount":7,"unpackedSize":33680,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCGVKXaNuiSGHlt+Zn3ZxdqrlFI+BG/w48x4E3iD7UnBAIhAPzHvdCOMKmi4OBKi9MXJkiBvgcU7SiwGk8+ZZZ5b5tv"}]},"_npmUser":{"name":"zhangjianlin007","email":"990996641@qq.com"},"directories":{},"maintainers":[{"name":"zhangjianlin007","email":"990996641@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/crikket-sdk_1.0.0_1782455725809_0.4527432106057885"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-26T06:35:25.639Z","1.0.0":"2026-06-26T06:35:25.949Z","modified":"2026-06-26T06:35:26.156Z"},"maintainers":[{"name":"zhangjianlin007","email":"990996641@qq.com"}],"description":"Crikket anonymous oRPC client: export bugs, download artifacts, bulk status updates.","keywords":["crikket","bug-report","orpc"],"license":"ISC","readme":"# @d2c2d/crikket-sdk\n\nCrikket 缺陷平台匿名 oRPC 客户端（Node.js ≥ 18，ESM）。\n\n## 安装\n\n```bash\nnpm install @d2c2d/crikket-sdk\n```\n\n## 使用\n\nSDK 不读取 env 或配置文件；在脚本中自行读取 JSON 并传入参数。\n\n### 配置 `crikket.config.json`\n\n| 字段 | 说明 |\n|------|------|\n| `rpcBaseUrl` | Crikket RPC 根 URL，必填 |\n| `organizationId` | 组织 ID；`list` / `export` / `mark` 等方法需要 |\n\n```json\n{\n  \"rpcBaseUrl\": \"http://localhost:9100\",\n  \"organizationId\": \"org_xxx\"\n}\n```\n\n### 按 id 下载 — `download-bugs.mjs`\n\n```javascript\n// 用法: node download-bugs.mjs <bugId> [bugId...]\n// 示例: node download-bugs.mjs bug_1 bug_2\n// 说明: 匿名下载，无需 organizationId；输出到 ./out-bugs/{id}.md 等\n\nimport { mkdirSync, readFileSync } from \"node:fs\";\nimport { CrikketClient } from \"@d2c2d/crikket-sdk\";\n\nconst cfg = JSON.parse(readFileSync(\"./crikket.config.json\", \"utf8\"));\nconst ids = process.argv.slice(2).filter((s) => s.trim());\nif (ids.length === 0) {\n  console.error(\"用法: node download-bugs.mjs <bugId> [bugId...]\");\n  process.exit(1);\n}\n\nconst outDir = \"./out-bugs\";\nmkdirSync(outDir, { recursive: true });\n\nconst client = new CrikketClient({ baseUrl: cfg.rpcBaseUrl });\nconst results = await client.downloadBugs({ ids, outDir });\n\nfor (const row of results) {\n  console.log(row.ok ? `✓ ${row.id}` : `✗ ${row.id}: ${row.error}`);\n}\nif (results.some((r) => !r.ok)) process.exit(1);\n```\n\n### 导出 open bug — `export-open-bugs.mjs`\n\n```javascript\n// 用法: node export-open-bugs.mjs <search> [outDir]\n// 示例: node export-open-bugs.mjs http://localhost:3003 ./out-export\n// 说明: 按 search 筛选 open bug 并下载；实际写入 {outDir}/{search slug}/\n\nimport { mkdirSync, readFileSync } from \"node:fs\";\nimport { CrikketClient } from \"@d2c2d/crikket-sdk\";\n\nconst cfg = JSON.parse(readFileSync(\"./crikket.config.json\", \"utf8\"));\nconst search = process.argv[2]?.trim();\nconst outDir = process.argv[3]?.trim() ?? \"./out-export\";\nif (!search) {\n  console.error(\"用法: node export-open-bugs.mjs <search> [outDir]\");\n  process.exit(1);\n}\n\nmkdirSync(outDir, { recursive: true });\n\nconst client = new CrikketClient({\n  baseUrl: cfg.rpcBaseUrl,\n  organizationId: cfg.organizationId, // export 需要 org\n});\n\nconst { ids, outDir: targetDir, downloadResults } = await client.exportOpenBugs({\n  search,\n  outDir,\n});\n\nconsole.log(`open=${ids.length} → ${targetDir}`);\nfor (const row of downloadResults) {\n  console.log(row.ok ? `✓ ${row.id}` : `✗ ${row.id}: ${row.error}`);\n}\nif (downloadResults.some((r) => !r.ok)) process.exit(1);\n```\n\n### 批量 mark resolved — `mark-resolved.mjs`\n\n```javascript\n// 用法: node mark-resolved.mjs <bugId> [bugId...]\n// 示例: node mark-resolved.mjs bug_1 bug_2\n// 说明: 批量将 status 改为 resolved；markInProgress / markOpen 换方法名即可\n\nimport { readFileSync } from \"node:fs\";\nimport { CrikketClient } from \"@d2c2d/crikket-sdk\";\n\nconst cfg = JSON.parse(readFileSync(\"./crikket.config.json\", \"utf8\"));\nconst ids = process.argv.slice(2).filter((s) => s.trim());\nif (ids.length === 0) {\n  console.error(\"用法: node mark-resolved.mjs <bugId> [bugId...]\");\n  process.exit(1);\n}\n\nconst client = new CrikketClient({\n  baseUrl: cfg.rpcBaseUrl,\n  organizationId: cfg.organizationId, // bulk 改 status 需要 org\n});\n\nconst result = await client.markResolved({ ids });\nconsole.log(`updated ${result.updatedCount} bug(s): ${result.ids?.join(\", \") ?? \"\"}`);\n```\n\n## API\n\n### `CrikketClient`\n\n**构造**\n\n```typescript\nnew CrikketClient(opts: {\n  baseUrl: string;           // 非空\n  organizationId?: string;\n  fetch?: typeof fetch;\n})\n```\n\n**方法**\n\n| 方法 | 说明 | `organizationId` |\n|------|------|------------------|\n| `getBugById(id)` | 单条 bug 详情 | 否 |\n| `getDebuggerEvents(id)` | 调试事件 | 否 |\n| `downloadBugs({ ids, outDir })` | 下载 Markdown + sidecar | 否 |\n| `listByOrganizationId(input?)` | 分页列表 | 构造或 `input.organizationId` |\n| `getDashboardStats(input?)` | 仪表盘统计 | 构造或 `input.organizationId` |\n| `exportOpenBugs({ search, outDir, organizationId? })` | 列出 open 并下载 | 构造或参数 |\n| `updateBulkStatus({ ids, status, organizationId? })` | 通用批量改 status | 构造或参数 |\n| `markInProgress({ ids, organizationId? })` | → `in_progress` | 构造或参数 |\n| `markResolved({ ids, organizationId? })` | → `resolved` | 构造或参数 |\n| `markOpen({ ids, organizationId? })` | → `open` | 构造或参数 |\n\n缺少 `organizationId` 时，需要 org 的方法会在发请求前抛出错误。SDK 不校验 bug 是否属于该 org。\n\n**`listByOrganizationId` 参数**\n\n```typescript\n{\n  organizationId?: string;\n  page?: number;        // 默认 1\n  perPage?: number;     // 默认 12\n  search?: string;\n  sort?: string;        // 默认 \"newest\"\n  statuses?: BugStatus[];\n  priorities?: string[];\n}\n```\n\n**`downloadBugs` 返回值** — `DownloadResult[]`：\n\n```typescript\n{\n  id: string;\n  ok: boolean;\n  error?: string;\n  paths?: {\n    md: string;\n    reportJson: string;\n    debuggerJson: string;\n    networkJson?: string;\n  };\n}\n```\n\n**`exportOpenBugs` 返回值** — `ExportOpenResult`：\n\n```typescript\n{\n  ids: string[];\n  outDir: string;              // 实际写入目录（含 search slug）\n  downloadResults: DownloadResult[];\n}\n```\n\n### 主入口辅助函数\n\n| 导出 | 说明 |\n|------|------|\n| `uniqueBugIds(ids)` | trim 并去重 |\n| `validateBugIdList(ids)` | 校验非空、≤200 个 |\n| `assertBulkUpdatedCount(result, expected)` | 校验 `updatedCount` |\n| `updateBulkStatus({ baseUrl, organizationId, bugIds, status, fetchImpl? })` | 无 client 实例的 bulk 调用 |\n| `searchNormalized(search)` | 将 search 转为安全目录名 slug |\n\n### `@d2c2d/crikket-sdk/rpc`\n\n| 导出 | 说明 |\n|------|------|\n| `rpcPost(baseUrl, procedurePath, jsonInput, fetchImpl?)` | 原始 oRPC POST |\n| `unwrapProcedureResult(body)` | 解析 tRPC/oRPC 响应体 |\n| `RpcError` | HTTP 或 RPC 错误 |\n\n### 类型\n\n`BugStatus`、`BulkUpdateResult`、`CrikketClientOptions`、`DashboardStatsInput`、`DownloadResult`、`ExportOpenResult`、`ListByOrganizationIdInput`、`ListPage`\n\n## 输出目录\n\n**`downloadBugs`** — 扁平写入 `{outDir}/`：\n\n```\n{id}.md\n{id}.report.json\n{id}.debugger.json\n{id}.network.json              # 有 networkRequests 时\n{id}.attachments/*.picked_dom.html\n```\n\n**`exportOpenBugs`** — 写入 `{outDir}/{searchNormalized(search)}/`，文件名同上。\n","readmeFilename":"README.md","_rev":"1-b0b83a0928f3649bd7033f32a0fe5e67"}