shennian 0.2.5 → 0.2.7

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.
@@ -15,6 +15,7 @@ export declare class CodexAdapter extends AgentAdapter {
15
15
  private rpcSeq;
16
16
  private pendingRequests;
17
17
  private appServerReady;
18
+ private appServerStartupError;
18
19
  private activeTurnId;
19
20
  private namedThread;
20
21
  private deltaItemIds;
@@ -29,6 +30,8 @@ export declare class CodexAdapter extends AgentAdapter {
29
30
  private initializeAppServer;
30
31
  private sendRpc;
31
32
  private handleAppServerMessage;
33
+ private handleAppServerNonJsonStdout;
34
+ private failAppServerStartup;
32
35
  private handleAppServerCompletedItem;
33
36
  private handleAppServerUsage;
34
37
  private handleCodexItemLine;
@@ -20,6 +20,7 @@ export class CodexAdapter extends AgentAdapter {
20
20
  rpcSeq = 1;
21
21
  pendingRequests = new Map();
22
22
  appServerReady = null;
23
+ appServerStartupError = null;
23
24
  activeTurnId = null;
24
25
  namedThread = false;
25
26
  deltaItemIds = new Set();
@@ -128,6 +129,7 @@ export class CodexAdapter extends AgentAdapter {
128
129
  });
129
130
  this.process = proc;
130
131
  this.stderrBuf = '';
132
+ this.appServerStartupError = null;
131
133
  const rl = createInterface({ input: proc.stdout });
132
134
  rl.on('line', (line) => {
133
135
  if (!line.trim())
@@ -137,6 +139,7 @@ export class CodexAdapter extends AgentAdapter {
137
139
  msg = JSON.parse(line);
138
140
  }
139
141
  catch {
142
+ this.handleAppServerNonJsonStdout(line);
140
143
  return;
141
144
  }
142
145
  this.handleAppServerMessage(msg);
@@ -215,6 +218,8 @@ export class CodexAdapter extends AgentAdapter {
215
218
  this.namedThread = !!response.thread?.name;
216
219
  }
217
220
  sendRpc(method, params, timeoutMs = 60_000) {
221
+ if (this.appServerStartupError)
222
+ return Promise.reject(this.appServerStartupError);
218
223
  const proc = this.process;
219
224
  if (!proc?.stdin)
220
225
  return Promise.reject(new Error('codex app-server is not running'));
@@ -289,6 +294,19 @@ export class CodexAdapter extends AgentAdapter {
289
294
  }
290
295
  }
291
296
  }
297
+ handleAppServerNonJsonStdout(line) {
298
+ const normalized = normalizeTerminalText(line);
299
+ if (!looksLikeCodexInteractiveAuthPrompt(normalized))
300
+ return;
301
+ this.failAppServerStartup(new Error('Codex 未登录。请先在这台电脑的终端运行 `codex`,按提示完成 ChatGPT 登录或配置 API key,然后再回到神念发送消息。'));
302
+ }
303
+ failAppServerStartup(error) {
304
+ if (this.appServerStartupError)
305
+ return;
306
+ this.appServerStartupError = error;
307
+ this.rejectAllPending(error);
308
+ this.process?.kill('SIGTERM');
309
+ }
292
310
  handleAppServerCompletedItem(params) {
293
311
  const item = typeof params.item === 'object' && params.item !== null
294
312
  ? params.item
@@ -766,6 +784,23 @@ function normalizeCodexStderr(stderr) {
766
784
  .filter((line) => !isIgnorableCodexStderrLine(line))
767
785
  .join('\n');
768
786
  }
787
+ function normalizeTerminalText(text) {
788
+ return text
789
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, ' ')
790
+ .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, ' ')
791
+ .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ' ')
792
+ .replace(/\s+/g, ' ')
793
+ .trim();
794
+ }
795
+ function looksLikeCodexInteractiveAuthPrompt(text) {
796
+ if (!text)
797
+ return false;
798
+ const normalized = text.toLowerCase();
799
+ return (normalized.includes('welcome to codex') ||
800
+ normalized.includes('sign in with chatgpt') ||
801
+ normalized.includes('provide your own api key') ||
802
+ normalized.includes('press enter to continue'));
803
+ }
769
804
  function isIgnorableCodexStderrLine(line) {
770
805
  return [
771
806
  /WARNING: proceeding, even though we could not update PATH:/i,
@@ -114,27 +114,32 @@ async function discoverGeminiModels() {
114
114
  return [];
115
115
  return fallbackGeminiModels();
116
116
  }
117
+ const fallbackPiModels = [
118
+ { id: 'qwen3.6-plus', name: 'Qwen 3.6 Plus', provider: 'dashscope', isDefault: true },
119
+ { id: 'qwen3.6-flash', name: 'Qwen 3.6 Flash', provider: 'dashscope' },
120
+ ];
117
121
  async function discoverPiModels(context) {
118
122
  if (!context.serverUrl || !context.authToken)
119
- return [];
123
+ return fallbackPiModels;
120
124
  try {
121
- const response = await fetch(`${context.serverUrl.replace(/\/$/, '')}/models`, {
125
+ const response = await fetch(`${context.serverUrl.replace(/\/$/, '')}/api/models`, {
122
126
  headers: {
123
127
  Authorization: `Bearer ${context.authToken}`,
124
128
  },
125
129
  });
126
130
  if (!response.ok)
127
- return [];
131
+ return fallbackPiModels;
128
132
  const data = (await response.json());
129
- return (data.models ?? []).map((model) => ({
133
+ const models = (data.models ?? []).map((model) => ({
130
134
  id: model.id,
131
135
  name: model.name?.trim() || model.id,
132
136
  provider: model.provider,
133
137
  isDefault: model.id === data.default,
134
138
  }));
139
+ return models.length > 0 ? models : fallbackPiModels;
135
140
  }
136
141
  catch {
137
- return [];
142
+ return fallbackPiModels;
138
143
  }
139
144
  }
140
145
  async function discoverBuiltinModels(type, context) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Shennian — AI Agent Mobile Console CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsc",
53
- "build:publish": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.publish.json",
53
+ "build:publish": "node -e \"const fs=require('node:fs'); fs.rmSync('dist', { recursive: true, force: true }); fs.rmSync('.tsbuildinfo.publish', { force: true })\" && tsc -p tsconfig.publish.json",
54
54
  "dev": "tsc --watch"
55
55
  }
56
56
  }