From 08a66d7659fa4b8009d532a23955ccac3fbd72fc Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Mar 2026 14:39:24 +0000 Subject: [PATCH] refactor: restructure project layout and add install.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move src/ → plugin/ with subdirectories: - plugin/core/ (business logic, models, store, permissions, utils, memory) - plugin/tools/ (query, resources) - plugin/commands/ (placeholder for slash commands) - plugin/hooks/ (placeholder for lifecycle hooks) - plugin/index.ts (wiring layer only, no business logic) - Add install.mjs with --install, --uninstall, --openclaw-profile-path - Add skills/ and docs/ root directories - Move planning docs (PLAN.md, FEAT.md, AGENT_TASKS.md) to docs/ - Remove old scripts/install.sh - Update tsconfig rootDir: src → plugin - Update README.md and README.zh.md with new layout - Bump version to 0.2.0 - All tests pass --- README.md | 48 +++--- README.zh.md | 48 +++--- docs/.gitkeep | 0 AGENT_TASKS.md => docs/AGENT_TASKS.md | 0 FEAT.md => docs/FEAT.md | 0 PLAN.md => docs/PLAN.md | 0 install.mjs | 145 ++++++++++++++++++ package-lock.json | 4 +- package.json | 4 +- plugin/commands/.gitkeep | 0 {src => plugin/core}/config/defaults.ts | 0 {src => plugin/core}/memory/scopeMemory.ts | 0 {src => plugin/core}/models/audit.ts | 0 {src => plugin/core}/models/errors.ts | 0 {src => plugin/core}/models/types.ts | 0 {src => plugin/core}/permissions/authorize.ts | 0 {src => plugin/core}/store/auditStore.ts | 0 {src => plugin/core}/store/jsonStore.ts | 0 {src => plugin/core}/utils/fs.ts | 0 {src => plugin/core}/utils/id.ts | 0 src/index.ts => plugin/core/yonexus.ts | 6 +- plugin/hooks/.gitkeep | 0 plugin/index.ts | 45 ++++++ {src => plugin}/tools/query.ts | 4 +- {src => plugin}/tools/resources.ts | 6 +- scripts/demo.ts | 2 +- scripts/install.sh | 21 --- skills/.gitkeep | 0 tests/smoke.ts | 4 +- tsconfig.json | 4 +- 30 files changed, 260 insertions(+), 81 deletions(-) create mode 100644 docs/.gitkeep rename AGENT_TASKS.md => docs/AGENT_TASKS.md (100%) rename FEAT.md => docs/FEAT.md (100%) rename PLAN.md => docs/PLAN.md (100%) create mode 100644 install.mjs create mode 100644 plugin/commands/.gitkeep rename {src => plugin/core}/config/defaults.ts (100%) rename {src => plugin/core}/memory/scopeMemory.ts (100%) rename {src => plugin/core}/models/audit.ts (100%) rename {src => plugin/core}/models/errors.ts (100%) rename {src => plugin/core}/models/types.ts (100%) rename {src => plugin/core}/permissions/authorize.ts (100%) rename {src => plugin/core}/store/auditStore.ts (100%) rename {src => plugin/core}/store/jsonStore.ts (100%) rename {src => plugin/core}/utils/fs.ts (100%) rename {src => plugin/core}/utils/id.ts (100%) rename src/index.ts => plugin/core/yonexus.ts (98%) create mode 100644 plugin/hooks/.gitkeep create mode 100644 plugin/index.ts rename {src => plugin}/tools/query.ts (95%) rename {src => plugin}/tools/resources.ts (95%) delete mode 100755 scripts/install.sh create mode 100644 skills/.gitkeep diff --git a/README.md b/README.md index 985a071..d5438fb 100644 --- a/README.md +++ b/README.md @@ -24,24 +24,20 @@ Yonexus is an OpenClaw plugin for organization hierarchy and agent identity mana ```text . -├─ plugin.json -├─ src/ -│ ├─ index.ts -│ ├─ models/ -│ ├─ permissions/ -│ ├─ store/ -│ ├─ tools/ -│ ├─ memory/ -│ └─ utils/ -├─ scripts/ -│ ├─ install.sh -│ └─ demo.ts -├─ tests/ -│ └─ smoke.ts -├─ examples/ -│ └─ sample-data.json -└─ dist/ - └─ yonexus/ +├─ plugin/ +│ ├─ index.ts # wiring: init, register commands/hooks/tools +│ ├─ commands/ # slash commands +│ ├─ tools/ # query & resource tools +│ ├─ hooks/ # lifecycle hooks +│ └─ core/ # business logic, models, store, permissions +├─ skills/ # skill definitions +├─ docs/ # project documentation +├─ scripts/ # demo & utility scripts +├─ tests/ # tests +├─ install.mjs # install/uninstall script +├─ plugin.json # plugin manifest +├─ README.md +└─ README.zh.md ``` ## Requirements @@ -54,11 +50,23 @@ Yonexus is an OpenClaw plugin for organization hierarchy and agent identity mana ```bash npm install npm run build -bash scripts/install.sh npm run test:smoke npm run demo ``` +## Install / Uninstall + +```bash +# Install (builds and copies to ~/.openclaw/plugins/yonexus) +node install.mjs --install + +# Install to custom openclaw profile path +node install.mjs --install --openclaw-profile-path /path/to/.openclaw + +# Uninstall +node install.mjs --uninstall +``` + ## Configuration `plugin.json` includes default config: @@ -98,8 +106,6 @@ Data & audit: ## Testing -Smoke test: - ```bash npm run test:smoke ``` diff --git a/README.zh.md b/README.zh.md index 116b412..2588292 100644 --- a/README.zh.md +++ b/README.zh.md @@ -24,24 +24,20 @@ Yonexus 是一个用于 OpenClaw 的组织结构与 Agent 身份管理插件。 ```text . -├─ plugin.json -├─ src/ -│ ├─ index.ts -│ ├─ models/ -│ ├─ permissions/ -│ ├─ store/ -│ ├─ tools/ -│ ├─ memory/ -│ └─ utils/ -├─ scripts/ -│ ├─ install.sh -│ └─ demo.ts -├─ tests/ -│ └─ smoke.ts -├─ examples/ -│ └─ sample-data.json -└─ dist/ - └─ yonexus/ +├─ plugin/ +│ ├─ index.ts # 接线层:初始化、注册命令/hooks/tools +│ ├─ commands/ # slash commands +│ ├─ tools/ # 查询与资源工具 +│ ├─ hooks/ # 生命周期钩子 +│ └─ core/ # 业务逻辑、模型、存储、权限 +├─ skills/ # 技能定义 +├─ docs/ # 项目文档 +├─ scripts/ # 演示与工具脚本 +├─ tests/ # 测试 +├─ install.mjs # 安装/卸载脚本 +├─ plugin.json # 插件清单 +├─ README.md +└─ README.zh.md ``` ## 环境要求 @@ -54,11 +50,23 @@ Yonexus 是一个用于 OpenClaw 的组织结构与 Agent 身份管理插件。 ```bash npm install npm run build -bash scripts/install.sh npm run test:smoke npm run demo ``` +## 安装 / 卸载 + +```bash +# 安装(构建并复制到 ~/.openclaw/plugins/yonexus) +node install.mjs --install + +# 安装到自定义 openclaw profile 路径 +node install.mjs --install --openclaw-profile-path /path/to/.openclaw + +# 卸载 +node install.mjs --uninstall +``` + ## 配置说明 `plugin.json` 默认包含以下配置: @@ -98,8 +106,6 @@ npm run demo ## 测试 -冒烟测试: - ```bash npm run test:smoke ``` diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AGENT_TASKS.md b/docs/AGENT_TASKS.md similarity index 100% rename from AGENT_TASKS.md rename to docs/AGENT_TASKS.md diff --git a/FEAT.md b/docs/FEAT.md similarity index 100% rename from FEAT.md rename to docs/FEAT.md diff --git a/PLAN.md b/docs/PLAN.md similarity index 100% rename from PLAN.md rename to docs/PLAN.md diff --git a/install.mjs b/install.mjs new file mode 100644 index 0000000..04cee3b --- /dev/null +++ b/install.mjs @@ -0,0 +1,145 @@ +#!/usr/bin/env node + +/** + * Yonexus Plugin Installer v0.2.0 + * + * Usage: + * node install.mjs --install + * node install.mjs --install --openclaw-profile-path /path/to/.openclaw + * node install.mjs --uninstall + * node install.mjs --uninstall --openclaw-profile-path /path/to/.openclaw + */ + +import { execSync } from 'child_process'; +import { existsSync, mkdirSync, copyFileSync, readdirSync, rmSync } from 'fs'; +import { dirname, join, resolve } from 'path'; +import { fileURLToPath } from 'url'; +import { homedir } from 'os'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = resolve(dirname(__filename)); + +const PLUGIN_NAME = 'yonexus'; +const SRC_DIST_DIR = join(__dirname, 'dist', PLUGIN_NAME); + +// ── Parse arguments ───────────────────────────────────────────────────── + +const args = process.argv.slice(2); +const isInstall = args.includes('--install'); +const isUninstall = args.includes('--uninstall'); + +const profileIdx = args.indexOf('--openclaw-profile-path'); +let openclawProfilePath = null; +if (profileIdx !== -1 && args[profileIdx + 1]) { + openclawProfilePath = resolve(args[profileIdx + 1]); +} + +function resolveOpenclawPath() { + if (openclawProfilePath) return openclawProfilePath; + if (process.env.OPENCLAW_PATH) return resolve(process.env.OPENCLAW_PATH); + return join(homedir(), '.openclaw'); +} + +// ── Colors ────────────────────────────────────────────────────────────── + +const c = { + reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m', + yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', +}; +function log(msg, color = 'reset') { console.log(`${c[color]}${msg}${c.reset}`); } +function logOk(msg) { log(` ✓ ${msg}`, 'green'); } +function logWarn(msg) { log(` ⚠ ${msg}`, 'yellow'); } +function logErr(msg) { log(` ✗ ${msg}`, 'red'); } + +// ── Helpers ───────────────────────────────────────────────────────────── + +function copyDir(src, dest) { + mkdirSync(dest, { recursive: true }); + for (const entry of readdirSync(src, { withFileTypes: true })) { + const s = join(src, entry.name); + const d = join(dest, entry.name); + if (entry.name === 'node_modules') continue; + entry.isDirectory() ? copyDir(s, d) : copyFileSync(s, d); + } +} + +// ── Install ───────────────────────────────────────────────────────────── + +function install() { + console.log(''); + log('╔══════════════════════════════════════════════╗', 'cyan'); + log('║ Yonexus Plugin Installer v0.2.0 ║', 'cyan'); + log('╚══════════════════════════════════════════════╝', 'cyan'); + console.log(''); + + // 1. Build + log('[1/3] Building...', 'cyan'); + execSync('npm install', { cwd: __dirname, stdio: 'inherit' }); + execSync('npm run build', { cwd: __dirname, stdio: 'inherit' }); + + if (!existsSync(SRC_DIST_DIR)) { + logErr(`Build output not found at ${SRC_DIST_DIR}`); + process.exit(1); + } + logOk('Build complete'); + + // 2. Copy to plugins dir + log('[2/3] Installing...', 'cyan'); + const openclawPath = resolveOpenclawPath(); + const destDir = join(openclawPath, 'plugins', PLUGIN_NAME); + + log(` OpenClaw path: ${openclawPath}`, 'blue'); + + if (existsSync(destDir)) rmSync(destDir, { recursive: true, force: true }); + copyDir(SRC_DIST_DIR, destDir); + logOk(`Plugin files → ${destDir}`); + + // 3. Summary + log('[3/3] Done!', 'cyan'); + console.log(''); + log('✓ Yonexus installed successfully!', 'green'); + console.log(''); + log('Next steps:', 'blue'); + log(' openclaw gateway restart', 'cyan'); + console.log(''); +} + +// ── Uninstall ─────────────────────────────────────────────────────────── + +function uninstall() { + console.log(''); + log('Uninstalling Yonexus...', 'cyan'); + + const openclawPath = resolveOpenclawPath(); + const destDir = join(openclawPath, 'plugins', PLUGIN_NAME); + + if (existsSync(destDir)) { + rmSync(destDir, { recursive: true, force: true }); + logOk(`Removed ${destDir}`); + } else { + logWarn(`${destDir} not found, nothing to remove`); + } + + console.log(''); + log('✓ Yonexus uninstalled.', 'green'); + log('\nNext: openclaw gateway restart', 'yellow'); + console.log(''); +} + +// ── Main ──────────────────────────────────────────────────────────────── + +if (!isInstall && !isUninstall) { + console.log(''); + log('Yonexus Plugin Installer', 'cyan'); + console.log(''); + log('Usage:', 'blue'); + log(' node install.mjs --install Install plugin', 'reset'); + log(' node install.mjs --install --openclaw-profile-path Install to custom path', 'reset'); + log(' node install.mjs --uninstall Uninstall plugin', 'reset'); + log(' node install.mjs --uninstall --openclaw-profile-path Uninstall from custom path', 'reset'); + console.log(''); + process.exit(1); +} + +if (isInstall) install(); +if (isUninstall) uninstall(); diff --git a/package-lock.json b/package-lock.json index 6e9980f..0aee678 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openclaw-plugin-yonexus", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openclaw-plugin-yonexus", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@types/node": "^22.13.10", diff --git a/package.json b/package.json index 6faa4f5..49279e3 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "openclaw-plugin-yonexus", - "version": "0.1.0", + "version": "0.2.0", "description": "Yonexus OpenClaw plugin: hierarchy, identities, permissions, and scoped memory", "main": "dist/yonexus/index.js", "types": "dist/yonexus/index.d.ts", "scripts": { - "build": "tsc -p tsconfig.json", + "build": "tsc -p tsconfig.json && cp plugin.json dist/yonexus/plugin.json", "clean": "rm -rf dist", "prepare": "npm run clean && npm run build", "test:smoke": "tsx tests/smoke.ts", diff --git a/plugin/commands/.gitkeep b/plugin/commands/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/config/defaults.ts b/plugin/core/config/defaults.ts similarity index 100% rename from src/config/defaults.ts rename to plugin/core/config/defaults.ts diff --git a/src/memory/scopeMemory.ts b/plugin/core/memory/scopeMemory.ts similarity index 100% rename from src/memory/scopeMemory.ts rename to plugin/core/memory/scopeMemory.ts diff --git a/src/models/audit.ts b/plugin/core/models/audit.ts similarity index 100% rename from src/models/audit.ts rename to plugin/core/models/audit.ts diff --git a/src/models/errors.ts b/plugin/core/models/errors.ts similarity index 100% rename from src/models/errors.ts rename to plugin/core/models/errors.ts diff --git a/src/models/types.ts b/plugin/core/models/types.ts similarity index 100% rename from src/models/types.ts rename to plugin/core/models/types.ts diff --git a/src/permissions/authorize.ts b/plugin/core/permissions/authorize.ts similarity index 100% rename from src/permissions/authorize.ts rename to plugin/core/permissions/authorize.ts diff --git a/src/store/auditStore.ts b/plugin/core/store/auditStore.ts similarity index 100% rename from src/store/auditStore.ts rename to plugin/core/store/auditStore.ts diff --git a/src/store/jsonStore.ts b/plugin/core/store/jsonStore.ts similarity index 100% rename from src/store/jsonStore.ts rename to plugin/core/store/jsonStore.ts diff --git a/src/utils/fs.ts b/plugin/core/utils/fs.ts similarity index 100% rename from src/utils/fs.ts rename to plugin/core/utils/fs.ts diff --git a/src/utils/id.ts b/plugin/core/utils/id.ts similarity index 100% rename from src/utils/id.ts rename to plugin/core/utils/id.ts diff --git a/src/index.ts b/plugin/core/yonexus.ts similarity index 98% rename from src/index.ts rename to plugin/core/yonexus.ts index 468a331..c575921 100644 --- a/src/index.ts +++ b/plugin/core/yonexus.ts @@ -16,8 +16,8 @@ import type { import { authorize } from "./permissions/authorize"; import { AuditStore } from "./store/auditStore"; import { JsonStore } from "./store/jsonStore"; -import { queryIdentities } from "./tools/query"; -import { ResourceLayout } from "./tools/resources"; +import { queryIdentities } from "../tools/query"; +import { ResourceLayout } from "../tools/resources"; import { makeId } from "./utils/id"; export interface YonexusOptions { @@ -243,5 +243,3 @@ export class Yonexus { return this.store.snapshot(); } } - -export default Yonexus; diff --git a/plugin/hooks/.gitkeep b/plugin/hooks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugin/index.ts b/plugin/index.ts new file mode 100644 index 0000000..fcd15ae --- /dev/null +++ b/plugin/index.ts @@ -0,0 +1,45 @@ +/** + * Yonexus Plugin Entry + * + * This file is the wiring layer: it initializes and re-exports the core + * Yonexus class along with models, tools, and memory adapters. + * No business logic lives here. + */ + +// ── Core ──────────────────────────────────────────────────────────────── +export { Yonexus, type YonexusOptions } from "./core/yonexus"; + +// ── Models ────────────────────────────────────────────────────────────── +export { YonexusError } from "./core/models/errors"; +export type { ErrorCode } from "./core/models/errors"; +export type { AuditLogEntry } from "./core/models/audit"; +export type { + Action, + Actor, + Agent, + Department, + DocsScope, + DocsTopic, + Identity, + Organization, + QueryFilter, + QueryInput, + QueryOptions, + Role, + SchemaField, + Scope, + StoreState, + Supervisor, + Team, + YonexusSchema, +} from "./core/models/types"; + +// ── Tools ─────────────────────────────────────────────────────────────── +export { queryIdentities } from "./tools/query"; +export { ResourceLayout } from "./tools/resources"; + +// ── Memory ────────────────────────────────────────────────────────────── +export { ScopeMemory, type MemoryPort } from "./core/memory/scopeMemory"; + +// ── Default export ────────────────────────────────────────────────────── +export { Yonexus as default } from "./core/yonexus"; diff --git a/src/tools/query.ts b/plugin/tools/query.ts similarity index 95% rename from src/tools/query.ts rename to plugin/tools/query.ts index cf506b6..a2a0bd7 100644 --- a/src/tools/query.ts +++ b/plugin/tools/query.ts @@ -1,5 +1,5 @@ -import { YonexusError } from '../models/errors'; -import type { Identity, QueryFilter, QueryInput, QueryOptions, YonexusSchema } from "../models/types"; +import { YonexusError } from '../core/models/errors'; +import type { Identity, QueryFilter, QueryInput, QueryOptions, YonexusSchema } from "../core/models/types"; const DEFAULT_LIMIT = 20; const MAX_LIMIT = 100; diff --git a/src/tools/resources.ts b/plugin/tools/resources.ts similarity index 95% rename from src/tools/resources.ts rename to plugin/tools/resources.ts index 8b0f194..1125fae 100644 --- a/src/tools/resources.ts +++ b/plugin/tools/resources.ts @@ -1,8 +1,8 @@ import fs from 'node:fs'; import path from 'node:path'; -import { YonexusError } from '../models/errors'; -import type { DocsScope, DocsTopic } from '../models/types'; -import { slug } from '../utils/id'; +import { YonexusError } from '../core/models/errors'; +import type { DocsScope, DocsTopic } from '../core/models/types'; +import { slug } from '../core/utils/id'; const TOPICS: DocsTopic[] = ['docs', 'notes', 'knowledge', 'rules', 'lessons', 'workflows']; diff --git a/scripts/demo.ts b/scripts/demo.ts index 917d9d8..c423fb2 100644 --- a/scripts/demo.ts +++ b/scripts/demo.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import fs from 'node:fs'; -import { Yonexus } from '../src/index'; +import { Yonexus } from '../plugin/index'; const dataFile = path.resolve(process.cwd(), 'data/demo-org.json'); if (fs.existsSync(dataFile)) fs.unlinkSync(dataFile); diff --git a/scripts/install.sh b/scripts/install.sh deleted file mode 100755 index a9153c6..0000000 --- a/scripts/install.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Yonexus install script -# - Registers plugin name: yonexus -# - Places build output in dist/yonexus - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -DIST_DIR="$ROOT_DIR/dist/yonexus" - -cd "$ROOT_DIR" - -if [ ! -d node_modules ]; then - npm install -fi - -npm run build -mkdir -p "$DIST_DIR" -cp -f "$ROOT_DIR/plugin.json" "$DIST_DIR/plugin.json" - -echo "[yonexus] install complete -> $DIST_DIR" diff --git a/skills/.gitkeep b/skills/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/smoke.ts b/tests/smoke.ts index d500d58..e60389e 100644 --- a/tests/smoke.ts +++ b/tests/smoke.ts @@ -1,8 +1,8 @@ import assert from 'node:assert'; import path from 'node:path'; import fs from 'node:fs'; -import { Yonexus } from '../src/index'; -import { YonexusError } from '../src/models/errors'; +import { Yonexus } from '../plugin/index'; +import { YonexusError } from '../plugin/core/models/errors'; const root = path.resolve(process.cwd(), 'data/test-openclaw'); const dataFile = path.resolve(process.cwd(), 'data/test-org.json'); diff --git a/tsconfig.json b/tsconfig.json index 60ab324..35c53a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,9 +8,9 @@ "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "outDir": "dist/yonexus", - "rootDir": "src", + "rootDir": "plugin", "declaration": true, "types": ["node"] }, - "include": ["src/**/*.ts"] + "include": ["plugin/**/*.ts"] }