dsh-horon 1.0.0 → 1.0.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/README.md CHANGED
@@ -153,8 +153,8 @@ node test/host-smoke.mjs && node test/artifact-smoke.mjs # 只跑纯函数
153
153
  | `hooks` | `true` | `false` 只留 `horon` 工具,不挂钩子(也可用环境变量 `HORON_HOOKS=off`) |
154
154
  | `skills` | `spawn` / `artifact` 模式外默认开 | 是否注册三个 horon skill |
155
155
  | `dataDir` | `~/.horon/docker` | `artifact` 与 `docker` 共用的宿主数据目录(库、`.env`、tag 插件) |
156
- | `dbFile` | `<dataDir>/data/horon.db` | SQLite 库;`docker` 模式下所在目录挂到容器 `/data` |
157
- | `envFile` | `<dataDir>/.env` | API key / 代理;`docker` 模式缺失时从 `docker/env.example` 复制,`artifact` 模式作为 `app/.env` 的种子 |
156
+ | `dbFile` | `<dataDir>/data/horon.db` | SQLite 库;`docker` 模式挂到容器 `/data`,`artifact` 模式写进 `app/.env` 的 `HORON_DB`。从 docker 切过来时**显式写这一行**,否则会指向另一个库 |
157
+ | `envFile` | `<dataDir>/.env` | API key / 代理;`docker` 模式缺失时从 `docker/env.example` 复制,`artifact` 模式作为 `app/.env` 的种子(`HORON_DB` 由 `dbFile` 覆盖) |
158
158
  | `tagPluginsDir` | `<dataDir>/tag_plugins` | tag 插件目录(`docker` 模式挂到 `/app/backend/tag_plugins`) |
159
159
  | `dockerBind` / `dockerPort` | `127.0.0.1` / `8710` | 容器发布地址 |
160
160
  | `image` / `container` | `<horon.lock.json image>` / `horon` | 镜像与容器名 |
package/artifact.js CHANGED
@@ -89,6 +89,14 @@ export function artifactSettings(config) {
89
89
  basePython: text(c.python) ?? (process.platform === 'win32' ? 'python' : 'python3'),
90
90
  // Shared with docker mode on purpose: switching modes keeps the database, .env and tag plugins.
91
91
  dataDir: path.resolve(expandHome(text(c.dataDir) ?? path.join(os.homedir(), '.horon', 'docker'))),
92
+ // An explicit dbFile / envFile wins, so a user who ran docker mode keeps the very same memory
93
+ // file and API keys after switching. "My notes are gone" must not be the first report.
94
+ dbFile: text(c.dbFile)
95
+ ? path.resolve(expandHome(text(c.dbFile)))
96
+ : path.join(path.resolve(expandHome(text(c.dataDir) ?? path.join(os.homedir(), '.horon', 'docker'))), 'data', 'horon.db'),
97
+ envFile: text(c.envFile)
98
+ ? path.resolve(expandHome(text(c.envFile)))
99
+ : path.join(path.resolve(expandHome(text(c.dataDir) ?? path.join(os.homedir(), '.horon', 'docker'))), '.env'),
92
100
  lock,
93
101
  }
94
102
  }
@@ -221,19 +229,20 @@ async function extract(archive, destination, log) {
221
229
  }
222
230
 
223
231
  // The service reads ROOT/.env and needs HORON_DB there. The file lives inside the version
224
- // directory, but every path in it points at the shared dataDir, so it survives an upgrade.
232
+ // directory, but every path in it points outside, at the shared database and key file, so it
233
+ // survives an upgrade. It is rewritten on every start of this code path, so editing dbFile or
234
+ // envFile in the profile takes effect instead of being silently ignored.
225
235
  async function writeEnv(a, log) {
226
236
  const file = path.join(a.appDir, '.env')
227
- if (existsSync(file)) return
228
- const shared = path.join(a.dataDir, '.env')
229
237
  let body = ''
230
- if (existsSync(shared)) {
231
- body = await fs.readFile(shared, 'utf8')
232
- log(`seeded app/.env from ${shared}`)
238
+ if (existsSync(a.envFile)) {
239
+ body = await fs.readFile(a.envFile, 'utf8')
240
+ log(`seeded app/.env from ${a.envFile}`)
233
241
  }
234
- const dbFile = path.join(a.dataDir, 'data', 'horon.db')
235
- if (!/^\s*HORON_DB\s*=/m.test(body)) body = `${body}${body && !body.endsWith('\n') ? '\n' : ''}HORON_DB=${dbFile}\n`
236
- await fs.writeFile(file, body, 'utf8')
242
+ const lines = body.split(/\r?\n/).filter((line) => !/^\s*HORON_DB\s*=/.test(line))
243
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop()
244
+ lines.push(`HORON_DB=${a.dbFile}`)
245
+ await fs.writeFile(file, `${lines.join('\n')}\n`, 'utf8')
237
246
  }
238
247
 
239
248
  /**
@@ -248,6 +257,8 @@ export async function provisionArtifact(a, log = () => {}) {
248
257
  const python = venvPython(a.appDir)
249
258
  if (existsSync(marker) && existsSync(python)) {
250
259
  log(`artifact already installed at ${a.root}`)
260
+ // Still reconcile app/.env: the config may name a different database since the last start.
261
+ await writeEnv(a, log)
251
262
  return { root: a.appDir, python }
252
263
  }
253
264
 
package/index.js CHANGED
@@ -442,6 +442,8 @@ function describeArtifact(a) {
442
442
  url: a.url,
443
443
  sha256: a.sha256 || undefined,
444
444
  dataDir: a.dataDir,
445
+ dbFile: a.dbFile,
446
+ envFile: a.envFile,
445
447
  pinned: a.lock?.horon?.commit,
446
448
  image: a.lock?.image ? `${a.lock.image.name}:${a.lock.image.tag}` : undefined,
447
449
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-horon",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Horon memory graph as a DeepSeek Harness plugin: the horon tool, lifecycle hooks, the console panel and a one-click service runtime (released artifact or Docker image)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,11 +40,16 @@
40
40
  "assemble": "node scripts/assemble.mjs"
41
41
  },
42
42
  "dsh": {
43
- "bundle": { "patch": "./cordis.patch.yml" },
43
+ "bundle": {
44
+ "patch": "./cordis.patch.yml"
45
+ },
44
46
  "client": {
45
47
  "platform": "web",
46
48
  "immediately": true,
47
- "inject": ["@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-settings"]
49
+ "inject": [
50
+ "@deepseek-ai/dsh-client-locale",
51
+ "@deepseek-ai/dsh-client-ui-settings"
52
+ ]
48
53
  }
49
54
  }
50
55
  }