quilltap 4.11.0-dev.97 → 4.11.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,62 @@
1
+ /**
2
+ * Platform default instance root, including the Docker case.
3
+ *
4
+ * The bug this guards: inside the Docker image `quilltap db --lock-status`
5
+ * fell through to `~/.quilltap` (a home directory the image never creates),
6
+ * reported "No instance lock found" while the server held one, and
7
+ * `db --tables` reported a missing database.
8
+ *
9
+ * @jest-environment node
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const os = require('os');
15
+ const path = require('path');
16
+
17
+ const { getDefaultBaseDir, isDockerContainer, DOCKER_BASE_DIR } = require('../platform-paths');
18
+ const { resolveDataDir } = require('../db-helpers');
19
+
20
+ const savedEnv = { ...process.env };
21
+
22
+ afterEach(() => {
23
+ process.env = { ...savedEnv };
24
+ });
25
+
26
+ describe('platform default instance root', () => {
27
+ it('is /app/quilltap inside the Docker image', () => {
28
+ process.env.DOCKER_CONTAINER = 'true';
29
+ expect(isDockerContainer()).toBe(true);
30
+ expect(getDefaultBaseDir()).toBe(DOCKER_BASE_DIR);
31
+ });
32
+
33
+ it('is the per-OS location outside a container', () => {
34
+ delete process.env.DOCKER_CONTAINER;
35
+ if (isDockerContainer()) return; // the suite itself is running in a container
36
+ const expected = process.platform === 'darwin'
37
+ ? path.join(os.homedir(), 'Library', 'Application Support', 'Quilltap')
38
+ : process.platform === 'win32'
39
+ ? path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Quilltap')
40
+ : path.join(os.homedir(), '.quilltap');
41
+ expect(getDefaultBaseDir()).toBe(expected);
42
+ });
43
+ });
44
+
45
+ describe('resolveDataDir', () => {
46
+ it('defaults to the Docker data directory inside the image', () => {
47
+ process.env.DOCKER_CONTAINER = 'true';
48
+ delete process.env.QUILLTAP_DATA_DIR;
49
+ expect(resolveDataDir()).toBe('/app/quilltap/data');
50
+ });
51
+
52
+ it('still lets QUILLTAP_DATA_DIR win inside the image', () => {
53
+ process.env.DOCKER_CONTAINER = 'true';
54
+ process.env.QUILLTAP_DATA_DIR = '/elsewhere';
55
+ expect(resolveDataDir()).toBe(path.join('/elsewhere', 'data'));
56
+ });
57
+
58
+ it('still lets an explicit override win', () => {
59
+ process.env.DOCKER_CONTAINER = 'true';
60
+ expect(resolveDataDir('/explicit')).toBe(path.join('/explicit', 'data'));
61
+ });
62
+ });
package/lib/db-helpers.js CHANGED
@@ -15,14 +15,11 @@ function resolveDataDir(overrideDir) {
15
15
  : overrideDir;
16
16
  return path.join(resolved, 'data');
17
17
  }
18
- const os = require('os');
19
18
  if (process.env.QUILLTAP_DATA_DIR) {
20
19
  return path.join(process.env.QUILLTAP_DATA_DIR, 'data');
21
20
  }
22
- const home = os.homedir();
23
- if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Quilltap', 'data');
24
- if (process.platform === 'win32') return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Quilltap', 'data');
25
- return path.join(home, '.quilltap', 'data');
21
+ const { getDefaultBaseDir } = require('./platform-paths');
22
+ return path.join(getDefaultBaseDir(), 'data');
26
23
  }
27
24
 
28
25
  // Resolve the data dir + database passphrase a subcommand should use, given
@@ -206,10 +206,7 @@ let exitHandlersRegistered = false;
206
206
  function detectEnvironmentType() {
207
207
  if (process.versions && process.versions.electron) return 'electron';
208
208
  if (process.env.ELECTRON_DEV) return 'electron';
209
- if (process.env.DOCKER_CONTAINER === 'true') return 'docker';
210
- try {
211
- if (fs.existsSync('/.dockerenv')) return 'docker';
212
- } catch { /* not Docker */ }
209
+ if (require('./platform-paths').isDockerContainer()) return 'docker';
213
210
  return 'local';
214
211
  }
215
212
 
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Where an instance lives when nothing says otherwise.
5
+ *
6
+ * The CLI's mirror of the server's platform default (lib/paths.ts). Inside the
7
+ * Docker image the instance is always the bind mount at /app/quilltap, and the
8
+ * `nextjs` user has no home directory, so falling through to `~/.quilltap`
9
+ * sent `docker exec quilltap quilltap db …` to a database that does not exist
10
+ * and reported "no instance lock" while the server held one.
11
+ *
12
+ * Docker detection matches the server's lock-environment check
13
+ * (DOCKER_CONTAINER or /.dockerenv), deliberately without the server's
14
+ * "/app exists" heuristic, which misfires on hosts that happen to have /app.
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const os = require('os');
19
+ const path = require('path');
20
+
21
+ const DOCKER_BASE_DIR = '/app/quilltap';
22
+
23
+ function isDockerContainer() {
24
+ if (process.env.DOCKER_CONTAINER === 'true') return true;
25
+ try {
26
+ return fs.existsSync('/.dockerenv');
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ /** The instance root used when no flag, env var or registered instance names one. */
33
+ function getDefaultBaseDir() {
34
+ if (isDockerContainer()) return DOCKER_BASE_DIR;
35
+ const home = os.homedir();
36
+ if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Quilltap');
37
+ if (process.platform === 'win32') return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Quilltap');
38
+ return path.join(home, '.quilltap');
39
+ }
40
+
41
+ module.exports = {
42
+ DOCKER_BASE_DIR,
43
+ isDockerContainer,
44
+ getDefaultBaseDir,
45
+ };
@@ -21,6 +21,7 @@ const os = require('os');
21
21
  const crypto = require('crypto');
22
22
  const { execSync } = require('child_process');
23
23
  const { validateThemeBundle, validateManifest } = require('./theme-validation');
24
+ const { getDefaultBaseDir } = require('./platform-paths');
24
25
 
25
26
  // ============================================================================
26
27
  // COLOR HELPERS
@@ -54,10 +55,7 @@ function resolveBaseDir(overrideDir) {
54
55
  if (process.env.QUILLTAP_DATA_DIR) {
55
56
  return process.env.QUILLTAP_DATA_DIR;
56
57
  }
57
- const home = os.homedir();
58
- if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Quilltap');
59
- if (process.platform === 'win32') return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Quilltap');
60
- return path.join(home, '.quilltap');
58
+ return getDefaultBaseDir();
61
59
  }
62
60
 
63
61
  function getThemesDir(overrideDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quilltap",
3
- "version": "4.11.0-dev.97",
3
+ "version": "4.11.0",
4
4
  "description": "Self-hosted AI workspace for writers, worldbuilders, and roleplayers. Run with npx quilltap.",
5
5
  "author": {
6
6
  "name": "Charles Sebold",