openclawdreams 1.5.0 → 1.5.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
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.5.1](https://github.com/RogueCtrl/OpenClawDreams/compare/v1.2.2...v1.5.1) (2026-03-08)
6
+
7
+
8
+ ### Features
9
+
10
+ * dream pipeline v1.3 — workspace diff context, groundDream(), and notification fallback ([#58](https://github.com/RogueCtrl/OpenClawDreams/issues/58)) ([c683fb6](https://github.com/RogueCtrl/OpenClawDreams/commit/c683fb6fa2ce96e5c0e88e9c671e832aa44be69a))
11
+ * insight continuity — thread explored territory into dream/reflect prompts ([#65](https://github.com/RogueCtrl/OpenClawDreams/issues/65)) ([c7bdee1](https://github.com/RogueCtrl/OpenClawDreams/commit/c7bdee151e908e2c10d541a245f846e0ecb91706))
12
+ * nightmare cycle — 5% chance + forced CLI command ([#64](https://github.com/RogueCtrl/OpenClawDreams/issues/64)) ([50c261c](https://github.com/RogueCtrl/OpenClawDreams/commit/50c261c2dbfa86e4dfede669a51b0deb60666136))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * DST-safe scheduler with catch-up window ([#54](https://github.com/RogueCtrl/OpenClawDreams/issues/54)) ([230a943](https://github.com/RogueCtrl/OpenClawDreams/commit/230a9436eadf4e374595a772d2b2de3ee910b6f6))
18
+ * prettier formatting — cli.ts and index.ts ([#52](https://github.com/RogueCtrl/OpenClawDreams/issues/52)) ([93df3d8](https://github.com/RogueCtrl/OpenClawDreams/commit/93df3d8e44a62ae79135eef765604786815d4f91))
19
+ * resolve MoltbookClient credentials from stable fallback path when DATA_DIR unset (fixes [#70](https://github.com/RogueCtrl/OpenClawDreams/issues/70)) ([48d3019](https://github.com/RogueCtrl/OpenClawDreams/commit/48d30191faa457a5dca9171816844758dfd7844a))
20
+ * run tests sequentially to prevent env var race condition ([#67](https://github.com/RogueCtrl/OpenClawDreams/issues/67)) ([22f0002](https://github.com/RogueCtrl/OpenClawDreams/commit/22f00022b4224422fa7fe0cd1b9c8dde6f8c5b2c))
21
+ * run tests with --test-isolation=process to prevent ESM module cache contamination between test files ([f53a222](https://github.com/RogueCtrl/OpenClawDreams/commit/f53a222a31588d4477bd034cc3764b1011801993))
22
+ * skip workspace diff on iCloud/sensitive paths; add workspaceDiffEnabled config ([dfe6b51](https://github.com/RogueCtrl/OpenClawDreams/commit/dfe6b51ac0ded5ba4ee9609089053cddb774221c))
23
+
24
+
25
+ ### Documentation
26
+
27
+ * remove roadmap from README and ROADMAP.md (tracked externally) ([9e8620d](https://github.com/RogueCtrl/OpenClawDreams/commit/9e8620dcc2e9bb176cef624a5c7bdfae026d2ced))
28
+ * update AGENTS.md and README for v1.3.0 — workspace diffs, groundDream(), notification fallback ([0a5b19a](https://github.com/RogueCtrl/OpenClawDreams/commit/0a5b19ab4a7a1e84b59ad5e4216be4b335eb767f))
29
+
5
30
  ## [1.5.0](https://github.com/RogueCtrl/OpenClawDreams/compare/v1.2.2...v1.5.0) (2026-03-08)
6
31
 
7
32
 
@@ -6,6 +6,10 @@ export declare const DATA_DIR: string;
6
6
  export declare const MEMORY_DIR: string;
7
7
  export declare const DREAMS_DIR: string;
8
8
  export declare const NIGHTMARES_DIR: string;
9
+ /** Stable fallback path for credentials when DATA_DIR is unset/volatile. */
10
+ export declare const STABLE_CONFIG_DIR: string;
11
+ export declare const STABLE_CREDENTIALS_FILE: string;
12
+ /** Primary credentials file path. Resolves to STABLE_CREDENTIALS_FILE if DATA_DIR is default/unset. */
9
13
  export declare const CREDENTIALS_FILE: string;
10
14
  export declare const AGENT_NAME: string;
11
15
  export declare const AGENT_MODEL: string;
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { config } from "dotenv";
5
5
  import { mkdirSync } from "node:fs";
6
+ import { homedir } from "node:os";
6
7
  import { dirname, resolve } from "node:path";
7
8
  import { fileURLToPath } from "node:url";
8
9
  config({ quiet: true });
@@ -14,7 +15,13 @@ export const DATA_DIR = resolve(BASE_DIR, "data");
14
15
  export const MEMORY_DIR = resolve(DATA_DIR, "memory");
15
16
  export const DREAMS_DIR = resolve(DATA_DIR, "dreams");
16
17
  export const NIGHTMARES_DIR = resolve(DATA_DIR, "nightmares");
17
- export const CREDENTIALS_FILE = resolve(DATA_DIR, "credentials.json");
18
+ /** Stable fallback path for credentials when DATA_DIR is unset/volatile. */
19
+ export const STABLE_CONFIG_DIR = resolve(homedir(), ".config", "openclawdreams");
20
+ export const STABLE_CREDENTIALS_FILE = resolve(STABLE_CONFIG_DIR, "credentials.json");
21
+ /** Primary credentials file path. Resolves to STABLE_CREDENTIALS_FILE if DATA_DIR is default/unset. */
22
+ export const CREDENTIALS_FILE = process.env.OPENCLAWDREAMS_DATA_DIR
23
+ ? resolve(DATA_DIR, "credentials.json")
24
+ : STABLE_CREDENTIALS_FILE;
18
25
  // Ensure directories exist
19
26
  for (const dir of [DATA_DIR, MEMORY_DIR, DREAMS_DIR, NIGHTMARES_DIR]) {
20
27
  mkdirSync(dir, { recursive: true });
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Moltbook API client.
3
3
  */
4
- import { readFileSync, writeFileSync, existsSync } from "node:fs";
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
5
+ import { dirname } from "node:path";
5
6
  import pRetry from "p-retry";
6
- import { MOLTBOOK_BASE_URL, CREDENTIALS_FILE } from "./config.js";
7
+ import { MOLTBOOK_BASE_URL, CREDENTIALS_FILE, STABLE_CREDENTIALS_FILE, } from "./config.js";
7
8
  import logger from "./logger.js";
8
9
  const RETRY_OPTIONS = {
9
10
  retries: 3,
@@ -18,14 +19,40 @@ export class MoltbookClient {
18
19
  this.baseUrl = MOLTBOOK_BASE_URL;
19
20
  }
20
21
  loadStoredKey() {
22
+ // 1. Try the primary path (this is either DATA_DIR/credentials.json OR stable path)
21
23
  if (existsSync(CREDENTIALS_FILE)) {
22
- const creds = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
23
- return creds.api_key ?? "";
24
+ try {
25
+ const creds = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
26
+ if (creds.api_key)
27
+ return creds.api_key;
28
+ }
29
+ catch (err) {
30
+ logger.error(`Error loading primary credentials: ${err}`);
31
+ }
32
+ }
33
+ // 2. Fall back to stable path if primary was missing or invalid
34
+ if (STABLE_CREDENTIALS_FILE !== CREDENTIALS_FILE &&
35
+ existsSync(STABLE_CREDENTIALS_FILE)) {
36
+ try {
37
+ const creds = JSON.parse(readFileSync(STABLE_CREDENTIALS_FILE, "utf-8"));
38
+ if (creds.api_key)
39
+ return creds.api_key;
40
+ }
41
+ catch (err) {
42
+ logger.error(`Error loading fallback credentials: ${err}`);
43
+ }
24
44
  }
25
45
  return "";
26
46
  }
27
47
  saveCredentials(data) {
48
+ // Ensure parent directory for the primary path exists
49
+ mkdirSync(dirname(CREDENTIALS_FILE), { recursive: true });
28
50
  writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2));
51
+ // Also save to stable path if primary is different (to support standalone/cli access)
52
+ if (CREDENTIALS_FILE !== STABLE_CREDENTIALS_FILE) {
53
+ mkdirSync(dirname(STABLE_CREDENTIALS_FILE), { recursive: true });
54
+ writeFileSync(STABLE_CREDENTIALS_FILE, JSON.stringify(data, null, 2));
55
+ }
29
56
  }
30
57
  headers() {
31
58
  const h = { "Content-Type": "application/json" };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=credentials_fallback.test.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { describe, it, before, after, mock } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync, readFileSync, } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ // Fake home dir
7
+ const fakeHome = mkdtempSync(join(tmpdir(), "es-fallback-test-home-"));
8
+ process.env.HOME = fakeHome;
9
+ // Unset DATA_DIR to ensure fallback path is used
10
+ delete process.env.OPENCLAWDREAMS_DATA_DIR;
11
+ // Import after setting HOME
12
+ const { STABLE_CREDENTIALS_FILE, CREDENTIALS_FILE } = await import("../src/config.js");
13
+ const { MoltbookClient } = await import("../src/moltbook.js");
14
+ function mockFetchJson(body, status = 200) {
15
+ return mock.fn(async () => {
16
+ return new Response(JSON.stringify(body), {
17
+ status,
18
+ headers: { "Content-Type": "application/json" },
19
+ });
20
+ });
21
+ }
22
+ describe("MoltbookClient Credentials Fallback", () => {
23
+ let originalFetch;
24
+ before(() => {
25
+ originalFetch = globalThis.fetch;
26
+ });
27
+ after(() => {
28
+ globalThis.fetch = originalFetch;
29
+ rmSync(fakeHome, { recursive: true, force: true });
30
+ });
31
+ it("resolves CREDENTIALS_FILE to stable fallback when DATA_DIR is unset", () => {
32
+ assert.equal(CREDENTIALS_FILE, STABLE_CREDENTIALS_FILE);
33
+ assert.ok(CREDENTIALS_FILE.includes(".config/openclawdreams"));
34
+ });
35
+ it("loads stored key from stable fallback when DATA_DIR is unset", async () => {
36
+ // Prepare fake credentials in the stable location
37
+ const configDir = join(fakeHome, ".config", "openclawdreams");
38
+ mkdirSync(configDir, { recursive: true });
39
+ const credsFile = join(configDir, "credentials.json");
40
+ writeFileSync(credsFile, JSON.stringify({ api_key: "fallback-key-123" }));
41
+ const client = new MoltbookClient();
42
+ globalThis.fetch = mockFetchJson({ status: "ok" });
43
+ await client.status();
44
+ const calls = globalThis.fetch.mock.calls;
45
+ const [, init] = calls[0].arguments;
46
+ const headers = init.headers;
47
+ assert.equal(headers["Authorization"], "Bearer fallback-key-123");
48
+ });
49
+ it("saves credentials to stable fallback when DATA_DIR is unset", async () => {
50
+ globalThis.fetch = mockFetchJson({
51
+ agent: {
52
+ api_key: "new-key-789",
53
+ claim_url: "https://moltbook.com/claim/xyz",
54
+ verification_code: "VERIFY789",
55
+ },
56
+ });
57
+ const client = new MoltbookClient("bootstrap-key");
58
+ await client.register("TestBot", "A test agent");
59
+ const credsFile = STABLE_CREDENTIALS_FILE;
60
+ assert.ok(existsSync(credsFile), "credentials file should be saved in stable location");
61
+ const creds = JSON.parse(readFileSync(credsFile, "utf-8"));
62
+ assert.equal(creds.api_key, "new-key-789");
63
+ });
64
+ });
65
+ //# sourceMappingURL=credentials_fallback.test.js.map
@@ -1,11 +1,14 @@
1
1
  import { describe, it, after, mock, beforeEach } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdtempSync, rmSync, existsSync, readFileSync } from "node:fs";
3
+ import { mkdtempSync, rmSync, existsSync, readFileSync, mkdirSync, writeFileSync, } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  // Isolated data dir
7
7
  const testDir = mkdtempSync(join(tmpdir(), "es-moltbook-test-"));
8
8
  process.env.OPENCLAWDREAMS_DATA_DIR = testDir;
9
+ // Isolated home dir
10
+ const fakeHome = mkdtempSync(join(tmpdir(), "es-moltbook-test-home-"));
11
+ process.env.HOME = fakeHome;
9
12
  const { MoltbookClient } = await import("../src/moltbook.js");
10
13
  const { CREDENTIALS_FILE } = await import("../src/config.js");
11
14
  const { closeLogger } = await import("../src/logger.js");
@@ -156,9 +159,27 @@ describe("MoltbookClient", () => {
156
159
  const headers = init.headers;
157
160
  assert.equal(headers["Authorization"], "Bearer new-key-456");
158
161
  });
162
+ it("loads from stable fallback if primary is missing when DATA_DIR IS set", async () => {
163
+ // 1. Delete primary credentials file if it exists
164
+ if (existsSync(CREDENTIALS_FILE))
165
+ rmSync(CREDENTIALS_FILE);
166
+ // 2. Prepare fake credentials in the stable location
167
+ mkdirSync(join(fakeHome, ".config", "openclawdreams"), { recursive: true });
168
+ const credsFile = join(fakeHome, ".config", "openclawdreams", "credentials.json");
169
+ writeFileSync(credsFile, JSON.stringify({ api_key: "cross-fallback-key-abc" }));
170
+ // 3. Client should load from stable path
171
+ const client = new MoltbookClient();
172
+ globalThis.fetch = mockFetchJson({ status: "ok" });
173
+ await client.status();
174
+ const calls = globalThis.fetch.mock.calls;
175
+ const [, init] = calls[0].arguments;
176
+ const headers = init.headers;
177
+ assert.equal(headers["Authorization"], "Bearer cross-fallback-key-abc");
178
+ });
159
179
  });
160
180
  after(async () => {
161
181
  await closeLogger();
162
182
  rmSync(testDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
183
+ rmSync(fakeHome, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
163
184
  });
164
185
  //# sourceMappingURL=moltbook.test.js.map
@@ -2,7 +2,7 @@
2
2
  "id": "openclawdreams",
3
3
  "name": "openclawdreams",
4
4
  "displayName": "ElectricSheep",
5
- "version": "1.5.0",
5
+ "version": "1.5.1",
6
6
  "description": "A reflection engine that synthesizes agent-operator interactions into dreams, enriched by community and web context",
7
7
  "entry": "dist/src/index.js",
8
8
  "skills": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawdreams",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "A reflection engine that synthesizes agent-operator interactions into dreams",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,7 @@
11
11
  "build": "tsc",
12
12
  "dev": "tsc --watch",
13
13
  "start": "node dist/bin/openclawdreams.js",
14
- "test": "node --import tsx --test --test-concurrency=1 'test/**/*.test.ts'",
14
+ "test": "node --import tsx --test --test-isolation=process --test-concurrency=1 'test/**/*.test.ts'",
15
15
  "lint": "eslint 'src/**/*.ts' 'test/**/*.ts' 'bin/**/*.ts'",
16
16
  "lint:fix": "eslint --fix 'src/**/*.ts' 'test/**/*.ts' 'bin/**/*.ts'",
17
17
  "format": "prettier --write 'src/**/*.ts' 'test/**/*.ts' 'bin/**/*.ts'",