@lobehub/cli 0.0.23 → 0.0.24
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/dist/index.js +146 -114
- package/man/man1/lh.1 +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -17187,6 +17187,118 @@ var GatewayClient = class extends EventEmitter {
|
|
|
17187
17187
|
}
|
|
17188
17188
|
};
|
|
17189
17189
|
|
|
17190
|
+
//#endregion
|
|
17191
|
+
//#region src/auth/apiKey.ts
|
|
17192
|
+
async function getUserIdFromApiKey(apiKey, serverUrl) {
|
|
17193
|
+
const normalizedServerUrl = normalizeUrl(serverUrl) || resolveServerUrl();
|
|
17194
|
+
const response = await fetch(`${normalizedServerUrl}/api/v1/users/me?includeCount=0`, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
17195
|
+
let body;
|
|
17196
|
+
try {
|
|
17197
|
+
body = await response.json();
|
|
17198
|
+
} catch {
|
|
17199
|
+
throw new Error(`Failed to parse response from ${normalizedServerUrl}/api/v1/users/me?includeCount=0.`);
|
|
17200
|
+
}
|
|
17201
|
+
if (!response.ok || body?.success === false) throw new Error(body?.error || body?.message || `Request failed with status ${response.status}.`);
|
|
17202
|
+
const userId = body?.data?.id || body?.data?.userId;
|
|
17203
|
+
if (!userId) throw new Error("Current user response did not include a user id.");
|
|
17204
|
+
return userId;
|
|
17205
|
+
}
|
|
17206
|
+
|
|
17207
|
+
//#endregion
|
|
17208
|
+
//#region src/auth/resolveToken.ts
|
|
17209
|
+
/**
|
|
17210
|
+
* Parse the `sub` claim from a JWT without verifying the signature.
|
|
17211
|
+
*/
|
|
17212
|
+
function parseJwtSub(token) {
|
|
17213
|
+
try {
|
|
17214
|
+
return JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString()).sub;
|
|
17215
|
+
} catch {
|
|
17216
|
+
return;
|
|
17217
|
+
}
|
|
17218
|
+
}
|
|
17219
|
+
/**
|
|
17220
|
+
* Resolve an access token from explicit options, environment variables, or stored credentials.
|
|
17221
|
+
* Exits the process if no token can be resolved.
|
|
17222
|
+
*/
|
|
17223
|
+
async function resolveToken(options) {
|
|
17224
|
+
const envJwt = process.env.LOBEHUB_JWT;
|
|
17225
|
+
if (envJwt) {
|
|
17226
|
+
const serverUrl = resolveServerUrl();
|
|
17227
|
+
const userId = parseJwtSub(envJwt);
|
|
17228
|
+
if (!userId) {
|
|
17229
|
+
log$7.error("Could not extract userId from LOBEHUB_JWT.");
|
|
17230
|
+
process.exit(1);
|
|
17231
|
+
}
|
|
17232
|
+
log$7.debug("Using LOBEHUB_JWT from environment");
|
|
17233
|
+
return {
|
|
17234
|
+
serverUrl,
|
|
17235
|
+
token: envJwt,
|
|
17236
|
+
tokenType: "jwt",
|
|
17237
|
+
userId
|
|
17238
|
+
};
|
|
17239
|
+
}
|
|
17240
|
+
if (options.token) {
|
|
17241
|
+
const userId = parseJwtSub(options.token);
|
|
17242
|
+
if (!userId) {
|
|
17243
|
+
log$7.error("Could not extract userId from token. Provide --user-id explicitly.");
|
|
17244
|
+
process.exit(1);
|
|
17245
|
+
}
|
|
17246
|
+
return {
|
|
17247
|
+
serverUrl: resolveServerUrl(),
|
|
17248
|
+
token: options.token,
|
|
17249
|
+
tokenType: "jwt",
|
|
17250
|
+
userId
|
|
17251
|
+
};
|
|
17252
|
+
}
|
|
17253
|
+
if (options.serviceToken) {
|
|
17254
|
+
if (!options.userId) {
|
|
17255
|
+
log$7.error("--user-id is required when using --service-token");
|
|
17256
|
+
process.exit(1);
|
|
17257
|
+
}
|
|
17258
|
+
return {
|
|
17259
|
+
serverUrl: resolveServerUrl(),
|
|
17260
|
+
token: options.serviceToken,
|
|
17261
|
+
tokenType: "serviceToken",
|
|
17262
|
+
userId: options.userId
|
|
17263
|
+
};
|
|
17264
|
+
}
|
|
17265
|
+
const envApiKey = process.env[CLI_API_KEY_ENV];
|
|
17266
|
+
if (envApiKey) try {
|
|
17267
|
+
const serverUrl = resolveServerUrl();
|
|
17268
|
+
const userId = await getUserIdFromApiKey(envApiKey, serverUrl);
|
|
17269
|
+
log$7.debug(`Using ${CLI_API_KEY_ENV} from environment`);
|
|
17270
|
+
return {
|
|
17271
|
+
serverUrl,
|
|
17272
|
+
token: envApiKey,
|
|
17273
|
+
tokenType: "apiKey",
|
|
17274
|
+
userId
|
|
17275
|
+
};
|
|
17276
|
+
} catch (error) {
|
|
17277
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
17278
|
+
log$7.error(`Failed to validate ${CLI_API_KEY_ENV}: ${message}`);
|
|
17279
|
+
process.exit(1);
|
|
17280
|
+
}
|
|
17281
|
+
const result = await getValidToken();
|
|
17282
|
+
if (result) {
|
|
17283
|
+
log$7.debug("Using stored credentials");
|
|
17284
|
+
const { credentials } = result;
|
|
17285
|
+
const serverUrl = resolveServerUrl();
|
|
17286
|
+
const userId = parseJwtSub(credentials.accessToken);
|
|
17287
|
+
if (!userId) {
|
|
17288
|
+
log$7.error("Stored token is invalid. Run 'lh login' again.");
|
|
17289
|
+
process.exit(1);
|
|
17290
|
+
}
|
|
17291
|
+
return {
|
|
17292
|
+
serverUrl,
|
|
17293
|
+
token: credentials.accessToken,
|
|
17294
|
+
tokenType: "jwt",
|
|
17295
|
+
userId
|
|
17296
|
+
};
|
|
17297
|
+
}
|
|
17298
|
+
log$7.error(`No authentication found. Run 'lh login' first, or set ${CLI_API_KEY_ENV}, or provide --token.`);
|
|
17299
|
+
process.exit(1);
|
|
17300
|
+
}
|
|
17301
|
+
|
|
17190
17302
|
//#endregion
|
|
17191
17303
|
//#region node_modules/.pnpm/node-machine-id@1.1.12/node_modules/node-machine-id/dist/index.js
|
|
17192
17304
|
var require_dist$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -18066,115 +18178,33 @@ const deriveDeviceId = (userId, options = {}) => {
|
|
|
18066
18178
|
};
|
|
18067
18179
|
|
|
18068
18180
|
//#endregion
|
|
18069
|
-
//#region src/
|
|
18070
|
-
async function getUserIdFromApiKey(apiKey, serverUrl) {
|
|
18071
|
-
const normalizedServerUrl = normalizeUrl(serverUrl) || resolveServerUrl();
|
|
18072
|
-
const response = await fetch(`${normalizedServerUrl}/api/v1/users/me`, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
18073
|
-
let body;
|
|
18074
|
-
try {
|
|
18075
|
-
body = await response.json();
|
|
18076
|
-
} catch {
|
|
18077
|
-
throw new Error(`Failed to parse response from ${normalizedServerUrl}/api/v1/users/me.`);
|
|
18078
|
-
}
|
|
18079
|
-
if (!response.ok || body?.success === false) throw new Error(body?.error || body?.message || `Request failed with status ${response.status}.`);
|
|
18080
|
-
const userId = body?.data?.id || body?.data?.userId;
|
|
18081
|
-
if (!userId) throw new Error("Current user response did not include a user id.");
|
|
18082
|
-
return userId;
|
|
18083
|
-
}
|
|
18084
|
-
|
|
18085
|
-
//#endregion
|
|
18086
|
-
//#region src/auth/resolveToken.ts
|
|
18181
|
+
//#region src/device/register.ts
|
|
18087
18182
|
/**
|
|
18088
|
-
*
|
|
18183
|
+
* Resolve a stable device identity. An explicit `--device-id` wins (lets a user
|
|
18184
|
+
* pin a VM to a fixed identity); otherwise derive from the machine id so the
|
|
18185
|
+
* same machine + user maps to one device across reconnects. Returns undefined
|
|
18186
|
+
* when neither an explicit id nor a userId is available.
|
|
18089
18187
|
*/
|
|
18090
|
-
function
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
|
|
18095
|
-
|
|
18188
|
+
function resolveDeviceIdentity(userId, explicitDeviceId) {
|
|
18189
|
+
if (explicitDeviceId) return {
|
|
18190
|
+
deviceId: explicitDeviceId,
|
|
18191
|
+
identitySource: "fallback"
|
|
18192
|
+
};
|
|
18193
|
+
if (userId) return deriveDeviceId(userId);
|
|
18096
18194
|
}
|
|
18097
18195
|
/**
|
|
18098
|
-
*
|
|
18099
|
-
*
|
|
18196
|
+
* Register this device in the server registry. Shared by `lh login` (so the
|
|
18197
|
+
* device row exists right after auth) and `lh connect` (so the row exists
|
|
18198
|
+
* before the WS opens). Best-effort by contract: callers should wrap this in a
|
|
18199
|
+
* try/catch and treat any failure as non-fatal.
|
|
18100
18200
|
*/
|
|
18101
|
-
async function
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
process.exit(1);
|
|
18109
|
-
}
|
|
18110
|
-
log$7.debug("Using LOBEHUB_JWT from environment");
|
|
18111
|
-
return {
|
|
18112
|
-
serverUrl,
|
|
18113
|
-
token: envJwt,
|
|
18114
|
-
tokenType: "jwt",
|
|
18115
|
-
userId
|
|
18116
|
-
};
|
|
18117
|
-
}
|
|
18118
|
-
if (options.token) {
|
|
18119
|
-
const userId = parseJwtSub(options.token);
|
|
18120
|
-
if (!userId) {
|
|
18121
|
-
log$7.error("Could not extract userId from token. Provide --user-id explicitly.");
|
|
18122
|
-
process.exit(1);
|
|
18123
|
-
}
|
|
18124
|
-
return {
|
|
18125
|
-
serverUrl: resolveServerUrl(),
|
|
18126
|
-
token: options.token,
|
|
18127
|
-
tokenType: "jwt",
|
|
18128
|
-
userId
|
|
18129
|
-
};
|
|
18130
|
-
}
|
|
18131
|
-
if (options.serviceToken) {
|
|
18132
|
-
if (!options.userId) {
|
|
18133
|
-
log$7.error("--user-id is required when using --service-token");
|
|
18134
|
-
process.exit(1);
|
|
18135
|
-
}
|
|
18136
|
-
return {
|
|
18137
|
-
serverUrl: resolveServerUrl(),
|
|
18138
|
-
token: options.serviceToken,
|
|
18139
|
-
tokenType: "serviceToken",
|
|
18140
|
-
userId: options.userId
|
|
18141
|
-
};
|
|
18142
|
-
}
|
|
18143
|
-
const envApiKey = process.env[CLI_API_KEY_ENV];
|
|
18144
|
-
if (envApiKey) try {
|
|
18145
|
-
const serverUrl = resolveServerUrl();
|
|
18146
|
-
const userId = await getUserIdFromApiKey(envApiKey, serverUrl);
|
|
18147
|
-
log$7.debug(`Using ${CLI_API_KEY_ENV} from environment`);
|
|
18148
|
-
return {
|
|
18149
|
-
serverUrl,
|
|
18150
|
-
token: envApiKey,
|
|
18151
|
-
tokenType: "apiKey",
|
|
18152
|
-
userId
|
|
18153
|
-
};
|
|
18154
|
-
} catch (error) {
|
|
18155
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
18156
|
-
log$7.error(`Failed to validate ${CLI_API_KEY_ENV}: ${message}`);
|
|
18157
|
-
process.exit(1);
|
|
18158
|
-
}
|
|
18159
|
-
const result = await getValidToken();
|
|
18160
|
-
if (result) {
|
|
18161
|
-
log$7.debug("Using stored credentials");
|
|
18162
|
-
const { credentials } = result;
|
|
18163
|
-
const serverUrl = resolveServerUrl();
|
|
18164
|
-
const userId = parseJwtSub(credentials.accessToken);
|
|
18165
|
-
if (!userId) {
|
|
18166
|
-
log$7.error("Stored token is invalid. Run 'lh login' again.");
|
|
18167
|
-
process.exit(1);
|
|
18168
|
-
}
|
|
18169
|
-
return {
|
|
18170
|
-
serverUrl,
|
|
18171
|
-
token: credentials.accessToken,
|
|
18172
|
-
tokenType: "jwt",
|
|
18173
|
-
userId
|
|
18174
|
-
};
|
|
18175
|
-
}
|
|
18176
|
-
log$7.error(`No authentication found. Run 'lh login' first, or set ${CLI_API_KEY_ENV}, or provide --token.`);
|
|
18177
|
-
process.exit(1);
|
|
18201
|
+
async function registerDevice(auth, identity) {
|
|
18202
|
+
await createLambdaClient(auth).device.register.mutate({
|
|
18203
|
+
deviceId: identity.deviceId,
|
|
18204
|
+
hostname: os.hostname(),
|
|
18205
|
+
identitySource: identity.identitySource,
|
|
18206
|
+
platform: process.platform
|
|
18207
|
+
});
|
|
18178
18208
|
}
|
|
18179
18209
|
|
|
18180
18210
|
//#endregion
|
|
@@ -206936,10 +206966,7 @@ async function runConnect(options, isDaemonChild) {
|
|
|
206936
206966
|
gatewayUrl
|
|
206937
206967
|
});
|
|
206938
206968
|
const resolvedGatewayUrl = gatewayUrl || "https://device-gateway.lobehub.com";
|
|
206939
|
-
const identity = options.deviceId
|
|
206940
|
-
deviceId: options.deviceId,
|
|
206941
|
-
identitySource: "fallback"
|
|
206942
|
-
} : auth.userId ? deriveDeviceId(auth.userId) : void 0;
|
|
206969
|
+
const identity = resolveDeviceIdentity(auth.userId, options.deviceId);
|
|
206943
206970
|
const client = new GatewayClient({
|
|
206944
206971
|
channel: process.env.LOBEHUB_CLI_CHANNEL || "cli",
|
|
206945
206972
|
connectionId: loadOrCreateConnectionId(),
|
|
@@ -207081,12 +207108,7 @@ async function runConnect(options, isDaemonChild) {
|
|
|
207081
207108
|
process.exit(0);
|
|
207082
207109
|
});
|
|
207083
207110
|
if (identity) try {
|
|
207084
|
-
await
|
|
207085
|
-
deviceId: identity.deviceId,
|
|
207086
|
-
hostname: os.hostname(),
|
|
207087
|
-
identitySource: identity.identitySource,
|
|
207088
|
-
platform: process.platform
|
|
207089
|
-
});
|
|
207111
|
+
await registerDevice(auth, identity);
|
|
207090
207112
|
} catch (err) {
|
|
207091
207113
|
error(`Device registration failed (non-fatal): ${err.message}`);
|
|
207092
207114
|
}
|
|
@@ -211162,6 +211184,16 @@ function registerLoginCommand(program) {
|
|
|
211162
211184
|
gatewayUrl: existingSettings.gatewayUrl,
|
|
211163
211185
|
serverUrl
|
|
211164
211186
|
} : { serverUrl });
|
|
211187
|
+
const identity = resolveDeviceIdentity(parseJwtSub(body.access_token));
|
|
211188
|
+
if (identity && identity.identitySource !== "fallback") try {
|
|
211189
|
+
await registerDevice({
|
|
211190
|
+
serverUrl,
|
|
211191
|
+
token: body.access_token,
|
|
211192
|
+
tokenType: "jwt"
|
|
211193
|
+
}, identity);
|
|
211194
|
+
} catch (err) {
|
|
211195
|
+
log$7.warn(`Device registration failed (non-fatal): ${err.message}`);
|
|
211196
|
+
}
|
|
211165
211197
|
log$7.info("Login successful! Credentials saved.");
|
|
211166
211198
|
return;
|
|
211167
211199
|
}
|
package/man/man1/lh.1
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.\" Code generated by `npm run man:generate`; DO NOT EDIT.
|
|
2
2
|
.\" Manual command details come from the Commander command tree.
|
|
3
|
-
.TH LH 1 "" "@lobehub/cli 0.0.
|
|
3
|
+
.TH LH 1 "" "@lobehub/cli 0.0.24" "User Commands"
|
|
4
4
|
.SH NAME
|
|
5
5
|
lh \- LobeHub CLI \- manage and connect to LobeHub services
|
|
6
6
|
.SH SYNOPSIS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lh": "./dist/index.js",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"tsdown": "^0.21.4",
|
|
32
32
|
"typescript": "^6.0.3",
|
|
33
33
|
"ws": "^8.18.1",
|
|
34
|
+
"@lobechat/device-gateway-client": "1.0.0",
|
|
34
35
|
"@lobechat/agent-gateway-client": "1.0.0",
|
|
35
|
-
"@lobechat/device-identity": "1.0.0",
|
|
36
36
|
"@lobechat/local-file-shell": "1.0.0",
|
|
37
|
-
"@lobechat/
|
|
38
|
-
"@lobechat/
|
|
37
|
+
"@lobechat/device-identity": "1.0.0",
|
|
38
|
+
"@lobechat/heterogeneous-agents": "1.0.0"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public",
|