clawdentity 0.0.0

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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/install-skill-mode.ts
4
+ import { constants, existsSync } from "fs";
5
+ import { access, copyFile, mkdir, readdir, readFile } from "fs/promises";
6
+ import { createRequire } from "module";
7
+ import { homedir } from "os";
8
+ import { dirname, join, relative } from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ // src/io.ts
12
+ var withTrailingNewline = (value) => value.endsWith("\n") ? value : `${value}
13
+ `;
14
+ var writeStdoutLine = (value) => {
15
+ process.stdout.write(withTrailingNewline(value));
16
+ };
17
+ var writeStderrLine = (value) => {
18
+ process.stderr.write(withTrailingNewline(value));
19
+ };
20
+
21
+ // src/install-skill-mode.ts
22
+ var OPENCLAW_DIR_NAME = ".openclaw";
23
+ var SKILL_PACKAGE_NAME = "@clawdentity/openclaw-skill";
24
+ var SKILL_DIR_NAME = "clawdentity-openclaw-relay";
25
+ var RELAY_MODULE_FILE_NAME = "relay-to-peer.mjs";
26
+ function isRecord(value) {
27
+ return typeof value === "object" && value !== null;
28
+ }
29
+ var SkillInstallError = class extends Error {
30
+ code;
31
+ details;
32
+ constructor(input) {
33
+ super(input.message);
34
+ this.name = "SkillInstallError";
35
+ this.code = input.code;
36
+ this.details = input.details ?? {};
37
+ }
38
+ };
39
+ function getErrorCode(error) {
40
+ if (!isRecord(error)) {
41
+ return void 0;
42
+ }
43
+ return typeof error.code === "string" ? error.code : void 0;
44
+ }
45
+ function parseBooleanFlag(value) {
46
+ if (value === void 0) {
47
+ return void 0;
48
+ }
49
+ const normalized = value.trim().toLowerCase();
50
+ if (normalized === "" || normalized === "1" || normalized === "true" || normalized === "yes") {
51
+ return true;
52
+ }
53
+ if (normalized === "0" || normalized === "false" || normalized === "no") {
54
+ return false;
55
+ }
56
+ return void 0;
57
+ }
58
+ function hasSkillFlagInNpmArgv(rawArgv) {
59
+ if (!rawArgv || rawArgv.trim().length === 0) {
60
+ return false;
61
+ }
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(rawArgv);
65
+ } catch {
66
+ return false;
67
+ }
68
+ if (!isRecord(parsed)) {
69
+ return false;
70
+ }
71
+ const original = parsed.original;
72
+ if (!Array.isArray(original)) {
73
+ return false;
74
+ }
75
+ return original.some((entry) => entry === "--skill");
76
+ }
77
+ function isSkillInstallRequested(env = process.env) {
78
+ const envFlag = parseBooleanFlag(env.npm_config_skill);
79
+ if (envFlag !== void 0) {
80
+ return envFlag;
81
+ }
82
+ return hasSkillFlagInNpmArgv(env.npm_config_argv);
83
+ }
84
+ function resolveHomeDir(inputHomeDir) {
85
+ if (typeof inputHomeDir === "string" && inputHomeDir.trim().length > 0) {
86
+ return inputHomeDir.trim();
87
+ }
88
+ return homedir();
89
+ }
90
+ function resolveOpenclawDir(homeDir, inputOpenclawDir) {
91
+ if (typeof inputOpenclawDir === "string" && inputOpenclawDir.trim().length > 0) {
92
+ return inputOpenclawDir.trim();
93
+ }
94
+ return join(homeDir, OPENCLAW_DIR_NAME);
95
+ }
96
+ function resolveSkillPackageRoot(input) {
97
+ if (typeof input.skillPackageRoot === "string" && input.skillPackageRoot.trim().length > 0) {
98
+ return input.skillPackageRoot.trim();
99
+ }
100
+ const overriddenRoot = input.env.CLAWDENTITY_SKILL_PACKAGE_ROOT;
101
+ if (typeof overriddenRoot === "string" && overriddenRoot.trim().length > 0) {
102
+ return overriddenRoot.trim();
103
+ }
104
+ const bundledSkillRoot = join(
105
+ dirname(fileURLToPath(import.meta.url)),
106
+ "..",
107
+ "skill-bundle",
108
+ "openclaw-skill"
109
+ );
110
+ if (existsSync(bundledSkillRoot)) {
111
+ return bundledSkillRoot;
112
+ }
113
+ const require2 = createRequire(import.meta.url);
114
+ let packageJsonPath;
115
+ try {
116
+ packageJsonPath = require2.resolve(`${SKILL_PACKAGE_NAME}/package.json`);
117
+ return dirname(packageJsonPath);
118
+ } catch {
119
+ const workspaceFallbackRoot = join(
120
+ dirname(fileURLToPath(import.meta.url)),
121
+ "..",
122
+ "..",
123
+ "openclaw-skill"
124
+ );
125
+ if (existsSync(workspaceFallbackRoot)) {
126
+ return workspaceFallbackRoot;
127
+ }
128
+ throw new SkillInstallError({
129
+ code: "CLI_SKILL_PACKAGE_NOT_FOUND",
130
+ message: "Skill artifacts are unavailable. Set CLAWDENTITY_SKILL_PACKAGE_ROOT or provide bundled skill assets before using --skill mode.",
131
+ details: {
132
+ packageName: SKILL_PACKAGE_NAME,
133
+ bundledSkillRoot,
134
+ workspaceFallbackRoot
135
+ }
136
+ });
137
+ }
138
+ }
139
+ async function assertReadableFile(filePath, details) {
140
+ try {
141
+ await access(filePath, constants.R_OK);
142
+ } catch (error) {
143
+ if (getErrorCode(error) === "ENOENT") {
144
+ throw new SkillInstallError({
145
+ code: "CLI_SKILL_ARTIFACT_MISSING",
146
+ message: "Required skill artifact is missing",
147
+ details: {
148
+ ...details,
149
+ sourcePath: filePath
150
+ }
151
+ });
152
+ }
153
+ throw error;
154
+ }
155
+ }
156
+ async function listFilesRecursively(directoryPath) {
157
+ const entries = await readdir(directoryPath, { withFileTypes: true });
158
+ const files = [];
159
+ for (const entry of entries.sort(
160
+ (left, right) => left.name.localeCompare(right.name)
161
+ )) {
162
+ const entryPath = join(directoryPath, entry.name);
163
+ if (entry.isDirectory()) {
164
+ files.push(...await listFilesRecursively(entryPath));
165
+ continue;
166
+ }
167
+ if (entry.isFile()) {
168
+ files.push(entryPath);
169
+ }
170
+ }
171
+ return files;
172
+ }
173
+ async function resolveArtifacts(input) {
174
+ const skillRoot = join(input.skillPackageRoot, "skill");
175
+ const skillDocSource = join(skillRoot, "SKILL.md");
176
+ const referencesRoot = join(skillRoot, "references");
177
+ const relaySource = join(
178
+ input.skillPackageRoot,
179
+ "dist",
180
+ RELAY_MODULE_FILE_NAME
181
+ );
182
+ await assertReadableFile(skillDocSource, {
183
+ artifact: "SKILL.md"
184
+ });
185
+ await assertReadableFile(relaySource, {
186
+ artifact: RELAY_MODULE_FILE_NAME
187
+ });
188
+ let referenceFiles;
189
+ try {
190
+ referenceFiles = await listFilesRecursively(referencesRoot);
191
+ } catch (error) {
192
+ if (getErrorCode(error) === "ENOENT") {
193
+ throw new SkillInstallError({
194
+ code: "CLI_SKILL_ARTIFACT_MISSING",
195
+ message: "Required skill references directory is missing",
196
+ details: {
197
+ sourcePath: referencesRoot,
198
+ artifact: "references"
199
+ }
200
+ });
201
+ }
202
+ throw error;
203
+ }
204
+ if (referenceFiles.length === 0) {
205
+ throw new SkillInstallError({
206
+ code: "CLI_SKILL_REFERENCE_DIR_EMPTY",
207
+ message: "Required skill references directory is empty",
208
+ details: {
209
+ sourcePath: referencesRoot
210
+ }
211
+ });
212
+ }
213
+ const targetSkillRoot = join(
214
+ input.openclawDir,
215
+ "workspace",
216
+ "skills",
217
+ SKILL_DIR_NAME
218
+ );
219
+ const artifacts = [
220
+ {
221
+ sourcePath: skillDocSource,
222
+ targetPath: join(targetSkillRoot, "SKILL.md")
223
+ },
224
+ {
225
+ sourcePath: relaySource,
226
+ targetPath: join(targetSkillRoot, RELAY_MODULE_FILE_NAME)
227
+ },
228
+ {
229
+ sourcePath: relaySource,
230
+ targetPath: join(
231
+ input.openclawDir,
232
+ "hooks",
233
+ "transforms",
234
+ RELAY_MODULE_FILE_NAME
235
+ )
236
+ }
237
+ ];
238
+ for (const referenceFile of referenceFiles) {
239
+ const relativePath = relative(referencesRoot, referenceFile);
240
+ artifacts.push({
241
+ sourcePath: referenceFile,
242
+ targetPath: join(targetSkillRoot, "references", relativePath)
243
+ });
244
+ }
245
+ return artifacts.sort(
246
+ (left, right) => left.targetPath.localeCompare(right.targetPath)
247
+ );
248
+ }
249
+ async function copyArtifact(input) {
250
+ const sourceContent = await readFile(input.sourcePath);
251
+ let existingContent;
252
+ try {
253
+ existingContent = await readFile(input.targetPath);
254
+ } catch (error) {
255
+ if (getErrorCode(error) !== "ENOENT") {
256
+ throw error;
257
+ }
258
+ }
259
+ if (existingContent !== void 0 && sourceContent.equals(existingContent)) {
260
+ return "unchanged";
261
+ }
262
+ await mkdir(dirname(input.targetPath), { recursive: true });
263
+ await copyFile(input.sourcePath, input.targetPath);
264
+ if (existingContent !== void 0) {
265
+ return "updated";
266
+ }
267
+ return "installed";
268
+ }
269
+ async function installOpenclawSkillArtifacts(options = {}) {
270
+ const env = options.env ?? process.env;
271
+ const homeDir = resolveHomeDir(options.homeDir);
272
+ const openclawDir = resolveOpenclawDir(homeDir, options.openclawDir);
273
+ const skillPackageRoot = resolveSkillPackageRoot({
274
+ skillPackageRoot: options.skillPackageRoot,
275
+ env
276
+ });
277
+ const artifacts = await resolveArtifacts({
278
+ skillPackageRoot,
279
+ openclawDir
280
+ });
281
+ const records = [];
282
+ for (const artifact of artifacts) {
283
+ const action = await copyArtifact(artifact);
284
+ records.push({
285
+ action,
286
+ sourcePath: artifact.sourcePath,
287
+ targetPath: artifact.targetPath
288
+ });
289
+ }
290
+ return {
291
+ homeDir,
292
+ openclawDir,
293
+ skillPackageRoot,
294
+ targetSkillDirectory: join(
295
+ openclawDir,
296
+ "workspace",
297
+ "skills",
298
+ SKILL_DIR_NAME
299
+ ),
300
+ records
301
+ };
302
+ }
303
+ function toSummaryCounts(records) {
304
+ const installed = records.filter((record) => record.action === "installed");
305
+ const updated = records.filter((record) => record.action === "updated");
306
+ const unchanged = records.filter((record) => record.action === "unchanged");
307
+ return `installed=${installed.length} updated=${updated.length} unchanged=${unchanged.length}`;
308
+ }
309
+ function formatSkillInstallError(error) {
310
+ if (error instanceof SkillInstallError) {
311
+ const details = Object.entries(error.details).map(([key, value]) => `${key}=${value}`).join(" ");
312
+ if (details.length === 0) {
313
+ return `${error.code}: ${error.message}`;
314
+ }
315
+ return `${error.code}: ${error.message} (${details})`;
316
+ }
317
+ if (error instanceof Error) {
318
+ return error.message;
319
+ }
320
+ return String(error);
321
+ }
322
+ async function runNpmSkillInstall(options = {}) {
323
+ const env = options.env ?? process.env;
324
+ const writeStdout = options.writeStdout ?? writeStdoutLine;
325
+ const writeStderr = options.writeStderr ?? writeStderrLine;
326
+ if (!isSkillInstallRequested(env)) {
327
+ return { skipped: true };
328
+ }
329
+ writeStdout("[clawdentity] skill install mode detected (--skill)");
330
+ try {
331
+ const result = await installOpenclawSkillArtifacts({
332
+ env,
333
+ homeDir: options.homeDir,
334
+ openclawDir: options.openclawDir,
335
+ skillPackageRoot: options.skillPackageRoot
336
+ });
337
+ for (const record of result.records) {
338
+ writeStdout(
339
+ `[clawdentity] ${record.action}: ${record.targetPath} (source: ${record.sourcePath})`
340
+ );
341
+ }
342
+ writeStdout(`[clawdentity] ${toSummaryCounts(result.records)}`);
343
+ return {
344
+ skipped: false,
345
+ ...result
346
+ };
347
+ } catch (error) {
348
+ writeStderr(
349
+ `[clawdentity] skill install failed: ${formatSkillInstallError(error)}`
350
+ );
351
+ throw error;
352
+ }
353
+ }
354
+
355
+ // src/postinstall.ts
356
+ runNpmSkillInstall().catch(() => {
357
+ process.exitCode = 1;
358
+ });
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "clawdentity",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "bin": {
11
+ "clawdentity": "dist/bin.js"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "types": "./dist/index.d.ts"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "postinstall.mjs",
22
+ "skill-bundle"
23
+ ],
24
+ "scripts": {
25
+ "build": "pnpm run sync:skill-bundle && tsup",
26
+ "format": "biome format .",
27
+ "lint": "biome lint .",
28
+ "prepack": "pnpm run build",
29
+ "postinstall": "node ./postinstall.mjs",
30
+ "sync:skill-bundle": "node ./scripts/sync-skill-bundle.mjs",
31
+ "test": "vitest run",
32
+ "typecheck": "tsc --noEmit"
33
+ },
34
+ "dependencies": {
35
+ "commander": "^13.1.0",
36
+ "ws": "^8.19.0"
37
+ },
38
+ "devDependencies": {
39
+ "@clawdentity/connector": "workspace:*",
40
+ "@clawdentity/protocol": "workspace:*",
41
+ "@clawdentity/sdk": "workspace:*",
42
+ "@types/node": "^22.18.11"
43
+ }
44
+ }
@@ -0,0 +1,48 @@
1
+ import { constants } from "node:fs";
2
+ import { access } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+
6
+ function parseBooleanFlag(value) {
7
+ if (typeof value !== "string") {
8
+ return undefined;
9
+ }
10
+
11
+ const normalized = value.trim().toLowerCase();
12
+ if (
13
+ normalized === "" ||
14
+ normalized === "1" ||
15
+ normalized === "true" ||
16
+ normalized === "yes"
17
+ ) {
18
+ return true;
19
+ }
20
+
21
+ if (normalized === "0" || normalized === "false" || normalized === "no") {
22
+ return false;
23
+ }
24
+
25
+ return undefined;
26
+ }
27
+
28
+ const packageRoot = dirname(fileURLToPath(import.meta.url));
29
+ const bundledPostinstallPath = join(packageRoot, "dist", "postinstall.js");
30
+ const skillRequested = parseBooleanFlag(process.env.npm_config_skill) === true;
31
+
32
+ try {
33
+ await access(bundledPostinstallPath, constants.R_OK);
34
+ await import(pathToFileURL(bundledPostinstallPath).href);
35
+ } catch (error) {
36
+ if (error && typeof error === "object" && error.code === "ENOENT") {
37
+ if (skillRequested) {
38
+ process.stderr.write(
39
+ `[clawdentity] skill install failed: build artifact not found at ${bundledPostinstallPath}\n`,
40
+ );
41
+ process.exitCode = 1;
42
+ }
43
+ } else {
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ process.stderr.write(`[clawdentity] postinstall failed: ${message}\n`);
46
+ process.exitCode = 1;
47
+ }
48
+ }
@@ -0,0 +1,12 @@
1
+ # AGENTS.md (apps/cli/skill-bundle)
2
+
3
+ ## Purpose
4
+ - Store bundled skill artifacts shipped with the CLI package for npm `--skill` postinstall.
5
+
6
+ ## Rules
7
+ - Treat this folder as generated release input; do not hand-edit bundled files.
8
+ - Regenerate by running `pnpm -F clawdentity run sync:skill-bundle` after changes in `apps/openclaw-skill`.
9
+ - Required bundled files:
10
+ - `openclaw-skill/skill/SKILL.md`
11
+ - `openclaw-skill/skill/references/*`
12
+ - `openclaw-skill/dist/relay-to-peer.mjs`