wood-fired-tasks 2.0.0 → 2.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/CHANGELOG.md CHANGED
@@ -13,6 +13,26 @@ vulnerabilities, supply-chain pinning) are always called out under `Security`.
13
13
 
14
14
  _No changes yet._
15
15
 
16
+ ## [v2.0.1] - 2026-06-08
17
+
18
+ ### Fixed
19
+ - **`tasks setup` interactive "Remote" selection no longer crashes.** Choosing
20
+ option **3) Remote** from the interactive setup menu threw
21
+ `remote onboarding requires a --remote <url> base URL.` because the menu set
22
+ the mode but never captured the server URL — only the `--remote <url>` flag
23
+ did. The interactive remote path now prompts for the base URL when it is not
24
+ supplied as a flag, guarded by a TTY check so non-interactive/CI callers still
25
+ fail fast with the same clear message instead of hanging on stdin.
26
+ - **DB-path legacy-adopt tests are now hermetic.** `migrate-db-path.test.ts` and
27
+ `config-path-e2e.test.ts` exercise the "adopt `./data/tasks.db` when the OS
28
+ app-data DB is absent" precedence, but probed the real filesystem and so
29
+ failed on any dev machine where the `tasks` CLI had already created
30
+ `~/.local/share/wood-fired-tasks/tasks.db` (they passed on clean CI by luck).
31
+ `resolveMigrateDbPath`/`migrateCli` now forward the resolver's existing
32
+ injectable `exists` seam (default `fs.existsSync`, no behavior change), and the
33
+ tests inject a probe that hides the app-data DB — making the precedence-2
34
+ cases deterministic regardless of the developer's local state.
35
+
16
36
  ## [v2.0.0] - 2026-06-07
17
37
 
18
38
  The **identity auth cutover** — a breaking major. The legacy `X-API-Key`
@@ -8,7 +8,7 @@ import { resolveAssetPath } from '../../assets/resolve.js';
8
8
  import { configDir as defaultConfigDir } from '../../config/paths.js';
9
9
  import { resolvePathHint } from '../util/path-hint.js';
10
10
  import { buildNpmInvocation } from '../util/npm-spawn.js';
11
- import { selectFromMenu, promptSecret } from '../util/prompt.js';
11
+ import { selectFromMenu, promptSecret, promptLine } from '../util/prompt.js';
12
12
  import { shouldPrompt } from '../prompts/interactive.js';
13
13
  import { getServiceBackend } from './service.js';
14
14
  import { runDeviceLogin } from './login.js';
@@ -719,7 +719,20 @@ export async function runSetupInteractive(options = {}) {
719
719
  // token} without a URL can't get a LOCAL install mislabeled as remote.
720
720
  if (mode === 'remote') {
721
721
  const hasToken = typeof options.token === 'string' && options.token.length > 0;
722
- const hasRemote = typeof options.remote === 'string' && options.remote.length > 0;
722
+ let hasRemote = typeof options.remote === 'string' && options.remote.length > 0;
723
+ // Picking "Remote" from the interactive menu sets `mode` but NEVER the base
724
+ // URL — only the `--remote <url>` flag populates `options.remote`. Without
725
+ // this prompt the menu selection falls straight into runRemoteOnboarding,
726
+ // which throws 'remote onboarding requires a --remote <url> base URL.'.
727
+ // Prompt for it here so the menu path is usable; guarded by isInteractive()
728
+ // so a non-TTY / programmatic caller never hangs waiting on stdin.
729
+ if (!hasRemote && isInteractive()) {
730
+ const url = (await promptLine('Remote server base URL (e.g. http://host:3000): ', options.promptIO)).trim();
731
+ if (url.length > 0) {
732
+ options = { ...options, remote: url };
733
+ hasRemote = true;
734
+ }
735
+ }
723
736
  if (hasToken && hasRemote) {
724
737
  const setup = runSetup(options);
725
738
  return { mode: 'remote', oidc: null, method: 'manual-pat', ok: true, setup };
@@ -23,8 +23,12 @@ export declare function runMigrations(db: Database.Database): Promise<void>;
23
23
  * @param env - environment map (defaults to `process.env`).
24
24
  * @param cwd - base directory for resolving relative paths and probing the
25
25
  * legacy `./data/tasks.db` (defaults to `process.cwd()`).
26
+ * @param exists - filesystem existence probe forwarded to the unified resolver
27
+ * (defaults to `fs.existsSync`). Injectable so tests can isolate the
28
+ * legacy-adopt vs app-data branches without depending on whether the real OS
29
+ * app-data DB happens to exist on the dev machine running the suite.
26
30
  */
27
- export declare function resolveMigrateDbPath(env?: NodeJS.ProcessEnv, cwd?: string): string;
31
+ export declare function resolveMigrateDbPath(env?: NodeJS.ProcessEnv, cwd?: string, exists?: (p: string) => boolean): string;
28
32
  /**
29
33
  * CLI entry point body: resolve the target DB path via the unified resolver
30
34
  * (env > legacy-adopt > app-data default), ensure the parent
@@ -32,4 +36,4 @@ export declare function resolveMigrateDbPath(env?: NodeJS.ProcessEnv, cwd?: stri
32
36
  *
33
37
  * Extracted from the `isMain` guard so it is directly unit-testable.
34
38
  */
35
- export declare function migrateCli(env?: NodeJS.ProcessEnv, cwd?: string): Promise<string>;
39
+ export declare function migrateCli(env?: NodeJS.ProcessEnv, cwd?: string, exists?: (p: string) => boolean): Promise<string>;
@@ -1,5 +1,6 @@
1
1
  import { Umzug } from 'umzug';
2
2
  import { mkdir } from 'fs/promises';
3
+ import { existsSync } from 'fs';
3
4
  import { dirname, resolve, sep } from 'path';
4
5
  import { fileURLToPath, pathToFileURL } from 'url';
5
6
  import { initDatabase } from './database.js';
@@ -142,9 +143,13 @@ export async function runMigrations(db) {
142
143
  * @param env - environment map (defaults to `process.env`).
143
144
  * @param cwd - base directory for resolving relative paths and probing the
144
145
  * legacy `./data/tasks.db` (defaults to `process.cwd()`).
146
+ * @param exists - filesystem existence probe forwarded to the unified resolver
147
+ * (defaults to `fs.existsSync`). Injectable so tests can isolate the
148
+ * legacy-adopt vs app-data branches without depending on whether the real OS
149
+ * app-data DB happens to exist on the dev machine running the suite.
145
150
  */
146
- export function resolveMigrateDbPath(env = process.env, cwd = process.cwd()) {
147
- return resolve(cwd, resolveDbPath(env, cwd));
151
+ export function resolveMigrateDbPath(env = process.env, cwd = process.cwd(), exists = existsSync) {
152
+ return resolve(cwd, resolveDbPath(env, cwd, exists));
148
153
  }
149
154
  /**
150
155
  * CLI entry point body: resolve the target DB path via the unified resolver
@@ -153,8 +158,8 @@ export function resolveMigrateDbPath(env = process.env, cwd = process.cwd()) {
153
158
  *
154
159
  * Extracted from the `isMain` guard so it is directly unit-testable.
155
160
  */
156
- export async function migrateCli(env = process.env, cwd = process.cwd()) {
157
- const dbPath = resolveMigrateDbPath(env, cwd);
161
+ export async function migrateCli(env = process.env, cwd = process.cwd(), exists = existsSync) {
162
+ const dbPath = resolveMigrateDbPath(env, cwd, exists);
158
163
  // Create the parent directory (e.g. ./data, or a user-supplied path) if
159
164
  // it doesn't already exist. `recursive: true` is a no-op when present.
160
165
  await mkdir(dirname(dbPath), { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wood-fired-tasks",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Network-wide task tracking system for Wood Fired Games",
5
5
  "keywords": [
6
6
  "task-tracker",