@haaaiawd/second-nature 0.1.4 → 0.1.6

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/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { createRequire } from "node:module";
2
1
  function createFallbackCommands() {
3
2
  const commandNames = ["status", "policy", "credential", "quiet", "report", "session", "audit", "explain"];
4
3
  return commandNames.map((name) => ({
@@ -11,10 +10,9 @@ function createFallbackCommands() {
11
10
  }),
12
11
  }));
13
12
  }
14
- function resolveCommandRouterSafe() {
15
- const require = createRequire(import.meta.url);
13
+ async function resolveCommandRouterSafe() {
16
14
  try {
17
- const mod = require("./runtime/cli/index.js");
15
+ const mod = await import("./runtime/cli/index.js");
18
16
  if (mod?.createCommandRouter) {
19
17
  return mod.createCommandRouter();
20
18
  }
@@ -30,10 +28,9 @@ function resolveCommandRouterSafe() {
30
28
  },
31
29
  };
32
30
  }
33
- function createRuntimeService() {
34
- const require = createRequire(import.meta.url);
31
+ async function createRuntimeService() {
35
32
  try {
36
- const runtimeMod = require("./runtime/core/second-nature/runtime/service-entry.js");
33
+ const runtimeMod = await import("./runtime/core/second-nature/runtime/service-entry.js");
37
34
  if (runtimeMod?.startRuntimeService) {
38
35
  const handle = runtimeMod.startRuntimeService();
39
36
  return {
@@ -50,14 +47,13 @@ function createRuntimeService() {
50
47
  return {
51
48
  id: "second-nature-runtime",
52
49
  start() {
53
- return { ready: true, version: "0.1.4-minimal" };
50
+ return { ready: true, version: "0.1.6-minimal" };
54
51
  },
55
52
  };
56
53
  }
57
- function createLifecycleService() {
58
- const require = createRequire(import.meta.url);
54
+ async function createLifecycleService() {
59
55
  try {
60
- const lifecycleMod = require("./runtime/core/second-nature/runtime/lifecycle-service.js");
56
+ const lifecycleMod = await import("./runtime/core/second-nature/runtime/lifecycle-service.js");
61
57
  if (lifecycleMod?.recordRegistration) {
62
58
  return {
63
59
  id: "second-nature-lifecycle",
@@ -84,10 +80,10 @@ export default {
84
80
  id: "second-nature",
85
81
  name: "Second Nature",
86
82
  description: "Registers command/tool/service surface with load-reload lifecycle semantics.",
87
- register(api) {
88
- const router = resolveCommandRouterSafe();
89
- const runtimeService = createRuntimeService();
90
- const lifecycleService = createLifecycleService();
83
+ async register(api) {
84
+ const router = await resolveCommandRouterSafe();
85
+ const runtimeService = await createRuntimeService();
86
+ const lifecycleService = await createLifecycleService();
91
87
  api.registerService(runtimeService);
92
88
  api.registerService(lifecycleService);
93
89
  api.registerCli(({ program }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "second-nature",
3
3
  "name": "Second Nature",
4
- "version": "0.1.4",
4
+ "version": "0.1.6",
5
5
  "entry": "./index.js",
6
6
  "description": "OpenClaw native plugin package for continuity, explain, and recovery operations.",
7
7
  "capabilities": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haaaiawd/second-nature",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "OpenClaw native plugin for long-running agent continuity, Quiet memory curation, and explainable operator flows.",
5
5
  "keywords": [
6
6
  "openclaw",
@@ -30,8 +30,8 @@
30
30
  ]
31
31
  },
32
32
  "dependencies": {
33
- "better-sqlite3": "^11.10.0",
34
33
  "drizzle-orm": "^0.44.4",
34
+ "sql.js": "^1.14.1",
35
35
  "zod": "^4.1.5"
36
36
  }
37
37
  }
@@ -15,11 +15,20 @@ export function createActionBridge(stateApi) {
15
15
  if (!answer.trim()) {
16
16
  throw new Error("verification_answer_required");
17
17
  }
18
+ const credentialType = existing.credentialType ?? existing.credential_type;
19
+ const encryptedValue = existing.encryptedValue ?? existing.encrypted_value;
20
+ const expiresAt = existing.expiresAt ?? existing.expires_at;
21
+ const attemptsRemaining = existing.attemptsRemaining ?? existing.attempts_remaining;
22
+ const challengeText = existing.challengeText ?? existing.challenge_text;
18
23
  await stateApi.credentials.saveCredentialContext({
19
- ...existing,
24
+ platformId: existing.platformId ?? existing.platform_id,
25
+ credentialType: credentialType ?? "",
26
+ encryptedValue: encryptedValue ?? "",
20
27
  status: "active",
21
28
  verificationCode: answer,
22
- encryptedValue: existing.encryptedValue,
29
+ challengeText,
30
+ expiresAt,
31
+ attemptsRemaining,
23
32
  updatedAt: new Date().toISOString(),
24
33
  });
25
34
  },
@@ -59,7 +59,7 @@ export function createCliReadModels(deps) {
59
59
  },
60
60
  connectors: connectorSummary,
61
61
  credentials: credentials.map((item) => ({
62
- platformId: item.platformId,
62
+ platformId: item.platformId ?? item.platform_id,
63
63
  status: item.status,
64
64
  nextStep: buildCredentialNextStep(item.status),
65
65
  })),
@@ -138,7 +138,7 @@ export function createCliReadModels(deps) {
138
138
  };
139
139
  }
140
140
  return {
141
- platformId: record.platformId,
141
+ platformId: record.platformId ?? record.platform_id,
142
142
  status: record.status,
143
143
  verificationDeadline: record.expiresAt ?? undefined,
144
144
  attemptsRemaining: record.attemptsRemaining ?? undefined,
@@ -1,8 +1,8 @@
1
- import Database from "better-sqlite3";
2
- import { drizzle } from "drizzle-orm/better-sqlite3";
1
+ import { type Database } from "sql.js";
2
+ import { drizzle } from "drizzle-orm/sql-js";
3
3
  import * as schema from "./schema/index.js";
4
4
  export interface ObservabilityDatabase {
5
- sqlite: Database.Database;
5
+ sqlite: Database;
6
6
  db: ReturnType<typeof drizzle<typeof schema>>;
7
7
  schema: typeof schema;
8
8
  close(): void;
@@ -1,16 +1,41 @@
1
- import Database from "better-sqlite3";
2
- import { drizzle } from "drizzle-orm/better-sqlite3";
1
+ import initSqlJs from "sql.js";
2
+ import { drizzle } from "drizzle-orm/sql-js";
3
+ import path from "node:path";
4
+ import fs from "node:fs";
5
+ import { fileURLToPath } from "node:url";
3
6
  import * as schema from "./schema/index.js";
4
- export function createObservabilityDatabase(filename = "./data/observability.db") {
5
- const sqlite = new Database(filename);
6
- sqlite.pragma("journal_mode = WAL");
7
- sqlite.pragma("busy_timeout = 5000");
7
+ // Pre-initialize sql.js WASM at module load time
8
+ const SQL = await initSqlJs();
9
+ function resolveDbPath(filename) {
10
+ if (path.isAbsolute(filename) || filename === ":memory:") {
11
+ return filename;
12
+ }
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ const pluginRoot = path.resolve(__dirname, "..", "..", "..");
15
+ const dataDir = path.join(pluginRoot, "data");
16
+ if (!fs.existsSync(dataDir)) {
17
+ fs.mkdirSync(dataDir, { recursive: true });
18
+ }
19
+ return path.join(dataDir, filename);
20
+ }
21
+ export function createObservabilityDatabase(filename = "observability.db") {
22
+ const dbPath = resolveDbPath(filename);
23
+ const isMemory = filename === ":memory:";
24
+ let dbBuffer;
25
+ if (!isMemory && fs.existsSync(dbPath)) {
26
+ dbBuffer = fs.readFileSync(dbPath);
27
+ }
28
+ const sqlite = new SQL.Database(dbBuffer);
8
29
  const db = drizzle(sqlite, { schema });
9
30
  return {
10
31
  sqlite,
11
32
  db,
12
33
  schema,
13
34
  close() {
35
+ if (!isMemory) {
36
+ const data = sqlite.export();
37
+ fs.writeFileSync(dbPath, Buffer.from(data));
38
+ }
14
39
  sqlite.close();
15
40
  },
16
41
  };
@@ -1,8 +1,8 @@
1
- import Database from "better-sqlite3";
2
- import { drizzle } from "drizzle-orm/better-sqlite3";
1
+ import { type Database } from "sql.js";
2
+ import { drizzle } from "drizzle-orm/sql-js";
3
3
  import * as schema from "./schema/index.js";
4
4
  export interface StateDatabase {
5
- sqlite: Database.Database;
5
+ sqlite: Database;
6
6
  db: ReturnType<typeof drizzle<typeof schema>>;
7
7
  schema: typeof schema;
8
8
  close(): void;
@@ -1,16 +1,41 @@
1
- import Database from "better-sqlite3";
2
- import { drizzle } from "drizzle-orm/better-sqlite3";
1
+ import initSqlJs from "sql.js";
2
+ import { drizzle } from "drizzle-orm/sql-js";
3
+ import path from "node:path";
4
+ import fs from "node:fs";
5
+ import { fileURLToPath } from "node:url";
3
6
  import * as schema from "./schema/index.js";
4
- export function createStateDatabase(filename = "./data/state.db") {
5
- const sqlite = new Database(filename);
6
- sqlite.pragma("journal_mode = WAL");
7
- sqlite.pragma("busy_timeout = 5000");
7
+ // Pre-initialize sql.js WASM at module load time
8
+ const SQL = await initSqlJs();
9
+ function resolveDbPath(filename) {
10
+ if (path.isAbsolute(filename) || filename === ":memory:") {
11
+ return filename;
12
+ }
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ const pluginRoot = path.resolve(__dirname, "..", "..", "..");
15
+ const dataDir = path.join(pluginRoot, "data");
16
+ if (!fs.existsSync(dataDir)) {
17
+ fs.mkdirSync(dataDir, { recursive: true });
18
+ }
19
+ return path.join(dataDir, filename);
20
+ }
21
+ export function createStateDatabase(filename = "state.db") {
22
+ const dbPath = resolveDbPath(filename);
23
+ const isMemory = filename === ":memory:";
24
+ let dbBuffer;
25
+ if (!isMemory && fs.existsSync(dbPath)) {
26
+ dbBuffer = fs.readFileSync(dbPath);
27
+ }
28
+ const sqlite = new SQL.Database(dbBuffer);
8
29
  const db = drizzle(sqlite, { schema });
9
30
  return {
10
31
  sqlite,
11
32
  db,
12
33
  schema,
13
34
  close() {
35
+ if (!isMemory) {
36
+ const data = sqlite.export();
37
+ fs.writeFileSync(dbPath, Buffer.from(data));
38
+ }
14
39
  sqlite.close();
15
40
  },
16
41
  };
@@ -55,7 +55,8 @@ export class RepairAndBackupService {
55
55
  await fs.mkdir(backupDir, { recursive: true });
56
56
  const backupFileName = `state-${now.toISOString().replace(/[:.]/g, "-")}.db`;
57
57
  const backupPath = path.join(backupDir, backupFileName);
58
- await this.database.sqlite.backup(backupPath);
58
+ const dbData = this.database.sqlite.export();
59
+ await fs.writeFile(backupPath, Buffer.from(dbData));
59
60
  return {
60
61
  scannedAssetCount: assets.length,
61
62
  repairedOrphanIndexCount: repairedOrphanAssetIds.length,
@@ -27,7 +27,17 @@ export class DefaultStateAPI {
27
27
  const personaCandidateLoader = createPersonaCandidateLoader();
28
28
  this.read = {
29
29
  loadQuietInputs: (query) => quietInputLoader.loadQuietInputs(query),
30
- loadPolicy: (platformId) => policyRepository.findByPlatformId(platformId),
30
+ loadPolicy: async (platformId) => {
31
+ const record = await policyRepository.findByPlatformId(platformId);
32
+ if (!record)
33
+ return undefined;
34
+ return {
35
+ platformId: record.platformId ?? record.platform_id,
36
+ socialDailyLimit: record.socialDailyLimit ?? record.social_daily_limit,
37
+ quietEnabled: record.quietEnabled ?? Boolean(record.quiet_enabled),
38
+ updatedAt: record.updatedAt ?? record.updated_at,
39
+ };
40
+ },
31
41
  loadPersonaCandidates: (sceneContext) => personaCandidateLoader.loadPersonaCandidates(sceneContext),
32
42
  };
33
43
  this.write = {
@@ -46,7 +56,21 @@ export class DefaultStateAPI {
46
56
  };
47
57
  this.credentials = {
48
58
  loadCredentialContext: async (platformId) => {
49
- return credentialRepository.findByPlatformId(platformId);
59
+ const record = await credentialRepository.findByPlatformId(platformId);
60
+ if (!record)
61
+ return null;
62
+ const r = record;
63
+ return {
64
+ platformId: (r.platformId ?? r.platform_id),
65
+ credentialType: (r.credentialType ?? r.credential_type),
66
+ encryptedValue: (r.encryptedValue ?? r.encrypted_value),
67
+ status: (r.status ?? ""),
68
+ verificationCode: (r.verificationCode ?? r.verification_code ?? null),
69
+ challengeText: (r.challengeText ?? r.challenge_text ?? null),
70
+ expiresAt: (r.expiresAt ?? r.expires_at ?? null),
71
+ attemptsRemaining: (r.attemptsRemaining ?? r.attempts_remaining ?? null),
72
+ updatedAt: (r.updatedAt ?? r.updated_at ?? ""),
73
+ };
50
74
  },
51
75
  saveCredentialContext: async (input) => {
52
76
  const ctx = input;