botrun-msync
Advanced tools
| import { loadConfig, saveConfig } from '../../config.mjs'; | ||
| export async function setDataPath(dataPath, configPath) { | ||
| const config = await loadConfig(configPath); | ||
| config.data_path = dataPath; | ||
| await saveConfig(configPath, config); | ||
| return { data_path: dataPath }; | ||
| } |
+1
-1
| { | ||
| "name": "botrun-msync", | ||
| "version": "0.2.1", | ||
| "version": "0.3.0", | ||
| "description": "Git-backed memory sync CLI for AI agents (forked from botrun-mcli@0.2.2)", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+38
-27
@@ -82,32 +82,34 @@ # botrun-msync — Git-backed Memory Sync CLI for Agents | ||
| ### Base Path | ||
| ### Config 與 Data 分離 | ||
| All `bms` data lives under a single base directory: | ||
| config.json(scope 定義)和 data(clone 下來的 repo)可以放在不同位置: | ||
| - **config.json** — 集中管理,預設 `~/.botrun/bms/config.json` | ||
| - **data** — 跟著使用場景走,由 `memory init` 決定位置,預設 `$PWD/data/` | ||
| ``` | ||
| ~/.botrun/bms/ ← default base path | ||
| ├── config.json ← scope definitions | ||
| └── data/ | ||
| ├── my-notes/ ← git clone of my-notes scope | ||
| ├── team1/ ← git clone of team1 scope | ||
| └── team2/ ← git clone of team2 scope | ||
| ~/.botrun/bms/config.json ← scope 定義 + data_path 指向 | ||
| /your/project/data/ ← data 跟著專案走 | ||
| ├── my-notes/ ← git clone of my-notes scope | ||
| ├── team1/ ← git clone of team1 scope | ||
| └── team2/ ← git clone of team2 scope | ||
| ``` | ||
| Override with CLI option or environment variable: | ||
| ### Config File | ||
| 預設路徑:`~/.botrun/bms/config.json` | ||
| 覆蓋方式: | ||
| ```bash | ||
| npx botrun-msync --bms-path /tmp/test-bms memory init # CLI option (highest priority) | ||
| BMS_PATH=/custom/path npx botrun-msync memory init # environment variable | ||
| npx botrun-msync --config-path /path/to/config.json memory init # CLI option | ||
| BMS_CONFIG=/path/to/config.json npx botrun-msync memory init # env var | ||
| ``` | ||
| Priority: `--bms-path` > `BMS_PATH` > `~/.botrun/bms/` | ||
| Priority: `--config-path` > `BMS_CONFIG` > `~/.botrun/bms/config.json` | ||
| ### Config File | ||
| Located at `<BMS_PATH>/config.json` (default: `~/.botrun/bms/config.json`). | ||
| Override config path independently with: `BMS_CONFIG=/path/to/config.json` | ||
| ```json | ||
| { | ||
| "data_path": "/your/project/data", | ||
| "scopes": { | ||
@@ -133,2 +135,9 @@ "my-notes": { | ||
| |-------|----------|-------------| | ||
| | `data_path` | no | Absolute path to data directory. Written by `memory init` | | ||
| | `scopes` | yes | Scope definitions (see below) | | ||
| Scope fields: | ||
| | Field | Required | Description | | ||
| |-------|----------|-------------| | ||
| | `repo` | yes | Git repo URL (without `https://`) | | ||
@@ -146,2 +155,3 @@ | `branch` | no | Git branch to use. Omit = repo default branch | | ||
| npx botrun-msync config remove-scope <name> | ||
| npx botrun-msync config set-data-path <path> | ||
| npx botrun-msync config show | ||
@@ -154,4 +164,3 @@ ``` | ||
| |----------|---------| | ||
| | `BMS_PATH` | Base directory for all bms data (default: `~/.botrun/bms`) | | ||
| | `BMS_CONFIG` | Config file path (overrides `<BMS_PATH>/config.json`) | | ||
| | `BMS_CONFIG` | Config file path (default: `~/.botrun/bms/config.json`) | | ||
@@ -162,11 +171,13 @@ Each scope's token is configured via `--token-env`, which points to an environment variable name. There are no global token variables — every scope must declare its own. | ||
| ### `npx botrun-msync memory init` | ||
| ### `npx botrun-msync memory init [--data-path <path>]` | ||
| Clones all configured scope repos to `<BMS_PATH>/data/<scope-name>/`. If already cloned, pulls latest. | ||
| Clones all configured scope repos. Writes `data_path` to config.json. | ||
| Data path priority: `--data-path` > config `data_path` > `$PWD/data/` | ||
| ```json | ||
| { | ||
| "scopes": { | ||
| "my-notes": { "local": "/root/.botrun/bms/data/my-notes" }, | ||
| "team1": { "local": "/root/.botrun/bms/data/team1" } | ||
| "my-notes": { "local": "/your/project/data/my-notes" }, | ||
| "team1": { "local": "/your/project/data/team1" } | ||
| } | ||
@@ -187,3 +198,3 @@ } | ||
| "access": "readwrite", | ||
| "local": "/root/.botrun/bms/data/my-notes" | ||
| "local": "/your/project/data/my-notes" | ||
| }, | ||
@@ -194,3 +205,3 @@ "team1": { | ||
| "access": "readonly", | ||
| "local": "/root/.botrun/bms/data/team1" | ||
| "local": "/your/project/data/team1" | ||
| } | ||
@@ -226,3 +237,3 @@ } | ||
| VM starts | ||
| → npx botrun-msync memory init # clone repos to <BMS_PATH>/data/ | ||
| → npx botrun-msync memory init # clone repos to data/ | ||
| → agent reads/writes files # using native tools (Read, Write, grep) | ||
@@ -229,0 +240,0 @@ → npx botrun-msync memory sync # push changes |
+19
-6
@@ -8,2 +8,3 @@ #!/usr/bin/env node | ||
| import { showConfig } from './commands/config/show.mjs'; | ||
| import { setDataPath } from './commands/config/set-data-path.mjs'; | ||
| import { initMemory } from './commands/memory/init.mjs'; | ||
@@ -31,9 +32,9 @@ import { syncMemory } from './commands/memory/sync.mjs'; | ||
| .description('Git-backed memory sync for agents') | ||
| .version('0.2.0') | ||
| .version('0.3.0') | ||
| .helpCommand(false) | ||
| .option('--bms-path <path>', 'Base directory for all bms data') | ||
| .option('--config-path <path>', 'Path to config.json') | ||
| .configureHelp({ formatHelp: (cmd) => JSON.stringify(jsonHelp(cmd), null, 2) }) | ||
| .hook('preAction', (thisCommand) => { | ||
| const bmsPath = thisCommand.opts().bmsPath; | ||
| if (bmsPath) process.env.BMS_PATH = bmsPath; | ||
| const configPath = thisCommand.opts().configPath; | ||
| if (configPath) process.env.BMS_CONFIG = configPath; | ||
| }); | ||
@@ -74,2 +75,10 @@ | ||
| configCmd | ||
| .command('set-data-path <path>') | ||
| .description('Update the data directory path in config') | ||
| .action(async (path) => { | ||
| const result = await setDataPath(path, getConfigPath()); | ||
| console.log(JSON.stringify(result, null, 2)); | ||
| }); | ||
| // --- memory --- | ||
@@ -82,4 +91,8 @@ const memoryCmd = program.command('memory').description('Manage memory repos'); | ||
| .description('Clone all scope repos') | ||
| .action(async () => { | ||
| const result = await initMemory({ configPath: getConfigPath() }); | ||
| .option('--data-path <path>', 'Directory to clone repos into (default: $PWD/data/)') | ||
| .action(async (opts) => { | ||
| const result = await initMemory({ | ||
| configPath: getConfigPath(), | ||
| dataDir: opts.dataPath, | ||
| }); | ||
| console.log(JSON.stringify(result, null, 2)); | ||
@@ -86,0 +99,0 @@ }); |
| import { mkdir, lstat } from 'node:fs/promises'; | ||
| import { join } from 'node:path'; | ||
| import { loadConfig, getBasePath } from '../../config.mjs'; | ||
| import { join, resolve } from 'node:path'; | ||
| import { loadConfig, saveConfig, getDataPath } from '../../config.mjs'; | ||
| import { gitExec } from '../../git-cmd.mjs'; | ||
@@ -54,6 +54,12 @@ import { detectProvider, getProvider, resolveToken } from '../../git/provider.mjs'; | ||
| const configPath = options.configPath; | ||
| const dataDir = options.dataDir || join(getBasePath(), 'data'); | ||
| const config = await loadConfig(configPath); | ||
| const localMode = options.localMode || false; | ||
| const config = await loadConfig(configPath); | ||
| // data path priority: options.dataDir > config.data_path > $PWD/data/ | ||
| const dataDir = options.dataDir || getDataPath(config) || resolve('data'); | ||
| // Write data_path back to config | ||
| config.data_path = dataDir; | ||
| await saveConfig(configPath, config); | ||
| const result = { scopes: {} }; | ||
@@ -60,0 +66,0 @@ |
| import { join } from 'node:path'; | ||
| import { lstat } from 'node:fs/promises'; | ||
| import { loadConfig, getBasePath } from '../../config.mjs'; | ||
| import { loadConfig, getDataPath } from '../../config.mjs'; | ||
| export async function listScopes(options = {}) { | ||
| const config = await loadConfig(options.configPath); | ||
| const dataDir = options.dataDir || join(getBasePath(), 'data'); | ||
| const dataDir = options.dataDir || getDataPath(config); | ||
| const result = { scopes: {} }; | ||
@@ -15,7 +15,11 @@ | ||
| const scopeDir = join(dataDir, name); | ||
| try { | ||
| await lstat(scopeDir); | ||
| entry.local = scopeDir; | ||
| } catch { | ||
| if (dataDir) { | ||
| const scopeDir = join(dataDir, name); | ||
| try { | ||
| await lstat(scopeDir); | ||
| entry.local = scopeDir; | ||
| } catch { | ||
| entry.local = null; | ||
| } | ||
| } else { | ||
| entry.local = null; | ||
@@ -22,0 +26,0 @@ } |
| import { join } from 'node:path'; | ||
| import { loadConfig, getBasePath } from '../../config.mjs'; | ||
| import { loadConfig, getDataPath } from '../../config.mjs'; | ||
| import { gitExec } from '../../git-cmd.mjs'; | ||
@@ -7,4 +7,8 @@ | ||
| const config = await loadConfig(options.configPath); | ||
| const dataDir = options.dataDir || join(getBasePath(), 'data'); | ||
| const dataDir = options.dataDir || getDataPath(config); | ||
| if (!dataDir) { | ||
| throw new Error('No data_path in config. Run "bms memory init" first.'); | ||
| } | ||
| const result = { synced: [], pulled: [], skipped: [] }; | ||
@@ -17,3 +21,2 @@ | ||
| // 1. Always pull remote changes first | ||
| try { | ||
@@ -25,3 +28,2 @@ const before = await gitExec(['-C', cloneDir, 'rev-parse', 'HEAD']); | ||
| } catch { | ||
| // pull may fail if no upstream set; try setting it | ||
| try { | ||
@@ -39,3 +41,2 @@ const branch = await gitExec(['-C', cloneDir, 'rev-parse', '--abbrev-ref', 'HEAD']); | ||
| // 2. Check for local uncommitted changes and push | ||
| const status = await gitExec(['-C', cloneDir, 'status', '--porcelain']); | ||
@@ -42,0 +43,0 @@ if (status) { |
+5
-5
@@ -5,10 +5,10 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises'; | ||
| const DEFAULT_BASE_PATH = join(homedir(), '.botrun', 'bms'); | ||
| const DEFAULT_CONFIG_PATH = join(homedir(), '.botrun', 'bms', 'config.json'); | ||
| export function getBasePath() { | ||
| return process.env.BMS_PATH || DEFAULT_BASE_PATH; | ||
| export function getConfigPath() { | ||
| return process.env.BMS_CONFIG || DEFAULT_CONFIG_PATH; | ||
| } | ||
| export function getConfigPath() { | ||
| return process.env.BMS_CONFIG || join(getBasePath(), 'config.json'); | ||
| export function getDataPath(config) { | ||
| return config.data_path || null; | ||
| } | ||
@@ -15,0 +15,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
19708
6.99%15
7.14%332
8.85%246
4.68%4
-20%