@shulex/cxclawctl 0.1.7 → 0.1.9
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 +3 -6
- package/capabilities.lock.json +3 -2
- package/dist/cli.js +63 -12
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,15 +33,12 @@ codex plugin marketplace add SolveaCX/cx_claw_codex --ref marketplace/stable
|
|
|
33
33
|
codex plugin add cxclaw@cxclaw
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
安装后不需要单独执行登录命令。首次使用任何需要服务端访问的 CXClaw 能力时,CLI 会自动打开钉钉授权页并持续监听授权结果;授权成功后自动继续原命令,不需要再对 Codex 回复“已授权”。
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
npx --yes @shulex/cxclawctl@latest auth login
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
CLI 固定连接生产授权服务 `https://ai.solvea.cx/api_v2/ticket`,不接受用户指定服务地址或登录环境。CLI 会打开钉钉授权页;一次登录即可访问获准的测试和生产租户。授权完成后,检查登录状态和本地版本:
|
|
38
|
+
CLI 固定连接生产授权服务 `https://ai.solvea.cx/api_v2/ticket`,不接受用户指定服务地址或登录环境。一次登录即可访问获准的测试和生产租户。需要人工诊断授权时,可以执行:
|
|
43
39
|
|
|
44
40
|
```bash
|
|
41
|
+
npx --yes @shulex/cxclawctl@latest auth ensure
|
|
45
42
|
npx --yes @shulex/cxclawctl@latest auth status
|
|
46
43
|
npx --yes @shulex/cxclawctl@latest doctor
|
|
47
44
|
```
|
package/capabilities.lock.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"plugin": {
|
|
4
|
-
"pluginVersion": "0.1.
|
|
5
|
-
"cliVersion": "0.1.
|
|
4
|
+
"pluginVersion": "0.1.9",
|
|
5
|
+
"cliVersion": "0.1.9",
|
|
6
6
|
"toolSchemaVersion": "1",
|
|
7
7
|
"serverApiVersion": "1"
|
|
8
8
|
},
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
}
|
|
159
159
|
],
|
|
160
160
|
"localCommands": [
|
|
161
|
+
"node \"<PLUGIN_ROOT>/plugin-cli.mjs\" auth ensure",
|
|
161
162
|
"node \"<PLUGIN_ROOT>/plugin-cli.mjs\" auth status",
|
|
162
163
|
"node \"<PLUGIN_ROOT>/plugin-cli.mjs\" tenant current",
|
|
163
164
|
"node \"<PLUGIN_ROOT>/plugin-cli.mjs\" tenant search",
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ var AUTH_SERVER_URL = "https://ai.solvea.cx/api_v2/ticket";
|
|
|
12
12
|
// package.json
|
|
13
13
|
var package_default = {
|
|
14
14
|
name: "@shulex/cxclawctl",
|
|
15
|
-
version: "0.1.
|
|
15
|
+
version: "0.1.9",
|
|
16
16
|
description: "Solvea CXClaw CLI and Codex plugin runtime",
|
|
17
17
|
type: "module",
|
|
18
18
|
bin: {
|
|
@@ -67,6 +67,16 @@ var TOOL_SCHEMA_VERSION = "1";
|
|
|
67
67
|
var SERVER_API_VERSION = "1";
|
|
68
68
|
|
|
69
69
|
// src/http.ts
|
|
70
|
+
var HttpError = class extends Error {
|
|
71
|
+
constructor(status, responseBody) {
|
|
72
|
+
super(`server returned ${status}: ${responseBody}`);
|
|
73
|
+
this.status = status;
|
|
74
|
+
this.responseBody = responseBody;
|
|
75
|
+
this.name = "HttpError";
|
|
76
|
+
}
|
|
77
|
+
status;
|
|
78
|
+
responseBody;
|
|
79
|
+
};
|
|
70
80
|
function unwrap(value) {
|
|
71
81
|
if (value && typeof value === "object" && Object.keys(value).length === 1 && "data" in value) return value.data;
|
|
72
82
|
return value;
|
|
@@ -86,10 +96,11 @@ async function request(session, path, init = {}, tenant = false) {
|
|
|
86
96
|
const response = await fetch(`${AUTH_SERVER_URL}${path}`, { ...init, headers, signal: AbortSignal.timeout(3e4) });
|
|
87
97
|
const text = await response.text();
|
|
88
98
|
const value = text ? JSON.parse(text) : void 0;
|
|
89
|
-
if (!response.ok) throw new
|
|
99
|
+
if (!response.ok) throw new HttpError(response.status, text);
|
|
90
100
|
return unwrap(value);
|
|
91
101
|
} catch (error) {
|
|
92
102
|
last = error;
|
|
103
|
+
if (error instanceof HttpError && error.status < 500) break;
|
|
93
104
|
if (attempt < 2) await new Promise((r) => setTimeout(r, 200 * 2 ** attempt));
|
|
94
105
|
}
|
|
95
106
|
}
|
|
@@ -101,9 +112,15 @@ var post = (session, path, body, tenant = false) => request(session, path, { met
|
|
|
101
112
|
import { AsyncEntry } from "@napi-rs/keyring";
|
|
102
113
|
var options = process.platform === "linux" ? { linux: { store: "secret-service" } } : void 0;
|
|
103
114
|
var entry = () => new AsyncEntry("com.solvea.cxclaw", "session", options);
|
|
115
|
+
var SessionNotFoundError = class extends Error {
|
|
116
|
+
constructor() {
|
|
117
|
+
super("\u672A\u767B\u5F55");
|
|
118
|
+
this.name = "SessionNotFoundError";
|
|
119
|
+
}
|
|
120
|
+
};
|
|
104
121
|
async function loadSession() {
|
|
105
122
|
const raw = await entry().getPassword();
|
|
106
|
-
if (!raw) throw new
|
|
123
|
+
if (!raw) throw new SessionNotFoundError();
|
|
107
124
|
return JSON.parse(raw);
|
|
108
125
|
}
|
|
109
126
|
async function saveSession(session) {
|
|
@@ -116,7 +133,11 @@ async function deleteSession() {
|
|
|
116
133
|
|
|
117
134
|
// src/auth.ts
|
|
118
135
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
119
|
-
|
|
136
|
+
var loginInFlight;
|
|
137
|
+
function publicAuthStatus(session) {
|
|
138
|
+
return { ok: true, loggedIn: true, email: session.email, channel: session.channel, accessExpiresAt: session.accessExpiry };
|
|
139
|
+
}
|
|
140
|
+
async function runLogin(channel) {
|
|
120
141
|
const session = { channel, accessToken: "", refreshToken: "", accessExpiry: 0, employeeId: "", email: "" };
|
|
121
142
|
const start = await post(session, "/auth/device/start", { clientVersion: VERSION });
|
|
122
143
|
console.error(`\u8BF7\u5728\u6D4F\u89C8\u5668\u6253\u5F00\u4EE5\u4E0B\u5730\u5740\u5B8C\u6210\u9489\u9489\u6388\u6743\uFF1A
|
|
@@ -124,19 +145,32 @@ ${start.verificationUriComplete}
|
|
|
124
145
|
\u8BBE\u5907\u7801\uFF1A${start.userCode}`);
|
|
125
146
|
await open(start.verificationUriComplete).catch(() => void 0);
|
|
126
147
|
const deadline = Date.now() + start.expiresIn * 1e3;
|
|
148
|
+
console.error("\u6B63\u5728\u7B49\u5F85\u9489\u9489\u6388\u6743\uFF0C\u6388\u6743\u5B8C\u6210\u540E\u5F53\u524D\u547D\u4EE4\u4F1A\u81EA\u52A8\u7EE7\u7EED\uFF0C\u65E0\u9700\u56DE\u590D Codex\u3002");
|
|
149
|
+
let nextProgressAt = Date.now() + 15e3;
|
|
127
150
|
while (Date.now() < deadline) {
|
|
128
151
|
await sleep(Math.max(2, start.interval) * 1e3);
|
|
129
152
|
try {
|
|
130
153
|
const token = await post(session, "/auth/device/token", { deviceCode: start.deviceCode, clientVersion: VERSION });
|
|
131
154
|
Object.assign(session, { accessToken: token.accessToken, refreshToken: token.refreshToken, accessExpiry: Math.floor(Date.now() / 1e3) + token.expiresIn, employeeId: token.employee.id, email: token.employee.email });
|
|
132
155
|
await saveSession(session);
|
|
156
|
+
console.error("\u9489\u9489\u6388\u6743\u6210\u529F\uFF0C\u7EE7\u7EED\u6267\u884C\u5F53\u524D\u547D\u4EE4\u3002");
|
|
133
157
|
return session;
|
|
134
158
|
} catch (error) {
|
|
135
|
-
if (!
|
|
159
|
+
if (!(error instanceof HttpError) || error.status !== 428) throw error;
|
|
160
|
+
if (Date.now() >= nextProgressAt) {
|
|
161
|
+
console.error("\u4ECD\u5728\u7B49\u5F85\u9489\u9489\u6388\u6743\u2026\u2026");
|
|
162
|
+
nextProgressAt = Date.now() + 15e3;
|
|
163
|
+
}
|
|
136
164
|
}
|
|
137
165
|
}
|
|
138
166
|
throw new Error("device authorization expired");
|
|
139
167
|
}
|
|
168
|
+
function login(channel) {
|
|
169
|
+
if (!loginInFlight) loginInFlight = runLogin(channel).finally(() => {
|
|
170
|
+
loginInFlight = void 0;
|
|
171
|
+
});
|
|
172
|
+
return loginInFlight;
|
|
173
|
+
}
|
|
140
174
|
async function ensureAccess(session) {
|
|
141
175
|
if (session.accessToken && Date.now() / 1e3 + 30 < session.accessExpiry) return session;
|
|
142
176
|
const token = await post(session, "/auth/refresh", { refreshToken: session.refreshToken, clientVersion: VERSION });
|
|
@@ -144,8 +178,24 @@ async function ensureAccess(session) {
|
|
|
144
178
|
await saveSession(session);
|
|
145
179
|
return session;
|
|
146
180
|
}
|
|
147
|
-
async function
|
|
148
|
-
|
|
181
|
+
async function resolveAuthenticated(runtime) {
|
|
182
|
+
try {
|
|
183
|
+
return await runtime.refresh(await runtime.load());
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof SessionNotFoundError) {
|
|
186
|
+
console.error("CXClaw \u5C1A\u672A\u6388\u6743\uFF0C\u6B63\u5728\u81EA\u52A8\u53D1\u8D77\u9489\u9489\u6388\u6743\u3002");
|
|
187
|
+
return runtime.authorize();
|
|
188
|
+
}
|
|
189
|
+
if (error instanceof HttpError && error.status === 401) {
|
|
190
|
+
console.error("CXClaw \u767B\u5F55\u5DF2\u5931\u6548\uFF0C\u6B63\u5728\u91CD\u65B0\u53D1\u8D77\u9489\u9489\u6388\u6743\u3002");
|
|
191
|
+
await runtime.remove().catch(() => void 0);
|
|
192
|
+
return runtime.authorize();
|
|
193
|
+
}
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function authenticated() {
|
|
198
|
+
return resolveAuthenticated({ load: loadSession, refresh: ensureAccess, remove: deleteSession, authorize: () => login("stable") });
|
|
149
199
|
}
|
|
150
200
|
async function logout() {
|
|
151
201
|
let session;
|
|
@@ -2829,8 +2879,10 @@ var usage = () => [
|
|
|
2829
2879
|
async function runAuth(action2, args) {
|
|
2830
2880
|
if (action2 === "login") {
|
|
2831
2881
|
if (flag(args, "--server") || flag(args, "--env")) throw new Error("auth login \u56FA\u5B9A\u4F7F\u7528\u751F\u4EA7\u6388\u6743\u670D\u52A1\uFF0C\u4E0D\u652F\u6301 --server \u6216 --env");
|
|
2832
|
-
|
|
2882
|
+
const session = await login(option(args, "--channel") ?? "stable");
|
|
2883
|
+
return print(publicAuthStatus(session));
|
|
2833
2884
|
}
|
|
2885
|
+
if (action2 === "ensure") return print(publicAuthStatus(await authenticated()));
|
|
2834
2886
|
if (action2 === "logout") {
|
|
2835
2887
|
await logout();
|
|
2836
2888
|
return print({ ok: true, loggedOut: true });
|
|
@@ -2838,16 +2890,15 @@ async function runAuth(action2, args) {
|
|
|
2838
2890
|
if (action2 === "status") {
|
|
2839
2891
|
try {
|
|
2840
2892
|
const session = await loadSession();
|
|
2841
|
-
return print(
|
|
2893
|
+
return print(publicAuthStatus(session));
|
|
2842
2894
|
} catch {
|
|
2843
2895
|
return print({ ok: true, loggedIn: false });
|
|
2844
2896
|
}
|
|
2845
2897
|
}
|
|
2846
2898
|
if (action2 === "refresh") {
|
|
2847
|
-
|
|
2848
|
-
return print({ ok: true, accessExpiresAt: session.accessExpiry });
|
|
2899
|
+
return print(publicAuthStatus(await authenticated()));
|
|
2849
2900
|
}
|
|
2850
|
-
throw new Error("auth action must be login, logout, status, or refresh");
|
|
2901
|
+
throw new Error("auth action must be login, ensure, logout, status, or refresh");
|
|
2851
2902
|
}
|
|
2852
2903
|
async function runTenant(action2, args) {
|
|
2853
2904
|
const session = await authenticated();
|