@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 +11 -15
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/runtime/cli/action-bridge.js +11 -2
- package/runtime/cli/read-models/index.js +2 -2
- package/runtime/observability/db/index.d.ts +3 -3
- package/runtime/observability/db/index.js +31 -6
- package/runtime/storage/db/index.d.ts +3 -3
- package/runtime/storage/db/index.js +31 -6
- package/runtime/storage/services/repair-and-backup.js +2 -1
- package/runtime/storage/state-api.js +26 -2
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 =
|
|
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 =
|
|
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.
|
|
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 =
|
|
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 }) => {
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haaaiawd/second-nature",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
|
|
24
|
+
platformId: existing.platformId ?? existing.platform_id,
|
|
25
|
+
credentialType: credentialType ?? "",
|
|
26
|
+
encryptedValue: encryptedValue ?? "",
|
|
20
27
|
status: "active",
|
|
21
28
|
verificationCode: answer,
|
|
22
|
-
|
|
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 "
|
|
2
|
-
import { drizzle } from "drizzle-orm/
|
|
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
|
|
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
|
|
2
|
-
import { drizzle } from "drizzle-orm/
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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 "
|
|
2
|
-
import { drizzle } from "drizzle-orm/
|
|
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
|
|
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
|
|
2
|
-
import { drizzle } from "drizzle-orm/
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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;
|