ck-pi-zen-session 0.1.2 → 0.1.3

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 CHANGED
@@ -17,6 +17,8 @@ OpenCode Zen 免费模型同步、请求头伪装与 Session 会话自动维护
17
17
  - `x-session-affinity`: 与 session 严格同步
18
18
  - `X-Session-Id`: 与 session 严格同步
19
19
  - ⏱️ **30 分钟无感前置平滑轮换**:官方后端限制免费会话时长约为 1 小时(超时直接 403 `FreeTierError`)。本插件在底层请求拦截器(`before_provider_headers`)和 Agent 循环守卫(`before_agent_start`)中设置 30 分钟主动换新阈值,兼顾会话上下文平滑与长期高频调用稳定性,确保永不触碰 1 小时硬限制。
20
+ - 🚑 **网关异常自愈守卫 (`after_provider_response`)**:新增底层响应拦截,一旦检测到远端网关返回 401 或 403 `FreeTierError`,立即当场触发 Session 换新与后台持久化,并提示用户,彻底终结会话失效死锁。
21
+ - 🔑 **多源凭据自适应感知**:支持优先读取 `OPENCODE_API_KEY`、`OPENCODE_ZEN_API_KEY` 环境变量,亦可直接使用 `/zen <key>` 配置,无缝兼容各类容器与终端环境。
20
22
  - 🧰 **官方全套 6 大核心工具链守卫与字母序规约**:OpenCode Zen 免费端点对工具集有硬性要求(无工具直接拒绝)。插件在 `before_provider_request` 钩子中实时监控请求体:
21
23
  - 若用户处于纯文本或无工具模式,自动补齐官方完整的 6 大核心工具(`bash`, `edit`, `glob`, `grep`, `read`, `write`)与 `tool_choice: "auto"`,彻底根除 403 拦截;
22
24
  - 若请求已有工具,严格按照官方客户端规约(`localeCompare`)按函数名升序重排;
@@ -50,7 +52,7 @@ pi install git:github.com/1837620622/ck-pi-extension
50
52
  | 指令 | 说明 |
51
53
  | --- | --- |
52
54
  | `/zen <oc_sk_xxx>` | 设置/更新 API Key,自动在线验证并全量同步模型与全套请求头 |
53
- | `/zen` | 智能检查当前会话状态;若会话临近过期自动换新;若未配置 Key 则弹出交互输入框 |
55
+ | `/zen` | 立即一键强制换新 Session 并全面刷新所有请求头;若未配置 Key 则弹出交互输入框 |
54
56
  | `/zen refresh` | 强制生成全新的合法降序 Session ID 并更新所有请求头与落盘配置 |
55
57
  | `/zen status` | 查看当前 API Key 掩码、活跃会话精确存活时间及模型就绪状态 |
56
58
  | `/zen list`(或 `/zen models`) | 查看当前已激活的所有免费模型列表 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ck-pi-zen-session",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "OpenCode Zen session auto-refresh and free-tier model catalog synchronization for the Pi coding agent.",
5
5
  "keywords": [
6
6
  "pi-package",
package/src/index.ts CHANGED
@@ -146,7 +146,27 @@ export default function piZenSession(pi: ExtensionAPI): void {
146
146
  }
147
147
  });
148
148
 
149
- // 4. 常驻生命周期守卫:在每次 Agent 循环执行前,自动检测 Session 有效性并无感续期
149
+ // 4. 网关响应守卫:遇 401/403 会话受限或过期时,立即自愈换新 Session ID
150
+ pi.on("after_provider_response", (event, ctx) => {
151
+ const model = ctx.model;
152
+ const isZenModel =
153
+ model?.provider === ZEN_PROVIDER_ID ||
154
+ model?.id?.includes("-free") ||
155
+ model?.id === "big-pickle";
156
+
157
+ if (isZenModel && (event.status === 401 || event.status === 403)) {
158
+ // 自动异步刷新 Session
159
+ syncZenConfiguration({ forceSession: true }).catch(() => {});
160
+ if (ctx.hasUI) {
161
+ ctx.ui.notify(
162
+ `检测到 OpenCode Zen 会话受限 (HTTP ${event.status}),已自动轮换新 Session ID。`,
163
+ "warning",
164
+ );
165
+ }
166
+ }
167
+ });
168
+
169
+ // 5. 常驻生命周期守卫:在每次 Agent 循环执行前,自动检测 Session 有效性并无感续期
150
170
  pi.on("before_agent_start", async () => {
151
171
  try {
152
172
  const currentSession = getStoredZenSessionId();
@@ -162,7 +182,7 @@ export default function piZenSession(pi: ExtensionAPI): void {
162
182
  }
163
183
  });
164
184
 
165
- // 4. 注册 /zen 指令
185
+ // 6. 注册 /zen 指令
166
186
  pi.registerCommand("zen", {
167
187
  description: "OpenCode Zen 免费模型与 Session 会话自动维护 (/zen [key|refresh|status|list])",
168
188
  getArgumentCompletions: (prefix: string) => {
@@ -279,23 +299,24 @@ export async function handleZenCommand(
279
299
  // 5. 无参调用: /zen
280
300
  const status = getZenStatusInfo();
281
301
  if (status.hasKey) {
282
- // 已有 Key 时,自动检测并刷新 Session
302
+ // 已有 Key 时,自动强制刷新 Session 并重新注册热载
283
303
  try {
284
- notify(ctx, "正在检测并刷新 OpenCode Zen 会话...", "info");
285
- const result = await syncZenConfiguration({ forceSession: status.sessionExpired });
304
+ notify(ctx, "正在刷新 OpenCode Zen 会话与请求头...", "info");
305
+ const result = await syncZenConfiguration({ forceSession: true });
286
306
  if (pi) {
287
307
  registerZenProviderToPi(pi, result.apiKey, result.sessionId);
288
308
  }
289
309
  const msg = [
290
- "✨ [OpenCode Zen 已连接就绪]",
310
+ "✨ [OpenCode Zen 已自动刷新并就绪]",
291
311
  `• API Key: ${maskKey(result.apiKey)}`,
292
- `• 活跃 Session: ${result.sessionId} (${result.isNewSession ? "已换新" : "仍有效"})`,
293
- `• 免费模型: ${result.modelsCount} 个可用`,
312
+ `• 全新 Session: ${result.sessionId} (有效且已持久化)`,
313
+ `• 免费模型: 已同步 ${result.modelsCount} 个可用免费模型`,
314
+ `• 请求头保护: 30分钟自动轮换 + 7维官方签名 + 6大核心工具全注入`,
294
315
  "",
295
316
  "常用操作:",
296
317
  " /zen <key> - 更换 API Key",
297
- " /zen refresh - 换新会话 ID",
298
318
  " /zen status - 详情状态",
319
+ " /zen list - 查看所有可用免费模型",
299
320
  ].join("\n");
300
321
  notify(ctx, msg, "info");
301
322
  } catch (error) {
@@ -312,7 +333,7 @@ export async function handleZenCommand(
312
333
  "oc_sk_",
313
334
  );
314
335
  if (enteredKey && enteredKey.trim()) {
315
- await handleZenCommand(enteredKey.trim(), ctx);
336
+ await handleZenCommand(enteredKey.trim(), ctx, pi);
316
337
  return;
317
338
  }
318
339
  notify(ctx, "已取消输入。如需配置请使用 /zen <oc_sk_xxx>", "info");
package/src/sync.ts CHANGED
@@ -76,6 +76,15 @@ export async function fetchZenFreeModels(apiKey: string): Promise<string[]> {
76
76
  * 读取当前系统中已配置的 OpenCode Zen API Key
77
77
  */
78
78
  export function getStoredZenApiKey(authPath = defaultAuthPath(), modelsPath = defaultModelsPath()): string | null {
79
+ // 优先读取环境变量,支持免配置直接启动
80
+ const envKey =
81
+ process.env.OPENCODE_API_KEY ||
82
+ process.env.OPENCODE_ZEN_API_KEY ||
83
+ process.env.ZEN_API_KEY;
84
+ if (envKey && envKey.trim()) {
85
+ return envKey.trim();
86
+ }
87
+
79
88
  try {
80
89
  if (existsSync(authPath)) {
81
90
  const auth = JSON.parse(readFileSync(authPath, "utf8")) as Record<string, { key?: string }>;