@babyclaw/gateway 0.0.1 → 0.2.1
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.d.ts +92 -1
- package/dist/index.js +257 -72
- package/dist/index.js.map +1 -1
- package/dist/main.js +258 -71
- package/dist/main.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -134,6 +134,9 @@ declare class AdminClient {
|
|
|
134
134
|
shutdown(): Promise<{
|
|
135
135
|
ok: boolean;
|
|
136
136
|
}>;
|
|
137
|
+
reloadSkills(): Promise<{
|
|
138
|
+
ok: boolean;
|
|
139
|
+
}>;
|
|
137
140
|
private get;
|
|
138
141
|
}
|
|
139
142
|
|
|
@@ -148,6 +151,16 @@ declare function writeConfig({ config }: {
|
|
|
148
151
|
declare const CONFIG_PATH_ENV_VAR = "BABYCLAW_CONFIG_PATH";
|
|
149
152
|
declare function getDefaultConfigPath(): string;
|
|
150
153
|
declare function getConfigPath(): string;
|
|
154
|
+
declare const DEFAULT_WORKSPACE_ROOT = "~/babyclaw";
|
|
155
|
+
/**
|
|
156
|
+
* Resolves the workspace path from the config's workspace.root value.
|
|
157
|
+
* Supports ~ expansion for home-relative paths.
|
|
158
|
+
* Plain relative paths are resolved against the config file's directory
|
|
159
|
+
* so the result is deterministic regardless of where the process starts.
|
|
160
|
+
*/
|
|
161
|
+
declare function resolveWorkspaceRoot({ configRoot }: {
|
|
162
|
+
configRoot: string;
|
|
163
|
+
}): string;
|
|
151
164
|
|
|
152
165
|
declare function getDefaultConfigTemplate(): string;
|
|
153
166
|
|
|
@@ -204,6 +217,84 @@ declare function resolveLanguageModel({ config }: {
|
|
|
204
217
|
config: BabyclawConfig;
|
|
205
218
|
}): LanguageModel;
|
|
206
219
|
|
|
220
|
+
type SkillInstallSpec = {
|
|
221
|
+
id?: string;
|
|
222
|
+
kind: "brew" | "node" | "go" | "uv" | "download";
|
|
223
|
+
label?: string;
|
|
224
|
+
bins?: string[];
|
|
225
|
+
os?: string[];
|
|
226
|
+
formula?: string;
|
|
227
|
+
package?: string;
|
|
228
|
+
module?: string;
|
|
229
|
+
url?: string;
|
|
230
|
+
archive?: string;
|
|
231
|
+
extract?: boolean;
|
|
232
|
+
stripComponents?: number;
|
|
233
|
+
targetDir?: string;
|
|
234
|
+
};
|
|
235
|
+
type OpenClawSkillMetadata = {
|
|
236
|
+
always?: boolean;
|
|
237
|
+
skillKey?: string;
|
|
238
|
+
primaryEnv?: string;
|
|
239
|
+
emoji?: string;
|
|
240
|
+
homepage?: string;
|
|
241
|
+
os?: string[];
|
|
242
|
+
requires?: {
|
|
243
|
+
bins?: string[];
|
|
244
|
+
anyBins?: string[];
|
|
245
|
+
env?: string[];
|
|
246
|
+
config?: string[];
|
|
247
|
+
};
|
|
248
|
+
install?: SkillInstallSpec[];
|
|
249
|
+
};
|
|
250
|
+
type SkillFrontmatter = {
|
|
251
|
+
name: string;
|
|
252
|
+
description: string;
|
|
253
|
+
homepage?: string;
|
|
254
|
+
userInvocable: boolean;
|
|
255
|
+
disableModelInvocation: boolean;
|
|
256
|
+
commandDispatch?: string;
|
|
257
|
+
commandTool?: string;
|
|
258
|
+
commandArgMode?: string;
|
|
259
|
+
openclaw?: OpenClawSkillMetadata;
|
|
260
|
+
};
|
|
261
|
+
type SkillEntry = {
|
|
262
|
+
frontmatter: SkillFrontmatter;
|
|
263
|
+
slug: string;
|
|
264
|
+
relativePath: string;
|
|
265
|
+
};
|
|
266
|
+
declare function getSkillKey({ frontmatter, slug, }: {
|
|
267
|
+
frontmatter: SkillFrontmatter | null;
|
|
268
|
+
slug: string;
|
|
269
|
+
}): string;
|
|
270
|
+
type SkillsConfig = {
|
|
271
|
+
entries: Record<string, {
|
|
272
|
+
enabled: boolean;
|
|
273
|
+
apiKey?: string;
|
|
274
|
+
env?: Record<string, string>;
|
|
275
|
+
}>;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
type BundledSkillStatus = {
|
|
279
|
+
slug: string;
|
|
280
|
+
frontmatter: SkillFrontmatter | null;
|
|
281
|
+
enabled: boolean;
|
|
282
|
+
eligible: boolean;
|
|
283
|
+
ineligibilityReason: string | null;
|
|
284
|
+
hasInstallSpecs: boolean;
|
|
285
|
+
};
|
|
286
|
+
declare function listBundledSkills({ skillsConfig, fullConfig, }: {
|
|
287
|
+
skillsConfig: SkillsConfig;
|
|
288
|
+
fullConfig: Record<string, unknown>;
|
|
289
|
+
}): BundledSkillStatus[];
|
|
290
|
+
declare function getEnabledBundledSkills({ skillsConfig, fullConfig, }: {
|
|
291
|
+
skillsConfig: SkillsConfig;
|
|
292
|
+
fullConfig: Record<string, unknown>;
|
|
293
|
+
}): SkillEntry[];
|
|
294
|
+
declare function getBundledSkillPath({ slug }: {
|
|
295
|
+
slug: string;
|
|
296
|
+
}): string | null;
|
|
297
|
+
|
|
207
298
|
declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
|
|
208
299
|
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
209
300
|
declare const LOG_FORMATS: readonly ["json", "pretty"];
|
|
@@ -225,4 +316,4 @@ declare function createChildLogger({ context }: {
|
|
|
225
316
|
context: Record<string, unknown>;
|
|
226
317
|
}): pino.Logger;
|
|
227
318
|
|
|
228
|
-
export { AdminClient, type BabyclawConfig, CONFIG_PATH_ENV_VAR, ClawHubError, GatewayRuntime, type GatewayStatus, type InstallSkillResult, type LogFormat, type LogLevel, type LoggingConfig, SUPPORTED_PROVIDERS, SkillAlreadyInstalledError, type SkillSetupResult, babyclawConfigSchema, createChildLogger, createLogger, getAdminSocketPath, getConfigPath, getDefaultConfigPath, getDefaultConfigTemplate, getLogger, installSkillFromClawHub, loadConfig, loadConfigRaw, resolveLanguageModel, runSkillSetup, writeConfig };
|
|
319
|
+
export { AdminClient, type BabyclawConfig, type BundledSkillStatus, CONFIG_PATH_ENV_VAR, ClawHubError, DEFAULT_WORKSPACE_ROOT, GatewayRuntime, type GatewayStatus, type InstallSkillResult, type LogFormat, type LogLevel, type LoggingConfig, SUPPORTED_PROVIDERS, SkillAlreadyInstalledError, type SkillSetupResult, babyclawConfigSchema, createChildLogger, createLogger, getAdminSocketPath, getBundledSkillPath, getConfigPath, getDefaultConfigPath, getDefaultConfigTemplate, getEnabledBundledSkills, getLogger, getSkillKey, installSkillFromClawHub, listBundledSkills, loadConfig, loadConfigRaw, resolveLanguageModel, resolveWorkspaceRoot, runSkillSetup, writeConfig };
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/runtime.ts
|
|
8
|
-
import {
|
|
8
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
9
|
+
import { getBundledSkillsDir } from "@babyclaw/skills";
|
|
9
10
|
|
|
10
11
|
// src/ai/agent.ts
|
|
11
12
|
import {
|
|
@@ -2042,9 +2043,26 @@ import { z } from "zod";
|
|
|
2042
2043
|
// src/utils/path.ts
|
|
2043
2044
|
import { resolve, sep } from "path";
|
|
2044
2045
|
import { stat } from "fs/promises";
|
|
2046
|
+
var BUNDLED_SKILLS_PREFIX = "bundled-skills/";
|
|
2045
2047
|
function normalizeSeparators({ path }) {
|
|
2046
2048
|
return path.replaceAll("\\", "/");
|
|
2047
2049
|
}
|
|
2050
|
+
function resolveBundledSkillPath({
|
|
2051
|
+
bundledSkillsDir,
|
|
2052
|
+
requestedPath
|
|
2053
|
+
}) {
|
|
2054
|
+
const relative3 = requestedPath.slice(BUNDLED_SKILLS_PREFIX.length);
|
|
2055
|
+
const normalized = normalizeSeparators({ path: relative3 }).trim();
|
|
2056
|
+
if (normalized.length === 0) {
|
|
2057
|
+
throw new Error("Path is required");
|
|
2058
|
+
}
|
|
2059
|
+
const absoluteRoot = resolve(bundledSkillsDir);
|
|
2060
|
+
const absoluteTarget = resolve(absoluteRoot, normalized);
|
|
2061
|
+
if (!isSubPath({ parent: absoluteRoot, child: absoluteTarget })) {
|
|
2062
|
+
throw new Error("Path escapes bundled skills root");
|
|
2063
|
+
}
|
|
2064
|
+
return absoluteTarget;
|
|
2065
|
+
}
|
|
2048
2066
|
function resolveWorkspacePath({
|
|
2049
2067
|
workspaceRoot,
|
|
2050
2068
|
requestedPath
|
|
@@ -3801,10 +3819,25 @@ function createWorkspaceTools({ context }) {
|
|
|
3801
3819
|
defaultCode: "WORKSPACE_READ_FAILED",
|
|
3802
3820
|
input: { path, format },
|
|
3803
3821
|
action: async () => {
|
|
3804
|
-
const
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3822
|
+
const isBundled = path.startsWith(BUNDLED_SKILLS_PREFIX);
|
|
3823
|
+
let absolutePath;
|
|
3824
|
+
if (isBundled) {
|
|
3825
|
+
if (!context.bundledSkillsDir) {
|
|
3826
|
+
throw new ToolExecutionError({
|
|
3827
|
+
code: "BUNDLED_SKILLS_UNAVAILABLE",
|
|
3828
|
+
message: "Bundled skills directory is not configured"
|
|
3829
|
+
});
|
|
3830
|
+
}
|
|
3831
|
+
absolutePath = resolveBundledSkillPath({
|
|
3832
|
+
bundledSkillsDir: context.bundledSkillsDir,
|
|
3833
|
+
requestedPath: path
|
|
3834
|
+
});
|
|
3835
|
+
} else {
|
|
3836
|
+
absolutePath = resolveWorkspacePath({
|
|
3837
|
+
workspaceRoot: context.workspaceRoot,
|
|
3838
|
+
requestedPath: path
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3808
3841
|
const raw = await readFile6(absolutePath, "utf8");
|
|
3809
3842
|
ensurePayloadWithinLimit({
|
|
3810
3843
|
value: raw,
|
|
@@ -3862,6 +3895,12 @@ function createWorkspaceTools({ context }) {
|
|
|
3862
3895
|
hasValue: value !== void 0
|
|
3863
3896
|
},
|
|
3864
3897
|
action: async () => {
|
|
3898
|
+
if (path.startsWith(BUNDLED_SKILLS_PREFIX)) {
|
|
3899
|
+
throw new ToolExecutionError({
|
|
3900
|
+
code: "BUNDLED_SKILLS_READONLY",
|
|
3901
|
+
message: "Bundled skills are read-only and cannot be written to"
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3865
3904
|
const absolutePath = resolveWorkspacePath({
|
|
3866
3905
|
workspaceRoot: context.workspaceRoot,
|
|
3867
3906
|
requestedPath: path
|
|
@@ -4001,6 +4040,12 @@ function createWorkspaceTools({ context }) {
|
|
|
4001
4040
|
defaultCode: "WORKSPACE_DELETE_FAILED",
|
|
4002
4041
|
input: { path, recursive },
|
|
4003
4042
|
action: async () => {
|
|
4043
|
+
if (path.startsWith(BUNDLED_SKILLS_PREFIX)) {
|
|
4044
|
+
throw new ToolExecutionError({
|
|
4045
|
+
code: "BUNDLED_SKILLS_READONLY",
|
|
4046
|
+
message: "Bundled skills are read-only and cannot be deleted"
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4004
4049
|
const absolutePath = resolveWorkspacePath({
|
|
4005
4050
|
workspaceRoot: context.workspaceRoot,
|
|
4006
4051
|
requestedPath: path
|
|
@@ -4037,6 +4082,12 @@ function createWorkspaceTools({ context }) {
|
|
|
4037
4082
|
defaultCode: "WORKSPACE_MOVE_FAILED",
|
|
4038
4083
|
input: { from_path, to_path, overwrite },
|
|
4039
4084
|
action: async () => {
|
|
4085
|
+
if (from_path.startsWith(BUNDLED_SKILLS_PREFIX) || to_path.startsWith(BUNDLED_SKILLS_PREFIX)) {
|
|
4086
|
+
throw new ToolExecutionError({
|
|
4087
|
+
code: "BUNDLED_SKILLS_READONLY",
|
|
4088
|
+
message: "Bundled skills are read-only and cannot be moved"
|
|
4089
|
+
});
|
|
4090
|
+
}
|
|
4040
4091
|
const fromAbsolute = resolveWorkspacePath({
|
|
4041
4092
|
workspaceRoot: context.workspaceRoot,
|
|
4042
4093
|
requestedPath: from_path
|
|
@@ -4217,44 +4268,15 @@ function createUnifiedTools({
|
|
|
4217
4268
|
};
|
|
4218
4269
|
}
|
|
4219
4270
|
|
|
4220
|
-
// src/
|
|
4221
|
-
import {
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
userPath: join9(workspacePath, "USER.md")
|
|
4228
|
-
};
|
|
4229
|
-
}
|
|
4230
|
-
async function readPersonalityFiles({
|
|
4231
|
-
workspacePath
|
|
4271
|
+
// src/bundled-skills/index.ts
|
|
4272
|
+
import { listBundledSlugs, readBundledSkillContent, getBundledSkillInfo } from "@babyclaw/skills";
|
|
4273
|
+
|
|
4274
|
+
// src/workspace/skills/types.ts
|
|
4275
|
+
function getSkillKey({
|
|
4276
|
+
frontmatter,
|
|
4277
|
+
slug
|
|
4232
4278
|
}) {
|
|
4233
|
-
|
|
4234
|
-
const [identity, soul, user] = await Promise.all([
|
|
4235
|
-
readOptionalFile2({ path: identityPath }),
|
|
4236
|
-
readOptionalFile2({ path: soulPath }),
|
|
4237
|
-
readOptionalFile2({ path: userPath })
|
|
4238
|
-
]);
|
|
4239
|
-
return {
|
|
4240
|
-
identity,
|
|
4241
|
-
soul,
|
|
4242
|
-
user
|
|
4243
|
-
};
|
|
4244
|
-
}
|
|
4245
|
-
function hasCompletePersonalityFiles(files) {
|
|
4246
|
-
return typeof files.identity === "string" && files.identity.trim().length > 0 && typeof files.soul === "string" && files.soul.trim().length > 0 && typeof files.user === "string" && files.user.trim().length > 0;
|
|
4247
|
-
}
|
|
4248
|
-
async function readOptionalFile2({ path }) {
|
|
4249
|
-
try {
|
|
4250
|
-
return await readFile7(path, "utf8");
|
|
4251
|
-
} catch (error) {
|
|
4252
|
-
const maybeErrno = error;
|
|
4253
|
-
if (maybeErrno.code === "ENOENT") {
|
|
4254
|
-
return null;
|
|
4255
|
-
}
|
|
4256
|
-
throw error;
|
|
4257
|
-
}
|
|
4279
|
+
return frontmatter?.openclaw?.skillKey ?? frontmatter?.name ?? slug;
|
|
4258
4280
|
}
|
|
4259
4281
|
|
|
4260
4282
|
// src/workspace/skills/eligibility.ts
|
|
@@ -4264,54 +4286,84 @@ function getEligibleSkills({
|
|
|
4264
4286
|
skillsConfig,
|
|
4265
4287
|
fullConfig
|
|
4266
4288
|
}) {
|
|
4267
|
-
return skills.filter((skill) =>
|
|
4289
|
+
return skills.filter((skill) => {
|
|
4290
|
+
const { frontmatter, slug } = skill;
|
|
4291
|
+
const entry = skillsConfig.entries[getSkillKey({ frontmatter, slug })];
|
|
4292
|
+
if (entry?.enabled === false) return false;
|
|
4293
|
+
return checkSkillEligibility({ frontmatter, skillsConfig, fullConfig }).eligible;
|
|
4294
|
+
});
|
|
4268
4295
|
}
|
|
4269
|
-
function
|
|
4270
|
-
|
|
4296
|
+
function checkSkillEligibility({
|
|
4297
|
+
frontmatter,
|
|
4271
4298
|
skillsConfig,
|
|
4272
4299
|
fullConfig
|
|
4273
4300
|
}) {
|
|
4274
|
-
|
|
4301
|
+
if (frontmatter.disableModelInvocation) {
|
|
4302
|
+
return { eligible: false, reason: "Model invocation disabled" };
|
|
4303
|
+
}
|
|
4275
4304
|
const openclaw = frontmatter.openclaw;
|
|
4276
|
-
const skillKey = openclaw?.skillKey ?? frontmatter.name;
|
|
4277
|
-
const entry = skillsConfig.entries[skillKey];
|
|
4278
|
-
if (entry?.enabled === false) return false;
|
|
4279
|
-
if (frontmatter.disableModelInvocation) return false;
|
|
4280
4305
|
if (openclaw?.os && openclaw.os.length > 0) {
|
|
4281
|
-
if (!openclaw.os.includes(process.platform))
|
|
4306
|
+
if (!openclaw.os.includes(process.platform)) {
|
|
4307
|
+
return {
|
|
4308
|
+
eligible: false,
|
|
4309
|
+
reason: `Requires OS: ${openclaw.os.join(", ")} (current: ${process.platform})`
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
if (openclaw?.always) {
|
|
4314
|
+
return { eligible: true, reason: null };
|
|
4282
4315
|
}
|
|
4283
|
-
if (openclaw?.always) return true;
|
|
4284
4316
|
const requires = openclaw?.requires;
|
|
4285
|
-
if (!requires)
|
|
4317
|
+
if (!requires) {
|
|
4318
|
+
return { eligible: true, reason: null };
|
|
4319
|
+
}
|
|
4286
4320
|
if (requires.bins && requires.bins.length > 0) {
|
|
4287
|
-
|
|
4321
|
+
const missing = requires.bins.filter((bin) => !binaryExists({ name: bin }));
|
|
4322
|
+
if (missing.length > 0) {
|
|
4323
|
+
return { eligible: false, reason: `Missing binaries: ${missing.join(", ")}` };
|
|
4324
|
+
}
|
|
4288
4325
|
}
|
|
4289
4326
|
if (requires.anyBins && requires.anyBins.length > 0) {
|
|
4290
|
-
if (!requires.anyBins.some((bin) => binaryExists({ name: bin })))
|
|
4327
|
+
if (!requires.anyBins.some((bin) => binaryExists({ name: bin }))) {
|
|
4328
|
+
return {
|
|
4329
|
+
eligible: false,
|
|
4330
|
+
reason: `Requires at least one of: ${requires.anyBins.join(", ")}`
|
|
4331
|
+
};
|
|
4332
|
+
}
|
|
4291
4333
|
}
|
|
4292
4334
|
if (requires.env && requires.env.length > 0) {
|
|
4335
|
+
const skillKey = openclaw?.skillKey ?? frontmatter.name;
|
|
4336
|
+
const entry = skillsConfig.entries[skillKey];
|
|
4293
4337
|
const primaryEnv = openclaw?.primaryEnv;
|
|
4294
4338
|
const hasApiKeyInConfig = Boolean(entry?.apiKey);
|
|
4295
4339
|
for (const envVar of requires.env) {
|
|
4296
4340
|
if (process.env[envVar]) continue;
|
|
4297
4341
|
if (primaryEnv === envVar && hasApiKeyInConfig) continue;
|
|
4298
|
-
return false;
|
|
4342
|
+
return { eligible: false, reason: `Missing environment variable: ${envVar}` };
|
|
4299
4343
|
}
|
|
4300
4344
|
}
|
|
4301
4345
|
if (requires.config && requires.config.length > 0) {
|
|
4302
4346
|
for (const configPath of requires.config) {
|
|
4303
|
-
if (!getConfigValue({ config: fullConfig, path: configPath }))
|
|
4347
|
+
if (!getConfigValue({ config: fullConfig, path: configPath })) {
|
|
4348
|
+
return { eligible: false, reason: `Missing config value: ${configPath}` };
|
|
4349
|
+
}
|
|
4304
4350
|
}
|
|
4305
4351
|
}
|
|
4306
|
-
return true;
|
|
4352
|
+
return { eligible: true, reason: null };
|
|
4307
4353
|
}
|
|
4354
|
+
var binaryExistsCache = /* @__PURE__ */ new Map();
|
|
4308
4355
|
function binaryExists({ name }) {
|
|
4356
|
+
const cached = binaryExistsCache.get(name);
|
|
4357
|
+
if (cached !== void 0) return cached;
|
|
4358
|
+
let exists;
|
|
4309
4359
|
try {
|
|
4310
4360
|
execFileSync("which", [name], { stdio: "ignore" });
|
|
4311
|
-
|
|
4361
|
+
exists = true;
|
|
4312
4362
|
} catch {
|
|
4313
|
-
|
|
4363
|
+
exists = false;
|
|
4314
4364
|
}
|
|
4365
|
+
binaryExistsCache.set(name, exists);
|
|
4366
|
+
return exists;
|
|
4315
4367
|
}
|
|
4316
4368
|
function getConfigValue({
|
|
4317
4369
|
config,
|
|
@@ -4326,6 +4378,97 @@ function getConfigValue({
|
|
|
4326
4378
|
return current;
|
|
4327
4379
|
}
|
|
4328
4380
|
|
|
4381
|
+
// src/bundled-skills/index.ts
|
|
4382
|
+
function listBundledSkills({
|
|
4383
|
+
skillsConfig,
|
|
4384
|
+
fullConfig
|
|
4385
|
+
}) {
|
|
4386
|
+
const slugs = listBundledSlugs();
|
|
4387
|
+
return slugs.map((slug) => {
|
|
4388
|
+
const content = readBundledSkillContent({ slug });
|
|
4389
|
+
if (!content) {
|
|
4390
|
+
return {
|
|
4391
|
+
slug,
|
|
4392
|
+
frontmatter: null,
|
|
4393
|
+
enabled: false,
|
|
4394
|
+
eligible: false,
|
|
4395
|
+
ineligibilityReason: "Could not read SKILL.md",
|
|
4396
|
+
hasInstallSpecs: false
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4399
|
+
const raw = parseFrontmatter({ content });
|
|
4400
|
+
const frontmatter = raw ? buildFrontmatter({ raw }) : null;
|
|
4401
|
+
const skillKey = getSkillKey({ frontmatter, slug });
|
|
4402
|
+
const entry = skillsConfig.entries[skillKey];
|
|
4403
|
+
const enabled = entry?.enabled === true;
|
|
4404
|
+
const { eligible, reason } = frontmatter ? checkSkillEligibility({ frontmatter, skillsConfig, fullConfig }) : { eligible: false, reason: "Invalid frontmatter" };
|
|
4405
|
+
const hasInstallSpecs = (frontmatter?.openclaw?.install?.length ?? 0) > 0;
|
|
4406
|
+
return {
|
|
4407
|
+
slug,
|
|
4408
|
+
frontmatter,
|
|
4409
|
+
enabled,
|
|
4410
|
+
eligible,
|
|
4411
|
+
ineligibilityReason: reason,
|
|
4412
|
+
hasInstallSpecs
|
|
4413
|
+
};
|
|
4414
|
+
});
|
|
4415
|
+
}
|
|
4416
|
+
function getEnabledBundledSkills({
|
|
4417
|
+
skillsConfig,
|
|
4418
|
+
fullConfig
|
|
4419
|
+
}) {
|
|
4420
|
+
const all = listBundledSkills({ skillsConfig, fullConfig });
|
|
4421
|
+
return all.filter((s) => s.enabled && s.eligible && s.frontmatter).map((s) => ({
|
|
4422
|
+
frontmatter: s.frontmatter,
|
|
4423
|
+
slug: s.slug,
|
|
4424
|
+
relativePath: `bundled-skills/${s.slug}/SKILL.md`
|
|
4425
|
+
}));
|
|
4426
|
+
}
|
|
4427
|
+
function getBundledSkillPath({ slug }) {
|
|
4428
|
+
const info = getBundledSkillInfo({ slug });
|
|
4429
|
+
return info?.skillDir ?? null;
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
// src/onboarding/personality.ts
|
|
4433
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
4434
|
+
import { join as join9 } from "path";
|
|
4435
|
+
function getPersonalityFilePaths({ workspacePath }) {
|
|
4436
|
+
return {
|
|
4437
|
+
identityPath: join9(workspacePath, "IDENTITY.md"),
|
|
4438
|
+
soulPath: join9(workspacePath, "SOUL.md"),
|
|
4439
|
+
userPath: join9(workspacePath, "USER.md")
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
async function readPersonalityFiles({
|
|
4443
|
+
workspacePath
|
|
4444
|
+
}) {
|
|
4445
|
+
const { identityPath, soulPath, userPath } = getPersonalityFilePaths({ workspacePath });
|
|
4446
|
+
const [identity, soul, user] = await Promise.all([
|
|
4447
|
+
readOptionalFile2({ path: identityPath }),
|
|
4448
|
+
readOptionalFile2({ path: soulPath }),
|
|
4449
|
+
readOptionalFile2({ path: userPath })
|
|
4450
|
+
]);
|
|
4451
|
+
return {
|
|
4452
|
+
identity,
|
|
4453
|
+
soul,
|
|
4454
|
+
user
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
function hasCompletePersonalityFiles(files) {
|
|
4458
|
+
return typeof files.identity === "string" && files.identity.trim().length > 0 && typeof files.soul === "string" && files.soul.trim().length > 0 && typeof files.user === "string" && files.user.trim().length > 0;
|
|
4459
|
+
}
|
|
4460
|
+
async function readOptionalFile2({ path }) {
|
|
4461
|
+
try {
|
|
4462
|
+
return await readFile7(path, "utf8");
|
|
4463
|
+
} catch (error) {
|
|
4464
|
+
const maybeErrno = error;
|
|
4465
|
+
if (maybeErrno.code === "ENOENT") {
|
|
4466
|
+
return null;
|
|
4467
|
+
}
|
|
4468
|
+
throw error;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4329
4472
|
// src/agent/context.ts
|
|
4330
4473
|
async function loadAgentContext({
|
|
4331
4474
|
workspacePath,
|
|
@@ -4338,7 +4481,11 @@ async function loadAgentContext({
|
|
|
4338
4481
|
readWorkspaceGuide({ workspacePath }),
|
|
4339
4482
|
scanWorkspaceSkills({ workspacePath })
|
|
4340
4483
|
]);
|
|
4341
|
-
const
|
|
4484
|
+
const workspaceSkills = getEligibleSkills({ skills: allSkills, skillsConfig, fullConfig });
|
|
4485
|
+
const bundledSkills = getEnabledBundledSkills({ skillsConfig, fullConfig });
|
|
4486
|
+
const workspaceSlugs = new Set(workspaceSkills.map((s) => s.slug));
|
|
4487
|
+
const dedupedBundled = bundledSkills.filter((s) => !workspaceSlugs.has(s.slug));
|
|
4488
|
+
const skills = [...workspaceSkills, ...dedupedBundled];
|
|
4342
4489
|
const personalityFiles = hasCompletePersonalityFiles(rawPersonalityFiles) ? rawPersonalityFiles : void 0;
|
|
4343
4490
|
return { personalityFiles, toolNotesContent, agentsContent, skills };
|
|
4344
4491
|
}
|
|
@@ -4519,6 +4666,7 @@ var AgentTurnOrchestrator = class {
|
|
|
4519
4666
|
if (abortSignal.aborted) return;
|
|
4520
4667
|
const {
|
|
4521
4668
|
workspacePath,
|
|
4669
|
+
bundledSkillsDir,
|
|
4522
4670
|
aiAgent,
|
|
4523
4671
|
sessionManager,
|
|
4524
4672
|
schedulerService,
|
|
@@ -4618,6 +4766,7 @@ ${description}`;
|
|
|
4618
4766
|
toolDeps: this.toolDeps,
|
|
4619
4767
|
executionContext: {
|
|
4620
4768
|
workspaceRoot: workspacePath,
|
|
4769
|
+
bundledSkillsDir,
|
|
4621
4770
|
botTimezone: schedulerService.getTimezone(),
|
|
4622
4771
|
platform: event.platform,
|
|
4623
4772
|
chatId: event.chatId,
|
|
@@ -5932,11 +6081,11 @@ var ChatRegistry = class {
|
|
|
5932
6081
|
|
|
5933
6082
|
// src/config/loader.ts
|
|
5934
6083
|
import { access, mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
5935
|
-
import { dirname as
|
|
6084
|
+
import { dirname as dirname6 } from "path";
|
|
5936
6085
|
|
|
5937
6086
|
// src/config/paths.ts
|
|
5938
6087
|
import { homedir as homedir2 } from "os";
|
|
5939
|
-
import { join as join11 } from "path";
|
|
6088
|
+
import { dirname as dirname5, join as join11, resolve as resolve4, isAbsolute } from "path";
|
|
5940
6089
|
var CONFIG_PATH_ENV_VAR = "BABYCLAW_CONFIG_PATH";
|
|
5941
6090
|
function getDefaultConfigPath() {
|
|
5942
6091
|
return join11(homedir2(), ".babyclaw", "babyclaw.json");
|
|
@@ -5948,6 +6097,19 @@ function getConfigPath() {
|
|
|
5948
6097
|
}
|
|
5949
6098
|
return getDefaultConfigPath();
|
|
5950
6099
|
}
|
|
6100
|
+
function getConfigDir() {
|
|
6101
|
+
return dirname5(getConfigPath());
|
|
6102
|
+
}
|
|
6103
|
+
var DEFAULT_WORKSPACE_ROOT = "~/babyclaw";
|
|
6104
|
+
function resolveWorkspaceRoot({ configRoot }) {
|
|
6105
|
+
if (configRoot === "~" || configRoot.startsWith("~/")) {
|
|
6106
|
+
return join11(homedir2(), configRoot.slice(2));
|
|
6107
|
+
}
|
|
6108
|
+
if (isAbsolute(configRoot)) {
|
|
6109
|
+
return configRoot;
|
|
6110
|
+
}
|
|
6111
|
+
return resolve4(getConfigDir(), configRoot);
|
|
6112
|
+
}
|
|
5951
6113
|
|
|
5952
6114
|
// src/config/schema.ts
|
|
5953
6115
|
import { z as z11 } from "zod";
|
|
@@ -6101,9 +6263,9 @@ var babyclawConfigSchema = z11.object({
|
|
|
6101
6263
|
timezone: "UTC"
|
|
6102
6264
|
}),
|
|
6103
6265
|
workspace: z11.object({
|
|
6104
|
-
root: z11.string().min(1).default("
|
|
6266
|
+
root: z11.string().min(1).default("~/babyclaw")
|
|
6105
6267
|
}).strict().default({
|
|
6106
|
-
root: "
|
|
6268
|
+
root: "~/babyclaw"
|
|
6107
6269
|
}),
|
|
6108
6270
|
session: z11.object({
|
|
6109
6271
|
maxMessagesPerSession: z11.number().int().positive().default(120),
|
|
@@ -6163,7 +6325,7 @@ var DEFAULT_CONFIG_TEMPLATE = {
|
|
|
6163
6325
|
timezone: "UTC"
|
|
6164
6326
|
},
|
|
6165
6327
|
workspace: {
|
|
6166
|
-
root: "
|
|
6328
|
+
root: "~/babyclaw"
|
|
6167
6329
|
},
|
|
6168
6330
|
session: {
|
|
6169
6331
|
maxMessagesPerSession: 120,
|
|
@@ -6238,7 +6400,7 @@ async function loadConfigRaw() {
|
|
|
6238
6400
|
}
|
|
6239
6401
|
async function writeConfig({ config }) {
|
|
6240
6402
|
const configPath = getConfigPath();
|
|
6241
|
-
await mkdir5(
|
|
6403
|
+
await mkdir5(dirname6(configPath), { recursive: true });
|
|
6242
6404
|
await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
6243
6405
|
}
|
|
6244
6406
|
async function ensureConfigFileExists({ configPath }) {
|
|
@@ -6249,7 +6411,7 @@ async function ensureConfigFileExists({ configPath }) {
|
|
|
6249
6411
|
if (error.code !== "ENOENT") {
|
|
6250
6412
|
throw error;
|
|
6251
6413
|
}
|
|
6252
|
-
await mkdir5(
|
|
6414
|
+
await mkdir5(dirname6(configPath), { recursive: true });
|
|
6253
6415
|
await writeFile5(configPath, getDefaultConfigTemplate(), "utf8");
|
|
6254
6416
|
getLogger().warn({ configPath }, "Created config file. Fill required secrets and restart.");
|
|
6255
6417
|
}
|
|
@@ -6421,6 +6583,7 @@ var HeartbeatExecutor = class {
|
|
|
6421
6583
|
toolDeps: this.toolDeps,
|
|
6422
6584
|
executionContext: {
|
|
6423
6585
|
workspaceRoot: workspacePath,
|
|
6586
|
+
bundledSkillsDir: this.toolDeps.bundledSkillsDir,
|
|
6424
6587
|
botTimezone: schedulerService.getTimezone(),
|
|
6425
6588
|
platform: mainChat.platform,
|
|
6426
6589
|
chatId: platformChatId,
|
|
@@ -6877,6 +7040,7 @@ var SchedulerExecutor = class {
|
|
|
6877
7040
|
toolDeps: this.toolDeps,
|
|
6878
7041
|
executionContext: {
|
|
6879
7042
|
workspaceRoot: workspacePath,
|
|
7043
|
+
bundledSkillsDir: this.toolDeps.bundledSkillsDir,
|
|
6880
7044
|
botTimezone: schedulerService.getTimezone(),
|
|
6881
7045
|
platform: this.channelSender.platform,
|
|
6882
7046
|
chatId: chatIdStr,
|
|
@@ -7623,13 +7787,13 @@ function createDatabase({ workspacePath }) {
|
|
|
7623
7787
|
}
|
|
7624
7788
|
|
|
7625
7789
|
// src/database/migrate.ts
|
|
7626
|
-
import { dirname as
|
|
7790
|
+
import { dirname as dirname7, resolve as resolve5 } from "path";
|
|
7627
7791
|
import { fileURLToPath } from "url";
|
|
7628
7792
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
|
7629
|
-
var __dirname =
|
|
7793
|
+
var __dirname = dirname7(fileURLToPath(import.meta.url));
|
|
7630
7794
|
function applyMigrations({ workspacePath }) {
|
|
7631
7795
|
const db = createDatabase({ workspacePath });
|
|
7632
|
-
const migrationsFolder =
|
|
7796
|
+
const migrationsFolder = resolve5(__dirname, "..", "drizzle");
|
|
7633
7797
|
migrate(db, { migrationsFolder });
|
|
7634
7798
|
}
|
|
7635
7799
|
|
|
@@ -7667,7 +7831,8 @@ var GatewayRuntime = class {
|
|
|
7667
7831
|
},
|
|
7668
7832
|
"Gateway configuration summary"
|
|
7669
7833
|
);
|
|
7670
|
-
const workspacePath =
|
|
7834
|
+
const workspacePath = resolveWorkspaceRoot({ configRoot: config.workspace.root });
|
|
7835
|
+
mkdirSync3(workspacePath, { recursive: true });
|
|
7671
7836
|
log.info("Applying database migrations...");
|
|
7672
7837
|
applyMigrations({ workspacePath });
|
|
7673
7838
|
log.info("Database migrations applied");
|
|
@@ -7751,6 +7916,7 @@ var GatewayRuntime = class {
|
|
|
7751
7916
|
const adminSocketPath = getAdminSocketPath();
|
|
7752
7917
|
const toolDeps = {
|
|
7753
7918
|
workspacePath,
|
|
7919
|
+
bundledSkillsDir: getBundledSkillsDir(),
|
|
7754
7920
|
aiAgent,
|
|
7755
7921
|
sessionManager,
|
|
7756
7922
|
schedulerService,
|
|
@@ -7856,6 +8022,16 @@ var GatewayRuntime = class {
|
|
|
7856
8022
|
void this.stop().then(() => process.exit(0));
|
|
7857
8023
|
});
|
|
7858
8024
|
return { ok: true };
|
|
8025
|
+
},
|
|
8026
|
+
"/reload-skills": async () => {
|
|
8027
|
+
const freshConfig = await loadConfigRaw();
|
|
8028
|
+
if (!freshConfig) {
|
|
8029
|
+
return { ok: false, error: "config_not_found" };
|
|
8030
|
+
}
|
|
8031
|
+
toolDeps.skillsConfig = freshConfig.skills;
|
|
8032
|
+
toolDeps.fullConfig = freshConfig;
|
|
8033
|
+
log.info("Skills configuration reloaded via admin socket");
|
|
8034
|
+
return { ok: true };
|
|
7859
8035
|
}
|
|
7860
8036
|
}
|
|
7861
8037
|
});
|
|
@@ -7956,6 +8132,9 @@ var AdminClient = class {
|
|
|
7956
8132
|
async shutdown() {
|
|
7957
8133
|
return this.get({ path: "/shutdown" });
|
|
7958
8134
|
}
|
|
8135
|
+
async reloadSkills() {
|
|
8136
|
+
return this.get({ path: "/reload-skills" });
|
|
8137
|
+
}
|
|
7959
8138
|
get({ path }) {
|
|
7960
8139
|
return new Promise((resolve6, reject) => {
|
|
7961
8140
|
const req = request(
|
|
@@ -7997,6 +8176,7 @@ export {
|
|
|
7997
8176
|
AdminClient,
|
|
7998
8177
|
CONFIG_PATH_ENV_VAR,
|
|
7999
8178
|
ClawHubError,
|
|
8179
|
+
DEFAULT_WORKSPACE_ROOT,
|
|
8000
8180
|
GatewayRuntime,
|
|
8001
8181
|
SUPPORTED_PROVIDERS,
|
|
8002
8182
|
SkillAlreadyInstalledError,
|
|
@@ -8004,14 +8184,19 @@ export {
|
|
|
8004
8184
|
createChildLogger,
|
|
8005
8185
|
createLogger,
|
|
8006
8186
|
getAdminSocketPath,
|
|
8187
|
+
getBundledSkillPath,
|
|
8007
8188
|
getConfigPath,
|
|
8008
8189
|
getDefaultConfigPath,
|
|
8009
8190
|
getDefaultConfigTemplate,
|
|
8191
|
+
getEnabledBundledSkills,
|
|
8010
8192
|
getLogger,
|
|
8193
|
+
getSkillKey,
|
|
8011
8194
|
installSkillFromClawHub,
|
|
8195
|
+
listBundledSkills,
|
|
8012
8196
|
loadConfig,
|
|
8013
8197
|
loadConfigRaw,
|
|
8014
8198
|
resolveLanguageModel,
|
|
8199
|
+
resolveWorkspaceRoot,
|
|
8015
8200
|
runSkillSetup,
|
|
8016
8201
|
writeConfig
|
|
8017
8202
|
};
|