@rubytech/taskmaster 1.0.66 → 1.0.67

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.
@@ -42,7 +42,7 @@ function resolveStorePath(agentId, raw) {
42
42
  function mergeConfig(defaults, overrides, agentId) {
43
43
  const enabled = overrides?.enabled ?? defaults?.enabled ?? true;
44
44
  const sessionMemory = overrides?.experimental?.sessionMemory ?? defaults?.experimental?.sessionMemory ?? false;
45
- const provider = overrides?.provider ?? defaults?.provider ?? "local";
45
+ const provider = overrides?.provider ?? defaults?.provider ?? "auto";
46
46
  const defaultRemote = defaults?.remote;
47
47
  const overrideRemote = overrides?.remote;
48
48
  const hasRemote = Boolean(defaultRemote || overrideRemote);
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.66",
2
+ "version": "1.0.67",
3
3
  "commit": "d9051199b654984c06db2b45b2b5712191e13a2c",
4
- "builtAt": "2026-02-18T18:21:11.232Z"
4
+ "builtAt": "2026-02-18T18:41:34.789Z"
5
5
  }
@@ -402,7 +402,7 @@ const FIELD_HELP = {
402
402
  "agents.defaults.memorySearch": "Vector search over MEMORY.md and memory/*.md (per-agent overrides supported).",
403
403
  "agents.defaults.memorySearch.sources": 'Sources to index for memory search (default: ["memory"]; add "sessions" to include session transcripts).',
404
404
  "agents.defaults.memorySearch.experimental.sessionMemory": "Enable experimental session transcript indexing for memory search (default: false).",
405
- "agents.defaults.memorySearch.provider": 'Embedding provider ("openai", "gemini", or "local").',
405
+ "agents.defaults.memorySearch.provider": 'Embedding provider ("auto", "openai", "gemini", or "local"). Auto uses remote when API keys are available, falls back to local.',
406
406
  "agents.defaults.memorySearch.remote.baseUrl": "Custom base URL for remote embeddings (OpenAI-compatible proxies or Gemini overrides).",
407
407
  "agents.defaults.memorySearch.remote.apiKey": "Custom API key for the remote embedding provider.",
408
408
  "agents.defaults.memorySearch.remote.headers": "Extra headers for remote embeddings (merged; remote overrides OpenAI headers).",
@@ -286,7 +286,7 @@ export const MemorySearchSchema = z
286
286
  })
287
287
  .strict()
288
288
  .optional(),
289
- provider: z.union([z.literal("openai"), z.literal("local"), z.literal("gemini")]).optional(),
289
+ provider: z.union([z.literal("auto"), z.literal("openai"), z.literal("local"), z.literal("gemini")]).optional(),
290
290
  remote: z
291
291
  .object({
292
292
  baseUrl: z.string().optional(),
@@ -1,22 +1,29 @@
1
1
  import net from "node:net";
2
- import os from "node:os";
3
2
  import { pickPrimaryTailnetIPv4, pickPrimaryTailnetIPv6 } from "../infra/tailnet.js";
4
3
  /**
5
- * Collect all IP addresses assigned to this machine's network interfaces.
6
- * Includes LAN, loopback, Tailnet, and any other bound address.
7
- * Used to distinguish same-machine access from truly external requests.
4
+ * Check if an IPv4 address belongs to a private (RFC 1918) or link-local range.
5
+ * These addresses are non-routable on the public internet, so any request
6
+ * from one is by definition on the local network — safe for LAN access.
7
+ *
8
+ * Ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16
8
9
  */
9
- function getOwnIpAddresses() {
10
- const addresses = new Set();
11
- const interfaces = os.networkInterfaces();
12
- for (const iface of Object.values(interfaces)) {
13
- if (!iface)
14
- continue;
15
- for (const info of iface) {
16
- addresses.add(info.address.toLowerCase());
17
- }
18
- }
19
- return addresses;
10
+ function isPrivateIPv4(ip) {
11
+ const parts = ip.split(".");
12
+ if (parts.length !== 4)
13
+ return false;
14
+ const a = parseInt(parts[0], 10);
15
+ const b = parseInt(parts[1], 10);
16
+ if (Number.isNaN(a) || Number.isNaN(b))
17
+ return false;
18
+ if (a === 10)
19
+ return true; // 10.0.0.0/8
20
+ if (a === 172 && b >= 16 && b <= 31)
21
+ return true; // 172.16.0.0/12
22
+ if (a === 192 && b === 168)
23
+ return true; // 192.168.0.0/16
24
+ if (a === 169 && b === 254)
25
+ return true; // 169.254.0.0/16 (link-local)
26
+ return false;
20
27
  }
21
28
  export function isLoopbackAddress(ip) {
22
29
  if (!ip)
@@ -96,9 +103,9 @@ export function isLocalGatewayAddress(ip) {
96
103
  const tailnetIPv6 = pickPrimaryTailnetIPv6();
97
104
  if (tailnetIPv6 && ip.trim().toLowerCase() === tailnetIPv6.toLowerCase())
98
105
  return true;
99
- // Check all IPs assigned to this machine's network interfaces.
100
- // Covers same-machine access via LAN hostname, mDNS (.local), etc.
101
- if (getOwnIpAddresses().has(normalized))
106
+ // Any RFC 1918 private or link-local address is on the local network.
107
+ // These are non-routable on the public internet, so they're safe.
108
+ if (isPrivateIPv4(normalized))
102
109
  return true;
103
110
  return false;
104
111
  }
@@ -2,6 +2,7 @@ import fsSync from "node:fs";
2
2
  import os from "node:os";
3
3
  import { createSubsystemLogger } from "../logging/subsystem.js";
4
4
  import { resolveUserPath } from "../utils.js";
5
+ import { getCustomProviderApiKey } from "../agents/model-auth.js";
5
6
  import { createGeminiEmbeddingProvider } from "./embeddings-gemini.js";
6
7
  import { createOpenAiEmbeddingProvider } from "./embeddings-openai.js";
7
8
  import { importNodeLlamaCpp } from "./node-llama.js";
@@ -174,36 +175,48 @@ export async function createEmbeddingProvider(options) {
174
175
  };
175
176
  const formatPrimaryError = (err, provider) => provider === "local" ? formatLocalSetupError(err) : formatError(err);
176
177
  if (requestedProvider === "auto") {
177
- const missingKeyErrors = [];
178
- let localError = null;
178
+ const errors = [];
179
+ // 1. If user configured an explicit local model file, try it first.
179
180
  if (canAutoSelectLocal(options)) {
180
181
  try {
181
182
  const local = await createProvider("local");
182
183
  return { ...local, requestedProvider };
183
184
  }
184
185
  catch (err) {
185
- localError = formatLocalSetupError(err);
186
+ errors.push(formatLocalSetupError(err));
186
187
  }
187
188
  }
188
- for (const provider of ["openai", "gemini"]) {
189
+ // 2. Try remote providers preferred when API keys are available
190
+ // (faster, no RAM overhead, no model download).
191
+ // Only consider providers whose key is in the config (apiKeys section).
192
+ // Environment variables are ignored for auto-selection to avoid using
193
+ // stale dev keys that would fail at runtime.
194
+ const remoteProviders = [
195
+ { id: "openai", configKey: "openai" },
196
+ { id: "gemini", configKey: "google" },
197
+ ];
198
+ for (const { id, configKey } of remoteProviders) {
199
+ if (!getCustomProviderApiKey(options.config, configKey))
200
+ continue;
189
201
  try {
190
- const result = await createProvider(provider);
202
+ const result = await createProvider(id);
191
203
  return { ...result, requestedProvider };
192
204
  }
193
205
  catch (err) {
194
- const message = formatPrimaryError(err, provider);
195
- if (isMissingApiKeyError(err)) {
196
- missingKeyErrors.push(message);
197
- continue;
198
- }
199
- throw new Error(message);
206
+ errors.push(formatPrimaryError(err, id));
200
207
  }
201
208
  }
202
- const details = [...missingKeyErrors, localError].filter(Boolean);
203
- if (details.length > 0) {
204
- throw new Error(details.join("\n\n"));
209
+ // 3. Fall back to local (downloads default model on first use).
210
+ try {
211
+ const local = await createProvider("local");
212
+ return { ...local, requestedProvider };
205
213
  }
206
- throw new Error("No embeddings provider available.");
214
+ catch (err) {
215
+ errors.push(formatLocalSetupError(err));
216
+ }
217
+ throw new Error(errors.length > 0
218
+ ? errors.join("\n\n")
219
+ : "No embeddings provider available.");
207
220
  }
208
221
  try {
209
222
  const primary = await createProvider(requestedProvider);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.0.66",
3
+ "version": "1.0.67",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1267,6 +1267,57 @@ If the page loses connection during the update and doesn't reconnect within two
1267
1267
 
1268
1268
  ---
1269
1269
 
1270
+ ## Command Line (CLI)
1271
+
1272
+ Taskmaster includes a command-line tool called `taskmaster` that you can run in a terminal (Terminal on Mac, or via SSH on a Pi). You don't need the command line for day-to-day use — the Control Panel handles everything — but it's useful for troubleshooting, updating when the Control Panel isn't accessible, or automating tasks.
1273
+
1274
+ ### How to access the terminal
1275
+
1276
+ - **Mac:** Open **Terminal** (search for "Terminal" in Spotlight)
1277
+ - **Raspberry Pi (direct):** Plug in a keyboard and monitor, open Terminal from the taskbar
1278
+ - **Raspberry Pi (remote):** From another computer, run `ssh admin@taskmaster.local` (replace `taskmaster` with your hostname if changed)
1279
+
1280
+ ### Essential commands
1281
+
1282
+ | Command | What it does |
1283
+ |---------|-------------|
1284
+ | `taskmaster update` | Update to the latest version and restart the service |
1285
+ | `taskmaster daemon restart` | Restart the gateway service (picks up code and config changes) |
1286
+ | `taskmaster daemon status` | Check if the gateway service is running |
1287
+ | `taskmaster doctor` | Run health checks and fix common issues automatically |
1288
+ | `taskmaster status` | Show connection status for all channels (WhatsApp, Claude, etc.) |
1289
+ | `taskmaster dashboard` | Open the Control Panel in your browser |
1290
+
1291
+ ### Configuration commands
1292
+
1293
+ | Command | What it does |
1294
+ |---------|-------------|
1295
+ | `taskmaster config set <key> <value>` | Change a config setting (e.g., `taskmaster config set gateway.port 19000`) |
1296
+ | `taskmaster config get <key>` | Read a config setting |
1297
+ | `taskmaster config` | Open the interactive config wizard |
1298
+
1299
+ ### When to use the CLI instead of the Control Panel
1300
+
1301
+ - **Can't reach the Control Panel** — If the web UI is down, SSH into the device and use CLI commands to restart or update
1302
+ - **Updating from a specific version** — `taskmaster update` works even when the Control Panel can't load
1303
+ - **Changing network settings** — Port and hostname changes require the terminal (see "Changing Network Settings" above)
1304
+ - **Diagnosing problems** — `taskmaster doctor` runs automated health checks that can detect and fix issues the UI can't show
1305
+ - **Scripting and automation** — CLI commands can be combined in scripts for advanced setups
1306
+
1307
+ ### Getting help for any command
1308
+
1309
+ Add `--help` to any command to see its options:
1310
+
1311
+ ```bash
1312
+ taskmaster update --help
1313
+ taskmaster daemon --help
1314
+ taskmaster config set --help
1315
+ ```
1316
+
1317
+ Or run `taskmaster help` to see all available commands.
1318
+
1319
+ ---
1320
+
1270
1321
  ## Uninstalling Taskmaster
1271
1322
 
1272
1323
  ### From the Control Panel