@lobehub/cli 0.0.1 → 0.0.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/dist/index.js +216 -31
- package/man/man1/lh.1 +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import os from "node:os";
|
|
|
9
9
|
import crypto, { randomUUID } from "node:crypto";
|
|
10
10
|
import { createInterface } from "node:readline";
|
|
11
11
|
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
12
|
+
import ignore from "ignore";
|
|
12
13
|
//#region \0rolldown/runtime.js
|
|
13
14
|
var __create$2 = Object.create;
|
|
14
15
|
var __defProp$2 = Object.defineProperty;
|
|
@@ -5121,7 +5122,7 @@ async function getAuthAndServer() {
|
|
|
5121
5122
|
}
|
|
5122
5123
|
const result = await getValidToken();
|
|
5123
5124
|
if (!result) {
|
|
5124
|
-
log$1.error(`No authentication found. Run 'lh login' first, or set ${CLI_API_KEY_ENV}.`);
|
|
5125
|
+
log$1.error(`No authentication found. Run 'lh login' (or 'npx -y @lobehub/cli login') first, or set ${CLI_API_KEY_ENV}.`);
|
|
5125
5126
|
process.exit(1);
|
|
5126
5127
|
}
|
|
5127
5128
|
const serverUrl = resolveServerUrl();
|
|
@@ -28138,36 +28139,202 @@ function registerMessageCommand(program) {
|
|
|
28138
28139
|
}
|
|
28139
28140
|
//#endregion
|
|
28140
28141
|
//#region src/commands/migrate/openclaw.ts
|
|
28141
|
-
const
|
|
28142
|
-
|
|
28143
|
-
|
|
28144
|
-
".openclaw",
|
|
28145
|
-
"node_modules",
|
|
28142
|
+
const DEFAULT_AGENT_NAME = "OpenClaw";
|
|
28143
|
+
const IDENTITY_FILES = ["IDENTITY.md", "SOUL.md"];
|
|
28144
|
+
const DEFAULT_IGNORE_RULES = [
|
|
28146
28145
|
".git",
|
|
28146
|
+
".svn",
|
|
28147
|
+
".hg",
|
|
28148
|
+
".openclaw",
|
|
28149
|
+
".DS_Store",
|
|
28150
|
+
"Thumbs.db",
|
|
28151
|
+
"desktop.ini",
|
|
28152
|
+
".idea",
|
|
28147
28153
|
".vscode",
|
|
28154
|
+
".fleet",
|
|
28155
|
+
".cursor",
|
|
28156
|
+
".zed",
|
|
28157
|
+
"*.swp",
|
|
28158
|
+
"*.swo",
|
|
28159
|
+
"*~",
|
|
28160
|
+
"node_modules",
|
|
28161
|
+
".pnp",
|
|
28162
|
+
".yarn",
|
|
28163
|
+
"bower_components",
|
|
28164
|
+
"vendor",
|
|
28165
|
+
"jspm_packages",
|
|
28166
|
+
".venv",
|
|
28167
|
+
"venv",
|
|
28168
|
+
"env",
|
|
28148
28169
|
"__pycache__",
|
|
28149
|
-
"
|
|
28150
|
-
|
|
28151
|
-
|
|
28170
|
+
"*.pyc",
|
|
28171
|
+
"*.pyo",
|
|
28172
|
+
".mypy_cache",
|
|
28173
|
+
".ruff_cache",
|
|
28174
|
+
".pytest_cache",
|
|
28175
|
+
".tox",
|
|
28176
|
+
".eggs",
|
|
28177
|
+
"*.egg-info",
|
|
28178
|
+
".bundle",
|
|
28179
|
+
"target",
|
|
28180
|
+
"go.sum",
|
|
28181
|
+
".gradle",
|
|
28182
|
+
".m2",
|
|
28183
|
+
"bin",
|
|
28184
|
+
"obj",
|
|
28185
|
+
"packages",
|
|
28186
|
+
".cache",
|
|
28187
|
+
".parcel-cache",
|
|
28188
|
+
".next",
|
|
28189
|
+
".nuxt",
|
|
28190
|
+
".turbo",
|
|
28191
|
+
".output",
|
|
28192
|
+
"dist",
|
|
28193
|
+
"build",
|
|
28194
|
+
"out",
|
|
28195
|
+
".sass-cache",
|
|
28196
|
+
".env",
|
|
28197
|
+
".env.*",
|
|
28198
|
+
"coverage",
|
|
28199
|
+
".nyc_output",
|
|
28200
|
+
".terraform",
|
|
28201
|
+
"tmp",
|
|
28202
|
+
".tmp",
|
|
28203
|
+
"*.log",
|
|
28204
|
+
"logs",
|
|
28205
|
+
"*.sqlite",
|
|
28206
|
+
"*.sqlite3",
|
|
28207
|
+
"*.db",
|
|
28208
|
+
"*.db-shm",
|
|
28209
|
+
"*.db-wal",
|
|
28210
|
+
"*.ldb",
|
|
28211
|
+
"*.mdb",
|
|
28212
|
+
"*.accdb",
|
|
28213
|
+
"*.zip",
|
|
28214
|
+
"*.tar",
|
|
28215
|
+
"*.tar.gz",
|
|
28216
|
+
"*.tgz",
|
|
28217
|
+
"*.gz",
|
|
28218
|
+
"*.bz2",
|
|
28219
|
+
"*.xz",
|
|
28220
|
+
"*.rar",
|
|
28221
|
+
"*.7z",
|
|
28222
|
+
"*.jar",
|
|
28223
|
+
"*.war",
|
|
28224
|
+
"*.dll",
|
|
28225
|
+
"*.so",
|
|
28226
|
+
"*.dylib",
|
|
28227
|
+
"*.exe",
|
|
28228
|
+
"*.bin",
|
|
28229
|
+
"*.o",
|
|
28230
|
+
"*.a",
|
|
28231
|
+
"*.lib",
|
|
28232
|
+
"*.class",
|
|
28233
|
+
"*.png",
|
|
28234
|
+
"*.jpg",
|
|
28235
|
+
"*.jpeg",
|
|
28236
|
+
"*.gif",
|
|
28237
|
+
"*.bmp",
|
|
28238
|
+
"*.ico",
|
|
28239
|
+
"*.webp",
|
|
28240
|
+
"*.svg",
|
|
28241
|
+
"*.mp3",
|
|
28242
|
+
"*.mp4",
|
|
28243
|
+
"*.wav",
|
|
28244
|
+
"*.avi",
|
|
28245
|
+
"*.mov",
|
|
28246
|
+
"*.mkv",
|
|
28247
|
+
"*.flac",
|
|
28248
|
+
"*.ogg",
|
|
28249
|
+
"*.pdf",
|
|
28250
|
+
"*.woff",
|
|
28251
|
+
"*.woff2",
|
|
28252
|
+
"*.ttf",
|
|
28253
|
+
"*.otf",
|
|
28254
|
+
"*.eot",
|
|
28255
|
+
"package-lock.json",
|
|
28256
|
+
"yarn.lock",
|
|
28257
|
+
"pnpm-lock.yaml",
|
|
28258
|
+
"Gemfile.lock",
|
|
28259
|
+
"Cargo.lock",
|
|
28260
|
+
"poetry.lock",
|
|
28261
|
+
"composer.lock"
|
|
28262
|
+
];
|
|
28152
28263
|
/**
|
|
28153
|
-
*
|
|
28264
|
+
* Try to extract the agent name, description, and avatar emoji from
|
|
28265
|
+
* IDENTITY.md or SOUL.md. Falls back to "OpenClaw" if neither file
|
|
28266
|
+
* exists or parsing fails.
|
|
28267
|
+
*/
|
|
28268
|
+
function readAgentProfile(workspacePath) {
|
|
28269
|
+
for (const filename of IDENTITY_FILES) {
|
|
28270
|
+
const filePath = path.join(workspacePath, filename);
|
|
28271
|
+
if (!fs.existsSync(filePath)) continue;
|
|
28272
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
28273
|
+
const nameMatch = content.match(/\*{0,2}Name:?\*{0,2}\s*(.+)/i);
|
|
28274
|
+
const title = nameMatch ? nameMatch[1].trim() : DEFAULT_AGENT_NAME;
|
|
28275
|
+
const descMatch = content.match(/\*{0,2}(?:Creature|Vibe|Description):?\*{0,2}\s*(.+)/i);
|
|
28276
|
+
const description = descMatch ? descMatch[1].trim() : void 0;
|
|
28277
|
+
const emojiMatch = content.match(/\*{0,2}Emoji:?\*{0,2}\s*(.+)/i);
|
|
28278
|
+
const rawAvatar = emojiMatch ? emojiMatch[1].trim() : void 0;
|
|
28279
|
+
const isPlaceholder = rawAvatar && /^[_*((].*[))_*]$|^(?:tbd|todo|n\/?a|none|待定|未定)$/i.test(rawAvatar);
|
|
28280
|
+
return {
|
|
28281
|
+
avatar: rawAvatar && !isPlaceholder ? rawAvatar : void 0,
|
|
28282
|
+
description,
|
|
28283
|
+
title
|
|
28284
|
+
};
|
|
28285
|
+
}
|
|
28286
|
+
return { title: DEFAULT_AGENT_NAME };
|
|
28287
|
+
}
|
|
28288
|
+
/**
|
|
28289
|
+
* Build an ignore filter for the workspace. Uses .gitignore if present,
|
|
28290
|
+
* otherwise falls back to a comprehensive default rule set.
|
|
28291
|
+
*/
|
|
28292
|
+
function buildIgnoreFilter(workspacePath) {
|
|
28293
|
+
const ig = ignore();
|
|
28294
|
+
const gitignorePath = path.join(workspacePath, ".gitignore");
|
|
28295
|
+
if (fs.existsSync(gitignorePath)) ig.add(fs.readFileSync(gitignorePath, "utf8"));
|
|
28296
|
+
ig.add(DEFAULT_IGNORE_RULES);
|
|
28297
|
+
return ig;
|
|
28298
|
+
}
|
|
28299
|
+
/**
|
|
28300
|
+
* Recursively collect all files under `dir`, filtered by ignore rules.
|
|
28154
28301
|
* Returns paths relative to `baseDir`.
|
|
28155
28302
|
*/
|
|
28156
|
-
function collectFiles(dir, baseDir) {
|
|
28303
|
+
function collectFiles(dir, baseDir, ig) {
|
|
28157
28304
|
const results = [];
|
|
28158
28305
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
28159
|
-
|
|
28306
|
+
const relativePath = path.relative(baseDir, path.join(dir, entry.name));
|
|
28307
|
+
const testPath = entry.isDirectory() ? `${relativePath}/` : relativePath;
|
|
28308
|
+
if (ig.ignores(testPath)) continue;
|
|
28160
28309
|
const fullPath = path.join(dir, entry.name);
|
|
28161
|
-
if (entry.isDirectory()) results.push(...collectFiles(fullPath, baseDir));
|
|
28162
|
-
else if (entry.isFile()) results.push(
|
|
28310
|
+
if (entry.isDirectory()) results.push(...collectFiles(fullPath, baseDir, ig));
|
|
28311
|
+
else if (entry.isFile()) results.push(relativePath);
|
|
28163
28312
|
}
|
|
28164
28313
|
return results;
|
|
28165
28314
|
}
|
|
28166
28315
|
/**
|
|
28316
|
+
* Quick check: read the first 8KB and look for null bytes.
|
|
28317
|
+
* If found, the file is likely binary and should be skipped.
|
|
28318
|
+
*/
|
|
28319
|
+
function isBinaryFile(filePath) {
|
|
28320
|
+
const fd = fs.openSync(filePath, "r");
|
|
28321
|
+
try {
|
|
28322
|
+
const buf = Buffer.alloc(8192);
|
|
28323
|
+
const bytesRead = fs.readSync(fd, buf, 0, 8192, 0);
|
|
28324
|
+
for (let i = 0; i < bytesRead; i++) if (buf[i] === 0) return true;
|
|
28325
|
+
return false;
|
|
28326
|
+
} finally {
|
|
28327
|
+
fs.closeSync(fd);
|
|
28328
|
+
}
|
|
28329
|
+
}
|
|
28330
|
+
function formatAgentLabel(profile) {
|
|
28331
|
+
return profile.avatar ? `${profile.avatar} ${profile.title}` : profile.title;
|
|
28332
|
+
}
|
|
28333
|
+
/**
|
|
28167
28334
|
* Resolve the target agent ID.
|
|
28168
|
-
* Priority: --agent-id > --slug > create new
|
|
28335
|
+
* Priority: --agent-id > --slug > create new agent from workspace profile.
|
|
28169
28336
|
*/
|
|
28170
|
-
async function resolveAgentId(client, opts) {
|
|
28337
|
+
async function resolveAgentId(client, opts, profile) {
|
|
28171
28338
|
if (opts.agentId) return opts.agentId;
|
|
28172
28339
|
if (opts.slug) {
|
|
28173
28340
|
const agent = await client.agent.getBuiltinAgent.query({ slug: opts.slug });
|
|
@@ -28177,17 +28344,23 @@ async function resolveAgentId(client, opts) {
|
|
|
28177
28344
|
}
|
|
28178
28345
|
return agent.id;
|
|
28179
28346
|
}
|
|
28180
|
-
|
|
28181
|
-
|
|
28347
|
+
const label = formatAgentLabel(profile);
|
|
28348
|
+
log$1.info(`Creating new agent ${import_picocolors.default.bold(label)}...`);
|
|
28349
|
+
const id = (await client.agent.createAgent.mutate({ config: {
|
|
28350
|
+
avatar: profile.avatar,
|
|
28351
|
+
description: profile.description,
|
|
28352
|
+
title: profile.title
|
|
28353
|
+
} })).agentId;
|
|
28182
28354
|
if (!id) {
|
|
28183
28355
|
log$1.error("Failed to create agent — no agentId returned.");
|
|
28184
28356
|
process.exit(1);
|
|
28185
28357
|
}
|
|
28186
|
-
console.log(`${import_picocolors.default.green("✓")} Agent created: ${import_picocolors.default.bold(
|
|
28358
|
+
console.log(`${import_picocolors.default.green("✓")} Agent created: ${import_picocolors.default.bold(label)}`);
|
|
28187
28359
|
return id;
|
|
28188
28360
|
}
|
|
28189
28361
|
function registerOpenClawMigration(migrate) {
|
|
28190
|
-
migrate.command("openclaw").description("Import OpenClaw workspace files as agent documents
|
|
28362
|
+
migrate.command("openclaw").description("Import OpenClaw workspace files as agent documents").option("--source <path>", "Path to OpenClaw workspace", path.join(process.env.HOME || "~", ".openclaw", "workspace")).option("--agent-id <id>", "Import into an existing agent by ID").option("--slug <slug>", "Import into an existing agent by slug (e.g. \"inbox\")").option("--dry-run", "Preview files without importing").option("--yes", "Skip confirmation prompt").action(async (options) => {
|
|
28363
|
+
if (!options.dryRun) await getTrpcClient();
|
|
28191
28364
|
const workspacePath = path.resolve(options.source);
|
|
28192
28365
|
if (!fs.existsSync(workspacePath)) {
|
|
28193
28366
|
log$1.error(`OpenClaw workspace not found: ${workspacePath}`);
|
|
@@ -28197,7 +28370,9 @@ function registerOpenClawMigration(migrate) {
|
|
|
28197
28370
|
log$1.error(`Not a directory: ${workspacePath}`);
|
|
28198
28371
|
process.exit(1);
|
|
28199
28372
|
}
|
|
28200
|
-
const
|
|
28373
|
+
const profile = readAgentProfile(workspacePath);
|
|
28374
|
+
const label = formatAgentLabel(profile);
|
|
28375
|
+
const files = collectFiles(workspacePath, workspacePath, buildIgnoreFilter(workspacePath));
|
|
28201
28376
|
if (files.length === 0) {
|
|
28202
28377
|
log$1.info("No files found in workspace.");
|
|
28203
28378
|
return;
|
|
@@ -28210,38 +28385,48 @@ function registerOpenClawMigration(migrate) {
|
|
|
28210
28385
|
return;
|
|
28211
28386
|
}
|
|
28212
28387
|
if (!options.yes) {
|
|
28213
|
-
const target = options.agentId ? `agent ${import_picocolors.default.bold(options.agentId)}` : options.slug ? `agent slug "${import_picocolors.default.bold(options.slug)}"` : `a new
|
|
28388
|
+
const target = options.agentId ? `agent ${import_picocolors.default.bold(options.agentId)}` : options.slug ? `agent slug "${import_picocolors.default.bold(options.slug)}"` : `a new ${import_picocolors.default.bold(label)} agent`;
|
|
28214
28389
|
if (!await confirm(`Import ${files.length} file(s) as agent documents into ${target}?`)) {
|
|
28215
28390
|
console.log("Cancelled.");
|
|
28216
28391
|
return;
|
|
28217
28392
|
}
|
|
28218
28393
|
}
|
|
28219
28394
|
const client = await getTrpcClient();
|
|
28220
|
-
const agentId = await resolveAgentId(client, options);
|
|
28221
|
-
console.log(`\nImporting to
|
|
28395
|
+
const agentId = await resolveAgentId(client, options, profile);
|
|
28396
|
+
console.log(`\nImporting to ${import_picocolors.default.bold(label)}...\n`);
|
|
28222
28397
|
let success = 0;
|
|
28223
28398
|
let failed = 0;
|
|
28399
|
+
let skipped = 0;
|
|
28224
28400
|
for (const relativePath of files) {
|
|
28225
28401
|
const fullPath = path.join(workspacePath, relativePath);
|
|
28402
|
+
if (isBinaryFile(fullPath)) {
|
|
28403
|
+
console.log(` ${import_picocolors.default.dim("○")} ${relativePath} ${import_picocolors.default.dim("(binary, skipped)")}`);
|
|
28404
|
+
skipped++;
|
|
28405
|
+
continue;
|
|
28406
|
+
}
|
|
28226
28407
|
const content = fs.readFileSync(fullPath, "utf8");
|
|
28227
|
-
const
|
|
28408
|
+
const createdAt = fs.statSync(fullPath).mtime;
|
|
28228
28409
|
try {
|
|
28229
28410
|
await client.agentDocument.upsertDocument.mutate({
|
|
28230
28411
|
agentId,
|
|
28231
28412
|
content,
|
|
28232
|
-
|
|
28413
|
+
createdAt,
|
|
28414
|
+
filename: relativePath
|
|
28233
28415
|
});
|
|
28234
|
-
console.log(` ${import_picocolors.default.green("✓")} ${
|
|
28416
|
+
console.log(` ${import_picocolors.default.green("✓")} ${relativePath}`);
|
|
28235
28417
|
success++;
|
|
28236
28418
|
} catch (err) {
|
|
28237
|
-
console.log(` ${import_picocolors.default.red("✗")} ${
|
|
28419
|
+
console.log(` ${import_picocolors.default.red("✗")} ${relativePath} — ${err.message || err}`);
|
|
28238
28420
|
failed++;
|
|
28239
28421
|
}
|
|
28240
28422
|
}
|
|
28241
|
-
console.log();
|
|
28242
|
-
console.log(`${import_picocolors.default.green("✓")} Done: ${import_picocolors.default.bold(String(success))} imported` + (failed > 0 ? `, ${import_picocolors.default.red(String(failed))} failed` : ""));
|
|
28243
28423
|
const agentUrl = `${resolveServerUrl()}/agent/${agentId}`;
|
|
28244
|
-
|
|
28424
|
+
const skippedInfo = skipped > 0 ? `, ${skipped} skipped` : "";
|
|
28425
|
+
console.log();
|
|
28426
|
+
if (failed === 0) console.log(`${import_picocolors.default.green("✓")} Migration complete! ${import_picocolors.default.bold(String(success))} file(s) imported to ${import_picocolors.default.bold(label)}.${skippedInfo}`);
|
|
28427
|
+
else console.log(`${import_picocolors.default.yellow("⚠")} Migration finished with issues: ${import_picocolors.default.bold(String(success))} imported, ${import_picocolors.default.red(String(failed))} failed${skippedInfo}.`);
|
|
28428
|
+
console.log(`\n ${import_picocolors.default.dim("→")} ${import_picocolors.default.underline(agentUrl)}`);
|
|
28429
|
+
console.log();
|
|
28245
28430
|
});
|
|
28246
28431
|
}
|
|
28247
28432
|
//#endregion
|
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.3" "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.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lh": "./dist/index.js",
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"test:coverage": "bunx vitest run --config vitest.config.mts --coverage",
|
|
28
28
|
"type-check": "tsc --noEmit"
|
|
29
29
|
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"ignore": "^7.0.5"
|
|
32
|
+
},
|
|
30
33
|
"devDependencies": {
|
|
31
34
|
"@lobechat/device-gateway-client": "workspace:*",
|
|
32
35
|
"@lobechat/local-file-shell": "workspace:*",
|