openclaw-autoproxy 1.0.6 → 1.0.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.
package/README.md CHANGED
@@ -61,6 +61,7 @@ routes:
61
61
  - Start: `openclaw-autoproxy start`
62
62
  - Dev (watch): `openclaw-autoproxy dev`
63
63
  - Help: `openclaw-autoproxy help`
64
+ - Publish: `npm publish --registry=https://registry.npmjs.org --access public`
64
65
 
65
66
  Quick run (installed):
66
67
 
package/README.zh-CN.md CHANGED
@@ -59,6 +59,7 @@ routes:
59
59
  - 启动:`openclaw-autoproxy start`
60
60
  - 开发(热重载):`openclaw-autoproxy dev`
61
61
  - 帮助:`openclaw-autoproxy help`
62
+ - 发布:`npm publish --registry=https://registry.npmjs.org --access public`
62
63
 
63
64
  快速示例(安装并立即启动):
64
65
 
@@ -97,15 +97,20 @@ export function getModelHealthWindow(windowMs = DEFAULT_WINDOW_MS) {
97
97
  }
98
98
  }
99
99
  summaries.sort((a, b) => {
100
+ const aIsHealthy = a.lastStatusCode === 200 ? 1 : 0;
101
+ const bIsHealthy = b.lastStatusCode === 200 ? 1 : 0;
102
+ if (aIsHealthy !== bIsHealthy) {
103
+ return bIsHealthy - aIsHealthy;
104
+ }
105
+ if (a.avgResponseMs !== b.avgResponseMs) {
106
+ return a.avgResponseMs - b.avgResponseMs;
107
+ }
100
108
  if (a.accessCount !== b.accessCount) {
101
109
  return b.accessCount - a.accessCount;
102
110
  }
103
111
  if (a.successRatePct !== b.successRatePct) {
104
112
  return b.successRatePct - a.successRatePct;
105
113
  }
106
- if (a.avgResponseMs !== b.avgResponseMs) {
107
- return a.avgResponseMs - b.avgResponseMs;
108
- }
109
114
  return a.model.localeCompare(b.model);
110
115
  });
111
116
  return {
@@ -43,6 +43,12 @@ function formatTableNumber(value) {
43
43
  }
44
44
  return value.toFixed(2).replace(/\.00$/, "").replace(/(\.\d)0$/, "$1");
45
45
  }
46
+ function formatTableDurationMs(value) {
47
+ if (!Number.isFinite(value)) {
48
+ return "-";
49
+ }
50
+ return String(Math.round(value));
51
+ }
46
52
  function padTableCell(value, width, align) {
47
53
  return align === "right" ? value.padStart(width, " ") : value.padEnd(width, " ");
48
54
  }
@@ -54,8 +60,8 @@ function buildModelHealthTable(windowHours, models) {
54
60
  align: "right",
55
61
  value: (row) => row.lastStatusCode === null ? "-" : String(row.lastStatusCode),
56
62
  },
57
- { header: "Avg(ms)", align: "right", value: (row) => formatTableNumber(row.avgResponseMs) },
58
- { header: "Last(ms)", align: "right", value: (row) => formatTableNumber(row.lastResponseMs) },
63
+ { header: "Avg(ms)", align: "right", value: (row) => formatTableDurationMs(row.avgResponseMs) },
64
+ { header: "Last(ms)", align: "right", value: (row) => formatTableDurationMs(row.lastResponseMs) },
59
65
  { header: "Count", align: "right", value: (row) => String(row.accessCount) },
60
66
  { header: "OK%", align: "right", value: (row) => `${formatTableNumber(row.successRatePct)}%` },
61
67
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-autoproxy",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Local model-switching proxy gateway with OpenAI-compatible APIs",
5
5
  "type": "module",
6
6
  "main": "dist/gateway/server.js",
@@ -155,6 +155,17 @@ export function getModelHealthWindow(windowMs = DEFAULT_WINDOW_MS): {
155
155
  }
156
156
 
157
157
  summaries.sort((a, b) => {
158
+ const aIsHealthy = a.lastStatusCode === 200 ? 1 : 0;
159
+ const bIsHealthy = b.lastStatusCode === 200 ? 1 : 0;
160
+
161
+ if (aIsHealthy !== bIsHealthy) {
162
+ return bIsHealthy - aIsHealthy;
163
+ }
164
+
165
+ if (a.avgResponseMs !== b.avgResponseMs) {
166
+ return a.avgResponseMs - b.avgResponseMs;
167
+ }
168
+
158
169
  if (a.accessCount !== b.accessCount) {
159
170
  return b.accessCount - a.accessCount;
160
171
  }
@@ -163,10 +174,6 @@ export function getModelHealthWindow(windowMs = DEFAULT_WINDOW_MS): {
163
174
  return b.successRatePct - a.successRatePct;
164
175
  }
165
176
 
166
- if (a.avgResponseMs !== b.avgResponseMs) {
167
- return a.avgResponseMs - b.avgResponseMs;
168
- }
169
-
170
177
  return a.model.localeCompare(b.model);
171
178
  });
172
179
 
@@ -57,6 +57,14 @@ function formatTableNumber(value: number): string {
57
57
  return value.toFixed(2).replace(/\.00$/, "").replace(/(\.\d)0$/, "$1");
58
58
  }
59
59
 
60
+ function formatTableDurationMs(value: number): string {
61
+ if (!Number.isFinite(value)) {
62
+ return "-";
63
+ }
64
+
65
+ return String(Math.round(value));
66
+ }
67
+
60
68
  function padTableCell(value: string, width: number, align: "left" | "right"): string {
61
69
  return align === "right" ? value.padStart(width, " ") : value.padEnd(width, " ");
62
70
  }
@@ -70,8 +78,8 @@ function buildModelHealthTable(windowHours: number, models: Array<ModelHealthSum
70
78
  value: (row: ModelHealthSummary & { rank: number }) =>
71
79
  row.lastStatusCode === null ? "-" : String(row.lastStatusCode),
72
80
  },
73
- { header: "Avg(ms)", align: "right" as const, value: (row: ModelHealthSummary & { rank: number }) => formatTableNumber(row.avgResponseMs) },
74
- { header: "Last(ms)", align: "right" as const, value: (row: ModelHealthSummary & { rank: number }) => formatTableNumber(row.lastResponseMs) },
81
+ { header: "Avg(ms)", align: "right" as const, value: (row: ModelHealthSummary & { rank: number }) => formatTableDurationMs(row.avgResponseMs) },
82
+ { header: "Last(ms)", align: "right" as const, value: (row: ModelHealthSummary & { rank: number }) => formatTableDurationMs(row.lastResponseMs) },
75
83
  { header: "Count", align: "right" as const, value: (row: ModelHealthSummary & { rank: number }) => String(row.accessCount) },
76
84
  { header: "OK%", align: "right" as const, value: (row: ModelHealthSummary & { rank: number }) => `${formatTableNumber(row.successRatePct)}%` },
77
85
  ];