nextclaw 0.9.11 → 0.9.13
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/cli/index.js +133 -19
- package/package.json +1 -1
- package/templates/USAGE.md +9 -6
package/dist/cli/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
|
|
27
27
|
setPluginRuntimeBridge as setPluginRuntimeBridge2
|
|
28
28
|
} from "@nextclaw/openclaw-compat";
|
|
29
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as
|
|
29
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
30
30
|
import { join as join7, resolve as resolve9 } from "path";
|
|
31
31
|
import { createInterface as createInterface2 } from "readline";
|
|
32
32
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -1925,7 +1925,7 @@ var DiagnosticsCommands = class {
|
|
|
1925
1925
|
});
|
|
1926
1926
|
if (opts.json) {
|
|
1927
1927
|
console.log(JSON.stringify(report, null, 2));
|
|
1928
|
-
process.exitCode =
|
|
1928
|
+
process.exitCode = 0;
|
|
1929
1929
|
return;
|
|
1930
1930
|
}
|
|
1931
1931
|
console.log(`${this.deps.logo} ${APP_NAME} Status`);
|
|
@@ -1978,7 +1978,7 @@ var DiagnosticsCommands = class {
|
|
|
1978
1978
|
console.log(line);
|
|
1979
1979
|
}
|
|
1980
1980
|
}
|
|
1981
|
-
process.exitCode =
|
|
1981
|
+
process.exitCode = 0;
|
|
1982
1982
|
}
|
|
1983
1983
|
async doctor(opts = {}) {
|
|
1984
1984
|
const report = await this.collectRuntimeStatus({
|
|
@@ -2148,7 +2148,7 @@ var DiagnosticsCommands = class {
|
|
|
2148
2148
|
}
|
|
2149
2149
|
const logTail = params.verbose ? this.readLogTail(serviceState?.logPath ?? resolveServiceLogPath(), 25) : [];
|
|
2150
2150
|
const level = running ? managedHealth.state === "ok" ? issues.length > 0 ? "degraded" : "healthy" : "degraded" : "stopped";
|
|
2151
|
-
const exitCode =
|
|
2151
|
+
const exitCode = 0;
|
|
2152
2152
|
return {
|
|
2153
2153
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2154
2154
|
configPath,
|
|
@@ -2252,7 +2252,7 @@ import {
|
|
|
2252
2252
|
stopPluginChannelGateways
|
|
2253
2253
|
} from "@nextclaw/openclaw-compat";
|
|
2254
2254
|
import { startUiServer } from "@nextclaw/server";
|
|
2255
|
-
import { appendFileSync, closeSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, mkdtempSync, openSync, rmSync as rmSync3 } from "fs";
|
|
2255
|
+
import { appendFileSync, closeSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, mkdtempSync, openSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2256
2256
|
import { dirname, isAbsolute as isAbsolute2, join as join5, relative, resolve as resolve7 } from "path";
|
|
2257
2257
|
import { tmpdir } from "os";
|
|
2258
2258
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -4391,33 +4391,51 @@ var ServiceCommands = class {
|
|
|
4391
4391
|
}
|
|
4392
4392
|
async materializeMarketplaceGitSkillSource(params) {
|
|
4393
4393
|
const parsed = this.parseMarketplaceGitSkillSource(params.source);
|
|
4394
|
-
const gitPath =
|
|
4395
|
-
|
|
4396
|
-
|
|
4394
|
+
const gitPath = this.resolveGitExecutablePath();
|
|
4395
|
+
const fallbackNotes = [];
|
|
4396
|
+
if (gitPath) {
|
|
4397
|
+
try {
|
|
4398
|
+
return await this.materializeMarketplaceGitSkillViaGit({ gitPath, parsed });
|
|
4399
|
+
} catch (error) {
|
|
4400
|
+
fallbackNotes.push(`Git fast path failed: ${String(error)}`);
|
|
4401
|
+
}
|
|
4402
|
+
} else {
|
|
4403
|
+
fallbackNotes.push("Git fast path unavailable: git executable not found");
|
|
4397
4404
|
}
|
|
4405
|
+
const httpResult = await this.materializeMarketplaceGitSkillViaGithubApi(parsed);
|
|
4406
|
+
return {
|
|
4407
|
+
...httpResult,
|
|
4408
|
+
commandOutput: [...fallbackNotes, httpResult.commandOutput].filter(Boolean).join("\n")
|
|
4409
|
+
};
|
|
4410
|
+
}
|
|
4411
|
+
resolveGitExecutablePath() {
|
|
4412
|
+
return findExecutableOnPath("git");
|
|
4413
|
+
}
|
|
4414
|
+
async materializeMarketplaceGitSkillViaGit(params) {
|
|
4398
4415
|
const tempRoot = mkdtempSync(join5(tmpdir(), "nextclaw-marketplace-skill-"));
|
|
4399
4416
|
const repoDir = join5(tempRoot, "repo");
|
|
4400
4417
|
try {
|
|
4401
4418
|
const cloneArgs = ["clone", "--depth", "1", "--filter=blob:none", "--sparse"];
|
|
4402
|
-
if (parsed.ref) {
|
|
4403
|
-
cloneArgs.push("--branch", parsed.ref);
|
|
4419
|
+
if (params.parsed.ref) {
|
|
4420
|
+
cloneArgs.push("--branch", params.parsed.ref);
|
|
4404
4421
|
}
|
|
4405
|
-
cloneArgs.push(parsed.repoUrl, repoDir);
|
|
4406
|
-
const cloneResult = await this.runCommand(gitPath, cloneArgs, {
|
|
4422
|
+
cloneArgs.push(params.parsed.repoUrl, repoDir);
|
|
4423
|
+
const cloneResult = await this.runCommand(params.gitPath, cloneArgs, {
|
|
4407
4424
|
cwd: tempRoot,
|
|
4408
4425
|
timeoutMs: 18e4
|
|
4409
4426
|
});
|
|
4410
|
-
const sparseResult = await this.runCommand(gitPath, ["-C", repoDir, "sparse-checkout", "set", parsed.skillPath], {
|
|
4427
|
+
const sparseResult = await this.runCommand(params.gitPath, ["-C", repoDir, "sparse-checkout", "set", params.parsed.skillPath], {
|
|
4411
4428
|
cwd: tempRoot,
|
|
4412
4429
|
timeoutMs: 6e4
|
|
4413
4430
|
});
|
|
4414
|
-
const skillDir = join5(repoDir, parsed.skillPath);
|
|
4431
|
+
const skillDir = join5(repoDir, params.parsed.skillPath);
|
|
4415
4432
|
return {
|
|
4416
4433
|
tempRoot,
|
|
4417
4434
|
skillDir,
|
|
4418
4435
|
commandOutput: [
|
|
4419
|
-
|
|
4420
|
-
`Git
|
|
4436
|
+
"Installer path: git-sparse",
|
|
4437
|
+
`Git repository: ${params.parsed.repoUrl}`,
|
|
4438
|
+
`Git path: ${params.parsed.skillPath}`,
|
|
4421
4439
|
this.mergeCommandOutput(cloneResult.stdout, cloneResult.stderr),
|
|
4422
4440
|
this.mergeCommandOutput(sparseResult.stdout, sparseResult.stderr)
|
|
4423
4441
|
].filter(Boolean).join("\n")
|
|
@@ -4427,6 +4445,98 @@ var ServiceCommands = class {
|
|
|
4427
4445
|
throw error;
|
|
4428
4446
|
}
|
|
4429
4447
|
}
|
|
4448
|
+
async materializeMarketplaceGitSkillViaGithubApi(parsed) {
|
|
4449
|
+
const tempRoot = mkdtempSync(join5(tmpdir(), "nextclaw-marketplace-skill-"));
|
|
4450
|
+
const skillDir = join5(tempRoot, "skill");
|
|
4451
|
+
const downloadedFiles = [];
|
|
4452
|
+
try {
|
|
4453
|
+
mkdirSync4(skillDir, { recursive: true });
|
|
4454
|
+
await this.downloadGithubDirectoryToPath({
|
|
4455
|
+
parsed,
|
|
4456
|
+
remotePath: parsed.skillPath,
|
|
4457
|
+
localDir: skillDir,
|
|
4458
|
+
relativePrefix: "",
|
|
4459
|
+
downloadedFiles
|
|
4460
|
+
});
|
|
4461
|
+
return {
|
|
4462
|
+
tempRoot,
|
|
4463
|
+
skillDir,
|
|
4464
|
+
commandOutput: [
|
|
4465
|
+
"Installer path: github-http",
|
|
4466
|
+
`Git repository: ${parsed.repoUrl}`,
|
|
4467
|
+
`Git path: ${parsed.skillPath}`,
|
|
4468
|
+
`Downloaded files: ${downloadedFiles.length}`
|
|
4469
|
+
].join("\n")
|
|
4470
|
+
};
|
|
4471
|
+
} catch (error) {
|
|
4472
|
+
rmSync3(tempRoot, { recursive: true, force: true });
|
|
4473
|
+
throw error;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
async downloadGithubDirectoryToPath(params) {
|
|
4477
|
+
const encodedPath = params.remotePath.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
4478
|
+
const apiUrl = new URL(`https://api.github.com/repos/${params.parsed.owner}/${params.parsed.repo}/contents/${encodedPath}`);
|
|
4479
|
+
if (params.parsed.ref) {
|
|
4480
|
+
apiUrl.searchParams.set("ref", params.parsed.ref);
|
|
4481
|
+
}
|
|
4482
|
+
const payload = await this.fetchJsonWithHeaders(apiUrl.toString(), {
|
|
4483
|
+
Accept: "application/vnd.github+json",
|
|
4484
|
+
"User-Agent": `${APP_NAME2}/${getPackageVersion()}`
|
|
4485
|
+
});
|
|
4486
|
+
if (!Array.isArray(payload)) {
|
|
4487
|
+
throw new Error(`GitHub path is not a directory: ${params.remotePath}`);
|
|
4488
|
+
}
|
|
4489
|
+
for (const entry of payload) {
|
|
4490
|
+
if (!entry || typeof entry !== "object") {
|
|
4491
|
+
continue;
|
|
4492
|
+
}
|
|
4493
|
+
const item = entry;
|
|
4494
|
+
const itemName = typeof item.name === "string" ? item.name.trim() : "";
|
|
4495
|
+
const itemPath = typeof item.path === "string" ? item.path.trim() : "";
|
|
4496
|
+
if (!itemName || !itemPath) {
|
|
4497
|
+
continue;
|
|
4498
|
+
}
|
|
4499
|
+
if (item.type === "dir") {
|
|
4500
|
+
const childLocalDir = join5(params.localDir, itemName);
|
|
4501
|
+
mkdirSync4(childLocalDir, { recursive: true });
|
|
4502
|
+
await this.downloadGithubDirectoryToPath({
|
|
4503
|
+
parsed: params.parsed,
|
|
4504
|
+
remotePath: itemPath,
|
|
4505
|
+
localDir: childLocalDir,
|
|
4506
|
+
relativePrefix: params.relativePrefix ? `${params.relativePrefix}/${itemName}` : itemName,
|
|
4507
|
+
downloadedFiles: params.downloadedFiles
|
|
4508
|
+
});
|
|
4509
|
+
continue;
|
|
4510
|
+
}
|
|
4511
|
+
if (item.type !== "file") {
|
|
4512
|
+
throw new Error(`Unsupported GitHub skill entry type: ${item.type ?? "unknown"}`);
|
|
4513
|
+
}
|
|
4514
|
+
if (!item.download_url) {
|
|
4515
|
+
throw new Error(`GitHub skill file missing download_url: ${itemPath}`);
|
|
4516
|
+
}
|
|
4517
|
+
const content = await this.fetchBinaryWithHeaders(item.download_url, {
|
|
4518
|
+
"User-Agent": `${APP_NAME2}/${getPackageVersion()}`
|
|
4519
|
+
});
|
|
4520
|
+
const localFile = join5(params.localDir, itemName);
|
|
4521
|
+
writeFileSync4(localFile, content);
|
|
4522
|
+
params.downloadedFiles.push(params.relativePrefix ? `${params.relativePrefix}/${itemName}` : itemName);
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
async fetchJsonWithHeaders(url, headers) {
|
|
4526
|
+
const response = await fetch(url, { headers });
|
|
4527
|
+
if (!response.ok) {
|
|
4528
|
+
throw new Error(`HTTP ${response.status} when fetching ${url}`);
|
|
4529
|
+
}
|
|
4530
|
+
return await response.json();
|
|
4531
|
+
}
|
|
4532
|
+
async fetchBinaryWithHeaders(url, headers) {
|
|
4533
|
+
const response = await fetch(url, { headers });
|
|
4534
|
+
if (!response.ok) {
|
|
4535
|
+
throw new Error(`HTTP ${response.status} when downloading ${url}`);
|
|
4536
|
+
}
|
|
4537
|
+
const bytes = await response.arrayBuffer();
|
|
4538
|
+
return Buffer.from(bytes);
|
|
4539
|
+
}
|
|
4430
4540
|
parseMarketplaceGitSkillSource(source) {
|
|
4431
4541
|
const trimmed = source.trim();
|
|
4432
4542
|
if (!trimmed) {
|
|
@@ -4447,6 +4557,8 @@ var ServiceCommands = class {
|
|
|
4447
4557
|
throw new Error(`Unsupported GitHub skill source URL: ${source}`);
|
|
4448
4558
|
}
|
|
4449
4559
|
return {
|
|
4560
|
+
owner: owner2,
|
|
4561
|
+
repo: repo2,
|
|
4450
4562
|
repoUrl: `https://github.com/${owner2}/${repo2}.git`,
|
|
4451
4563
|
skillPath: pathParts2.join("/"),
|
|
4452
4564
|
ref: ref2
|
|
@@ -4466,6 +4578,8 @@ var ServiceCommands = class {
|
|
|
4466
4578
|
throw new Error(`Unsupported git skill source: ${source}`);
|
|
4467
4579
|
}
|
|
4468
4580
|
return {
|
|
4581
|
+
owner,
|
|
4582
|
+
repo,
|
|
4469
4583
|
repoUrl: `https://github.com/${owner}/${repo}.git`,
|
|
4470
4584
|
skillPath: pathParts.join("/"),
|
|
4471
4585
|
ref: ref && ref.trim().length > 0 ? ref.trim() : void 0
|
|
@@ -4628,7 +4742,7 @@ ${stderr}`.trim();
|
|
|
4628
4742
|
};
|
|
4629
4743
|
|
|
4630
4744
|
// src/cli/workspace.ts
|
|
4631
|
-
import { cpSync as cpSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync2, rmSync as rmSync4, writeFileSync as
|
|
4745
|
+
import { cpSync as cpSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync2, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4632
4746
|
import { createRequire } from "module";
|
|
4633
4747
|
import { dirname as dirname2, join as join6, resolve as resolve8 } from "path";
|
|
4634
4748
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -4672,7 +4786,7 @@ var WorkspaceManager = class {
|
|
|
4672
4786
|
const raw = readFileSync7(templatePath, "utf-8");
|
|
4673
4787
|
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
|
|
4674
4788
|
mkdirSync5(dirname2(filePath), { recursive: true });
|
|
4675
|
-
|
|
4789
|
+
writeFileSync5(filePath, content);
|
|
4676
4790
|
created.push(entry.target);
|
|
4677
4791
|
}
|
|
4678
4792
|
const memoryDir = join6(workspace, "memory");
|
|
@@ -5240,7 +5354,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
5240
5354
|
const merged = history.concat(
|
|
5241
5355
|
rl.history ?? []
|
|
5242
5356
|
);
|
|
5243
|
-
|
|
5357
|
+
writeFileSync6(historyFile, merged.join("\n"));
|
|
5244
5358
|
process.exit(0);
|
|
5245
5359
|
});
|
|
5246
5360
|
let running = true;
|
package/package.json
CHANGED
package/templates/USAGE.md
CHANGED
|
@@ -8,13 +8,14 @@ This guide covers installation, configuration, channels, tools, automation, and
|
|
|
8
8
|
|
|
9
9
|
## AI Self-Management Contract
|
|
10
10
|
|
|
11
|
-
When NextClaw AI needs to operate the product itself (status/doctor/channels/config/cron), follow these rules:
|
|
11
|
+
When NextClaw AI needs to operate the product itself (version/status/doctor/channels/config/cron), follow these rules:
|
|
12
12
|
|
|
13
13
|
1. **Read this guide first** (`USAGE.md`) before executing management commands.
|
|
14
|
-
2. **
|
|
15
|
-
3. **
|
|
16
|
-
4. **
|
|
17
|
-
5. **
|
|
14
|
+
2. **Use the exact command for the intent**: use `nextclaw --version` for version lookup; do not infer version from `status`.
|
|
15
|
+
3. **Prefer machine-readable output** (`--json`) whenever available.
|
|
16
|
+
4. **Close the loop after changes** with `nextclaw status --json` (and `nextclaw doctor --json` when needed).
|
|
17
|
+
5. **Be explicit about restart semantics** (hot-apply, auto-restart, or manual restart required).
|
|
18
|
+
6. **Never invent commands**; use documented commands or `nextclaw --help` / `nextclaw <subcommand> --help`.
|
|
18
19
|
|
|
19
20
|
---
|
|
20
21
|
|
|
@@ -421,6 +422,7 @@ Created under the workspace:
|
|
|
421
422
|
| `nextclaw ui` | Start UI and gateway in the foreground |
|
|
422
423
|
| `nextclaw gateway` | Start gateway only (for channels) |
|
|
423
424
|
| `nextclaw serve` | Run gateway + UI in the foreground (no background) |
|
|
425
|
+
| `nextclaw --version` | Show the installed NextClaw version |
|
|
424
426
|
| `nextclaw agent -m "message"` | Send a one-off message to the agent |
|
|
425
427
|
| `nextclaw agent` | Interactive chat in the terminal |
|
|
426
428
|
| `nextclaw agent --session <id> --model <model>` | Use a session-specific model/provider route (sticky for that session) |
|
|
@@ -460,8 +462,9 @@ If service is already running, new UI port flags do not hot-apply; use `nextclaw
|
|
|
460
462
|
|
|
461
463
|
Status/diagnostics tips:
|
|
462
464
|
|
|
465
|
+
- `nextclaw --version` is the only supported way to query the installed CLI version.
|
|
463
466
|
- `nextclaw status` shows runtime truth (process + health + config summary).
|
|
464
|
-
- `nextclaw status --json` outputs machine-readable status and
|
|
467
|
+
- `nextclaw status --json` outputs machine-readable status and exits `0` when the command itself succeeds; use the JSON `level` field (`healthy` / `degraded` / `stopped`) to interpret runtime state.
|
|
465
468
|
- `nextclaw status --fix` safely clears stale service state if PID is dead.
|
|
466
469
|
- `nextclaw doctor` runs additional checks (state coherence, health, port availability, provider readiness).
|
|
467
470
|
|