failproofai 1.0.4-beta.6 → 1.0.4-beta.8
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +5 -5
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +15 -4
- package/.next/standalone/.next/server/app/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__0v1hz15._.js → [externals]__0hmcwgg._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1nb206a._.js → [externals]__14b7cc6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0faqz9x._.js +22 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p-qi2t._.js → [root-of-the-server]__112b4go._.js} +22 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1l71g3y._.js +22 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_0otft92._.js → _1_eakl4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +10 -10
- package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +3 -0
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-4ja6q._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cr3xu._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1sc84bb._.js → [root-of-the-server]__00y9ww2._.js} +21 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04px77e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egr5s1._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0i0l1di._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n2txhk._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0r3c1_0._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17xnxlx._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1fvfw59._.js → [root-of-the-server]__187jks6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ckpmkk._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ui5ur8._.js → [root-of-the-server]__1fa1jd9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kaotix._.js +22 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0dxf928._.js → [root-of-the-server]__1kpd1za._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_02b3i98._.js → _00bqkhj._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_07t3jol._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1if9ixi._.js → _14c6q09._.js} +21 -2
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +33 -22
- package/.next/standalone/.next/static/chunks/0igq884ziuv-8.js +1 -0
- package/.next/standalone/.next/static/chunks/{20xkiqzvfkuvm.js → 0vm4wdx3w-2_w.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1gn08w2pgqh09.js → 1302duobnz07l.js} +1 -1
- package/.next/standalone/.next/static/chunks/{07c6l1lq5_69b.js → 1_x7pqemw5n_w.js} +1 -1
- package/.next/standalone/.next/static/chunks/{32m2ryfankf_2.js → 1chm9piluggc-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{12bsgz8czvcnn.js → 2mizt96igig1y.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2a84ug32tzik2.js → 2n85g-crgcx5j.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3jxd133cw2vm3.js → 2ukucf6g-kbs5.js} +1 -1
- package/.next/standalone/.next/static/chunks/349jkb79v5g6z.js +1 -0
- package/.next/standalone/.next/static/chunks/{1xgq22tri86an.js → 3a4gth64hwtwf.js} +1 -1
- package/.next/standalone/app/actions/get-scheduled-audit.ts +5 -0
- package/.next/standalone/app/actions/update-scheduled-audit.ts +14 -1
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +1 -1
- package/.next/standalone/app/audit/_components/empty-state.tsx +1 -1
- package/.next/standalone/app/audit/_components/leak-section.tsx +1 -1
- package/.next/standalone/app/settings/settings-client.tsx +42 -11
- package/.next/standalone/lib/download-session.ts +58 -13
- package/.next/standalone/lib/openclaw-db.ts +271 -0
- package/.next/standalone/lib/openclaw-projects.ts +54 -18
- package/.next/standalone/lib/openclaw-sessions.ts +75 -17
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +511 -315
- package/dist/worker.mjs +287 -92
- package/lib/download-session.ts +58 -13
- package/lib/openclaw-db.ts +271 -0
- package/lib/openclaw-projects.ts +54 -18
- package/lib/openclaw-sessions.ts +75 -17
- package/package.json +9 -9
- package/src/audit/index.ts +1 -1
- package/src/audit/leak-record.ts +24 -12
- package/src/audit/macos-notifier.ts +54 -66
- package/src/hooks/builtin-policies.ts +2 -2
- package/src/hooks/configure-wizard.ts +8 -1
- package/src/hooks/uninstall-cli.ts +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +0 -3
- package/.next/standalone/.next/static/chunks/3ioub_lpen-sq.js +0 -1
- package/.next/standalone/.next/static/chunks/44ncu88eq6z71.js +0 -1
- /package/.next/standalone/.next/static/{aeM365dNTtUZOZvNkX8ba → DNhaAH8iPGH0tIuxee5_2}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{aeM365dNTtUZOZvNkX8ba → DNhaAH8iPGH0tIuxee5_2}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{aeM365dNTtUZOZvNkX8ba → DNhaAH8iPGH0tIuxee5_2}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read OpenClaw 2026.9.2+ transcripts from the per-agent SQLite databases at
|
|
3
|
+
* `agents/<agentId>/agent/openclaw-agent.sqlite`.
|
|
4
|
+
*
|
|
5
|
+
* `event_json` is the exact logical JSONL record, so the dashboard can feed it
|
|
6
|
+
* through the same parser and download format used by archived JSONL files.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { openSqliteReadonly, type SqliteReader } from "./sqlite-reader";
|
|
11
|
+
|
|
12
|
+
const DB_NAME = "openclaw-agent.sqlite";
|
|
13
|
+
|
|
14
|
+
export interface OpenClawSqliteSession {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
agentId: string;
|
|
17
|
+
dbPath: string;
|
|
18
|
+
transcriptPath: string;
|
|
19
|
+
mtimeMs: number;
|
|
20
|
+
sizeBytes: number;
|
|
21
|
+
channel: string;
|
|
22
|
+
label?: string;
|
|
23
|
+
chatId?: string;
|
|
24
|
+
chatType?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface OpenClawSqliteTranscript {
|
|
28
|
+
agentId: string;
|
|
29
|
+
dbPath: string;
|
|
30
|
+
filePath: string;
|
|
31
|
+
rawLines: Record<string, unknown>[];
|
|
32
|
+
eventJsonLines: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface WindowRow {
|
|
36
|
+
session_id: string;
|
|
37
|
+
session_key: string;
|
|
38
|
+
updated_at: number;
|
|
39
|
+
transcript_updated_at: number | null;
|
|
40
|
+
ended_at: number | null;
|
|
41
|
+
channel: string | null;
|
|
42
|
+
chat_type: string | null;
|
|
43
|
+
display_name: string | null;
|
|
44
|
+
event_updated_at: number;
|
|
45
|
+
size_bytes: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface NodeRow {
|
|
49
|
+
current_session_id: string;
|
|
50
|
+
entry_json: string;
|
|
51
|
+
label: string | null;
|
|
52
|
+
display_name: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ConversationRow {
|
|
56
|
+
session_id: string;
|
|
57
|
+
channel: string;
|
|
58
|
+
kind: string;
|
|
59
|
+
peer_id: string;
|
|
60
|
+
delivery_target: string;
|
|
61
|
+
label: string | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface EventRow {
|
|
65
|
+
event_json: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface SessionMeta {
|
|
69
|
+
channel?: string;
|
|
70
|
+
label?: string;
|
|
71
|
+
chatId?: string;
|
|
72
|
+
chatType?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function str(value: unknown): string | undefined {
|
|
76
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function parseObject(value: string): Record<string, unknown> | undefined {
|
|
80
|
+
try {
|
|
81
|
+
const parsed: unknown = JSON.parse(value);
|
|
82
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
83
|
+
? (parsed as Record<string, unknown>)
|
|
84
|
+
: undefined;
|
|
85
|
+
} catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function syntheticPath(agentId: string, sessionId: string): string {
|
|
91
|
+
return `openclaw-sqlite://${encodeURIComponent(agentId)}/${sessionId}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Existing per-agent SQLite databases, derived from the configured agents. */
|
|
95
|
+
export function listOpenClawDatabases(
|
|
96
|
+
home: string,
|
|
97
|
+
agentIds: string[],
|
|
98
|
+
): Array<{ agentId: string; dbPath: string }> {
|
|
99
|
+
const out: Array<{ agentId: string; dbPath: string }> = [];
|
|
100
|
+
for (const agentId of agentIds) {
|
|
101
|
+
const dbPath = join(home, "agents", agentId, "agent", DB_NAME);
|
|
102
|
+
if (existsSync(dbPath)) out.push({ agentId, dbPath });
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readNodeMetadata(db: SqliteReader): Map<string, SessionMeta> {
|
|
108
|
+
const out = new Map<string, SessionMeta>();
|
|
109
|
+
let rows: NodeRow[];
|
|
110
|
+
try {
|
|
111
|
+
rows = db.query<NodeRow>(
|
|
112
|
+
`SELECT current_session_id, entry_json, label, display_name
|
|
113
|
+
FROM session_nodes`,
|
|
114
|
+
);
|
|
115
|
+
} catch {
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
for (const row of rows) {
|
|
119
|
+
const entry = parseObject(row.entry_json) ?? {};
|
|
120
|
+
const origin =
|
|
121
|
+
entry.origin &&
|
|
122
|
+
typeof entry.origin === "object" &&
|
|
123
|
+
!Array.isArray(entry.origin)
|
|
124
|
+
? (entry.origin as Record<string, unknown>)
|
|
125
|
+
: {};
|
|
126
|
+
out.set(row.current_session_id, {
|
|
127
|
+
channel:
|
|
128
|
+
str(entry.lastChannel) ?? str(origin.provider) ?? str(origin.surface),
|
|
129
|
+
label:
|
|
130
|
+
str(row.label) ??
|
|
131
|
+
str(row.display_name) ??
|
|
132
|
+
str(entry.displayName) ??
|
|
133
|
+
str(origin.label),
|
|
134
|
+
chatId: str(entry.lastTo) ?? str(origin.from),
|
|
135
|
+
chatType: str(entry.chatType) ?? str(origin.chatType),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readConversationMetadata(db: SqliteReader): Map<string, SessionMeta> {
|
|
142
|
+
const out = new Map<string, SessionMeta>();
|
|
143
|
+
let rows: ConversationRow[];
|
|
144
|
+
try {
|
|
145
|
+
rows = db.query<ConversationRow>(
|
|
146
|
+
`SELECT sc.session_id, c.channel, c.kind, c.peer_id,
|
|
147
|
+
c.delivery_target, c.label
|
|
148
|
+
FROM session_conversations sc
|
|
149
|
+
JOIN conversations c ON c.conversation_id = sc.conversation_id
|
|
150
|
+
ORDER BY CASE sc.role WHEN 'primary' THEN 0 ELSE 1 END, sc.last_seen_at DESC`,
|
|
151
|
+
);
|
|
152
|
+
} catch {
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
for (const row of rows) {
|
|
156
|
+
if (out.has(row.session_id)) continue;
|
|
157
|
+
out.set(row.session_id, {
|
|
158
|
+
channel: str(row.channel),
|
|
159
|
+
label: str(row.label),
|
|
160
|
+
chatId: str(row.delivery_target) ?? str(row.peer_id),
|
|
161
|
+
chatType: str(row.kind),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** List every transcript-bearing session in all per-agent databases. */
|
|
168
|
+
export async function listOpenClawSqliteSessions(
|
|
169
|
+
home: string,
|
|
170
|
+
agentIds: string[],
|
|
171
|
+
): Promise<OpenClawSqliteSession[]> {
|
|
172
|
+
const sessions: OpenClawSqliteSession[] = [];
|
|
173
|
+
for (const { agentId, dbPath } of listOpenClawDatabases(home, agentIds)) {
|
|
174
|
+
const db = await openSqliteReadonly(dbPath);
|
|
175
|
+
if (!db) continue;
|
|
176
|
+
try {
|
|
177
|
+
let rows: WindowRow[];
|
|
178
|
+
try {
|
|
179
|
+
rows = db.query<WindowRow>(
|
|
180
|
+
`SELECT w.session_id, w.session_key, w.updated_at,
|
|
181
|
+
w.transcript_updated_at, w.ended_at, w.channel,
|
|
182
|
+
w.chat_type, w.display_name,
|
|
183
|
+
COALESCE((SELECT MAX(e.created_at)
|
|
184
|
+
FROM transcript_events e
|
|
185
|
+
WHERE e.session_id = w.session_id), 0) AS event_updated_at,
|
|
186
|
+
COALESCE((SELECT SUM(length(e.event_json) + 1)
|
|
187
|
+
FROM transcript_events e
|
|
188
|
+
WHERE e.session_id = w.session_id), 0) AS size_bytes
|
|
189
|
+
FROM session_windows w
|
|
190
|
+
WHERE EXISTS (SELECT 1 FROM transcript_events e
|
|
191
|
+
WHERE e.session_id = w.session_id)`,
|
|
192
|
+
);
|
|
193
|
+
} catch {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const nodes = readNodeMetadata(db);
|
|
198
|
+
const conversations = readConversationMetadata(db);
|
|
199
|
+
for (const row of rows) {
|
|
200
|
+
const node = nodes.get(row.session_id);
|
|
201
|
+
const conversation = conversations.get(row.session_id);
|
|
202
|
+
sessions.push({
|
|
203
|
+
sessionId: row.session_id,
|
|
204
|
+
agentId,
|
|
205
|
+
dbPath,
|
|
206
|
+
transcriptPath: syntheticPath(agentId, row.session_id),
|
|
207
|
+
mtimeMs: Math.max(
|
|
208
|
+
Number(row.updated_at) || 0,
|
|
209
|
+
Number(row.transcript_updated_at) || 0,
|
|
210
|
+
Number(row.ended_at) || 0,
|
|
211
|
+
Number(row.event_updated_at) || 0,
|
|
212
|
+
),
|
|
213
|
+
sizeBytes: Number(row.size_bytes) || 0,
|
|
214
|
+
channel:
|
|
215
|
+
str(row.channel) ??
|
|
216
|
+
conversation?.channel ??
|
|
217
|
+
node?.channel ??
|
|
218
|
+
"local",
|
|
219
|
+
label: str(row.display_name) ?? conversation?.label ?? node?.label,
|
|
220
|
+
chatId: conversation?.chatId ?? node?.chatId,
|
|
221
|
+
chatType:
|
|
222
|
+
str(row.chat_type) ?? conversation?.chatType ?? node?.chatType,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
} finally {
|
|
226
|
+
db.close();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return sessions;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Read one SQLite transcript by UUID. The first matching agent wins. */
|
|
233
|
+
export async function readOpenClawSqliteTranscript(
|
|
234
|
+
home: string,
|
|
235
|
+
agentIds: string[],
|
|
236
|
+
sessionId: string,
|
|
237
|
+
): Promise<OpenClawSqliteTranscript | null> {
|
|
238
|
+
for (const { agentId, dbPath } of listOpenClawDatabases(home, agentIds)) {
|
|
239
|
+
const db = await openSqliteReadonly(dbPath);
|
|
240
|
+
if (!db) continue;
|
|
241
|
+
try {
|
|
242
|
+
let rows: EventRow[];
|
|
243
|
+
try {
|
|
244
|
+
rows = db.query<EventRow>(
|
|
245
|
+
`SELECT event_json
|
|
246
|
+
FROM transcript_events
|
|
247
|
+
WHERE session_id = ?
|
|
248
|
+
ORDER BY seq ASC`,
|
|
249
|
+
[sessionId],
|
|
250
|
+
);
|
|
251
|
+
} catch {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (rows.length === 0) continue;
|
|
255
|
+
const eventJsonLines = rows.map((row) => row.event_json);
|
|
256
|
+
const rawLines = eventJsonLines
|
|
257
|
+
.map(parseObject)
|
|
258
|
+
.filter((line): line is Record<string, unknown> => line !== undefined);
|
|
259
|
+
return {
|
|
260
|
+
agentId,
|
|
261
|
+
dbPath,
|
|
262
|
+
filePath: syntheticPath(agentId, sessionId),
|
|
263
|
+
rawLines,
|
|
264
|
+
eventJsonLines,
|
|
265
|
+
};
|
|
266
|
+
} finally {
|
|
267
|
+
db.close();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenClaw (openclaw gateway) session enumeration — AUDIT-ONLY.
|
|
3
3
|
*
|
|
4
|
-
* Surfaces
|
|
5
|
-
* synthetic dashboard "projects" grouped by
|
|
6
|
-
*
|
|
7
|
-
* recover the sessionKey (which encodes the channel for gateway sessions) and a
|
|
8
|
-
* reliable last-activity time. Verified live against openclaw v2026.7.1.
|
|
4
|
+
* Surfaces both legacy JSONL transcripts and OpenClaw 2026.9.2+ per-agent
|
|
5
|
+
* SQLite transcripts as synthetic dashboard "projects" grouped by
|
|
6
|
+
* (agentId, channel).
|
|
9
7
|
*/
|
|
10
8
|
import { readFileSync } from "node:fs";
|
|
11
9
|
import { join } from "node:path";
|
|
12
10
|
import { runtimeCache } from "./runtime-cache";
|
|
13
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
listOpenClawAgents,
|
|
13
|
+
listOpenClawTranscripts,
|
|
14
|
+
openclawHome,
|
|
15
|
+
} from "./openclaw-sessions";
|
|
14
16
|
import type { ProjectFolder, SessionFile } from "./projects";
|
|
15
17
|
import { formatDate } from "./format-date";
|
|
18
|
+
import { listOpenClawSqliteSessions } from "./openclaw-db";
|
|
16
19
|
|
|
17
20
|
export interface OpenClawSessionRef {
|
|
18
21
|
sessionId: string;
|
|
@@ -51,7 +54,13 @@ function str(v: unknown): string | undefined {
|
|
|
51
54
|
* than in the key — verified live against v2026.7.1. */
|
|
52
55
|
function readSessionsIndex(agentId: string): Map<string, SessionIndexMeta> {
|
|
53
56
|
const out = new Map<string, SessionIndexMeta>();
|
|
54
|
-
const indexPath = join(
|
|
57
|
+
const indexPath = join(
|
|
58
|
+
openclawHome(),
|
|
59
|
+
"agents",
|
|
60
|
+
agentId,
|
|
61
|
+
"sessions",
|
|
62
|
+
"sessions.json",
|
|
63
|
+
);
|
|
55
64
|
let raw: unknown;
|
|
56
65
|
try {
|
|
57
66
|
raw = JSON.parse(readFileSync(indexPath, "utf-8"));
|
|
@@ -64,7 +73,9 @@ function readSessionsIndex(agentId: string): Map<string, SessionIndexMeta> {
|
|
|
64
73
|
const e = v as Record<string, unknown>;
|
|
65
74
|
const sessionId = str(e.sessionId);
|
|
66
75
|
if (!sessionId) continue;
|
|
67
|
-
const origin = (
|
|
76
|
+
const origin = (
|
|
77
|
+
e.origin && typeof e.origin === "object" ? e.origin : {}
|
|
78
|
+
) as Record<string, unknown>;
|
|
68
79
|
const lastMs =
|
|
69
80
|
typeof e.lastInteractionAt === "number"
|
|
70
81
|
? e.lastInteractionAt
|
|
@@ -73,7 +84,8 @@ function readSessionsIndex(agentId: string): Map<string, SessionIndexMeta> {
|
|
|
73
84
|
: undefined;
|
|
74
85
|
out.set(sessionId, {
|
|
75
86
|
lastMs,
|
|
76
|
-
channel:
|
|
87
|
+
channel:
|
|
88
|
+
str(e.lastChannel) ?? str(origin.provider) ?? str(origin.surface),
|
|
77
89
|
chatType: str(e.chatType) ?? str(origin.chatType),
|
|
78
90
|
label: str(origin.label),
|
|
79
91
|
chatId: str(e.lastTo) ?? str(origin.from),
|
|
@@ -86,7 +98,7 @@ function readSessionsIndex(agentId: string): Map<string, SessionIndexMeta> {
|
|
|
86
98
|
export async function getOpenClawSessions(): Promise<OpenClawSessionRef[]> {
|
|
87
99
|
const transcripts = listOpenClawTranscripts();
|
|
88
100
|
const indexByAgent = new Map<string, Map<string, SessionIndexMeta>>();
|
|
89
|
-
const refs
|
|
101
|
+
const refs = new Map<string, OpenClawSessionRef>();
|
|
90
102
|
for (const t of transcripts) {
|
|
91
103
|
let idx = indexByAgent.get(t.agentId);
|
|
92
104
|
if (!idx) {
|
|
@@ -94,7 +106,7 @@ export async function getOpenClawSessions(): Promise<OpenClawSessionRef[]> {
|
|
|
94
106
|
indexByAgent.set(t.agentId, idx);
|
|
95
107
|
}
|
|
96
108
|
const meta = idx.get(t.sessionId);
|
|
97
|
-
refs.
|
|
109
|
+
refs.set(`${t.agentId}\0${t.sessionId}`, {
|
|
98
110
|
sessionId: t.sessionId,
|
|
99
111
|
agentId: t.agentId,
|
|
100
112
|
// Gateway sessions group by channel; CLI/local runs have none.
|
|
@@ -107,8 +119,18 @@ export async function getOpenClawSessions(): Promise<OpenClawSessionRef[]> {
|
|
|
107
119
|
sizeBytes: t.sizeBytes,
|
|
108
120
|
});
|
|
109
121
|
}
|
|
110
|
-
|
|
111
|
-
|
|
122
|
+
|
|
123
|
+
// SQLite is the live source on OpenClaw 2026.9.2+. Insert it second so it
|
|
124
|
+
// replaces an archived JSONL copy of the same agent/session.
|
|
125
|
+
const sqliteSessions = await listOpenClawSqliteSessions(
|
|
126
|
+
openclawHome(),
|
|
127
|
+
listOpenClawAgents(),
|
|
128
|
+
);
|
|
129
|
+
for (const s of sqliteSessions) {
|
|
130
|
+
refs.set(`${s.agentId}\0${s.sessionId}`, s);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [...refs.values()].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
112
134
|
}
|
|
113
135
|
|
|
114
136
|
export const getCachedOpenClawSessions = runtimeCache(getOpenClawSessions, 2);
|
|
@@ -145,7 +167,9 @@ export interface OpenClawNameSplit {
|
|
|
145
167
|
* form (`agentId: null`), so links shared before agents became part of the name
|
|
146
168
|
* keep resolving — to every agent on that channel, which is what they meant.
|
|
147
169
|
*/
|
|
148
|
-
export function openClawProjectNameCandidates(
|
|
170
|
+
export function openClawProjectNameCandidates(
|
|
171
|
+
name: string,
|
|
172
|
+
): OpenClawNameSplit[] {
|
|
149
173
|
if (!name.startsWith("openclaw-")) return [];
|
|
150
174
|
const rest = name.slice("openclaw-".length);
|
|
151
175
|
if (!rest) return [];
|
|
@@ -163,7 +187,9 @@ export function openClawProjectNameCandidates(name: string): OpenClawNameSplit[]
|
|
|
163
187
|
}
|
|
164
188
|
|
|
165
189
|
/** The best-guess split for a project name — `null` if it isn't an OpenClaw one. */
|
|
166
|
-
export function parseOpenClawProjectName(
|
|
190
|
+
export function parseOpenClawProjectName(
|
|
191
|
+
name: string,
|
|
192
|
+
): OpenClawNameSplit | null {
|
|
167
193
|
return openClawProjectNameCandidates(name)[0] ?? null;
|
|
168
194
|
}
|
|
169
195
|
|
|
@@ -190,7 +216,12 @@ export async function getOpenClawProjects(): Promise<ProjectFolder[]> {
|
|
|
190
216
|
prev.latest = Math.max(prev.latest, s.mtimeMs);
|
|
191
217
|
prev.count += 1;
|
|
192
218
|
} else {
|
|
193
|
-
groups.set(key, {
|
|
219
|
+
groups.set(key, {
|
|
220
|
+
agentId: s.agentId,
|
|
221
|
+
channel: s.channel,
|
|
222
|
+
latest: s.mtimeMs,
|
|
223
|
+
count: 1,
|
|
224
|
+
});
|
|
194
225
|
}
|
|
195
226
|
}
|
|
196
227
|
const out: ProjectFolder[] = [];
|
|
@@ -228,7 +259,9 @@ export async function getOpenClawSessionsByEncodedName(
|
|
|
228
259
|
const sessions = await getOpenClawSessions();
|
|
229
260
|
const matching = (split: OpenClawNameSplit) =>
|
|
230
261
|
sessions.filter(
|
|
231
|
-
(s) =>
|
|
262
|
+
(s) =>
|
|
263
|
+
s.channel === split.channel &&
|
|
264
|
+
(split.agentId === null || s.agentId === split.agentId),
|
|
232
265
|
);
|
|
233
266
|
|
|
234
267
|
// Take the first split that actually owns sessions — length alone picks the
|
|
@@ -250,7 +283,10 @@ export async function getOpenClawSessionsByEncodedName(
|
|
|
250
283
|
return {
|
|
251
284
|
// Legacy channel-only names keep their old cwd label, since they really do
|
|
252
285
|
// span every agent on that channel.
|
|
253
|
-
cwd:
|
|
286
|
+
cwd:
|
|
287
|
+
agentId === null
|
|
288
|
+
? `openclaw:${channel}`
|
|
289
|
+
: openClawProjectPath(agentId, channel),
|
|
254
290
|
sessions: matched.map((s) => {
|
|
255
291
|
const lastModified = new Date(s.mtimeMs);
|
|
256
292
|
return {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenClaw (openclaw gateway) session transcript loader + parser.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* IGNORE, and a `sessions.json` index keyed by sessionKey. Verified live
|
|
8
|
-
* against openclaw v2026.7.1.
|
|
4
|
+
* Legacy OpenClaw writes one JSONL transcript per session under `sessions/`;
|
|
5
|
+
* 2026.9.2+ stores the same logical records as `event_json` rows in each
|
|
6
|
+
* agent's `agent/openclaw-agent.sqlite`. SQLite is preferred when both exist.
|
|
9
7
|
*
|
|
10
8
|
* The transcript is type-discriminated JSONL:
|
|
11
9
|
* {type:"session", cwd, …} — header (carries cwd)
|
|
@@ -18,8 +16,8 @@
|
|
|
18
16
|
* `toolResult` by `toolCallId` (mirrors lib/hermes-sessions.ts) and is PURE, so
|
|
19
17
|
* it is unit-testable with plain line objects.
|
|
20
18
|
*
|
|
21
|
-
* Home override:
|
|
22
|
-
* gateway config dir).
|
|
19
|
+
* Home override: `OPENCLAW_STATE_DIR`, then `OPENCLAW_HOME` (used by tests /
|
|
20
|
+
* to point at a copied gateway config dir).
|
|
23
21
|
*/
|
|
24
22
|
import { readFile } from "node:fs/promises";
|
|
25
23
|
import { readdirSync, statSync } from "node:fs";
|
|
@@ -39,13 +37,18 @@ import {
|
|
|
39
37
|
type LogSource,
|
|
40
38
|
} from "./log-entries";
|
|
41
39
|
import { formatDuration } from "./format-duration";
|
|
40
|
+
import { readOpenClawSqliteTranscript } from "./openclaw-db";
|
|
42
41
|
|
|
43
42
|
/** OpenClaw sessions are stored under UUID filenames. */
|
|
44
43
|
export const OPENCLAW_SESSION_ID_RE = /^[0-9a-fA-F-]{36}$/;
|
|
45
44
|
|
|
46
|
-
/** Absolute path to OpenClaw's
|
|
45
|
+
/** Absolute path to OpenClaw's state home. */
|
|
47
46
|
export function openclawHome(): string {
|
|
48
|
-
return
|
|
47
|
+
return (
|
|
48
|
+
process.env.OPENCLAW_STATE_DIR ||
|
|
49
|
+
process.env.OPENCLAW_HOME ||
|
|
50
|
+
join(homedir(), ".openclaw")
|
|
51
|
+
);
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
// ── Parsing helpers ──
|
|
@@ -60,7 +63,11 @@ function extractText(content: unknown): string {
|
|
|
60
63
|
if (typeof content === "string") return content;
|
|
61
64
|
if (Array.isArray(content)) {
|
|
62
65
|
return content
|
|
63
|
-
.map((c) =>
|
|
66
|
+
.map((c) =>
|
|
67
|
+
isPlainObject(c) && typeof c.text === "string"
|
|
68
|
+
? (c.text as string)
|
|
69
|
+
: "",
|
|
70
|
+
)
|
|
64
71
|
.filter(Boolean)
|
|
65
72
|
.join("\n");
|
|
66
73
|
}
|
|
@@ -134,7 +141,10 @@ export function openclawLinesToLogEntries(
|
|
|
134
141
|
if (b.type === "text" && typeof b.text === "string") {
|
|
135
142
|
blocks.push({ type: "text", text: b.text });
|
|
136
143
|
} else if (b.type === "toolCall") {
|
|
137
|
-
const id =
|
|
144
|
+
const id =
|
|
145
|
+
typeof b.id === "string"
|
|
146
|
+
? b.id
|
|
147
|
+
: `${String(b.name ?? "tool")}-${blocks.length}`;
|
|
138
148
|
const name = typeof b.name === "string" ? b.name : "tool";
|
|
139
149
|
const input = isPlainObject(b.arguments) ? b.arguments : {};
|
|
140
150
|
const block: ToolUseBlock = { type: "tool_use", id, name, input };
|
|
@@ -151,17 +161,23 @@ export function openclawLinesToLogEntries(
|
|
|
151
161
|
entries.push({
|
|
152
162
|
type: "assistant",
|
|
153
163
|
...base,
|
|
154
|
-
message: {
|
|
164
|
+
message: {
|
|
165
|
+
role: "assistant",
|
|
166
|
+
content: blocks,
|
|
167
|
+
model: typeof m.model === "string" ? m.model : undefined,
|
|
168
|
+
},
|
|
155
169
|
} satisfies AssistantEntry);
|
|
156
170
|
continue;
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
if (role === "toolResult") {
|
|
160
|
-
const callId =
|
|
174
|
+
const callId =
|
|
175
|
+
typeof m.toolCallId === "string" ? m.toolCallId : undefined;
|
|
161
176
|
const block = callId ? toolUseById.get(callId) : undefined;
|
|
162
177
|
if (block) {
|
|
163
178
|
const details = isPlainObject(m.details) ? m.details : undefined;
|
|
164
|
-
const startMs =
|
|
179
|
+
const startMs =
|
|
180
|
+
(callId && toolUseStartMs.get(callId)) || date.getTime();
|
|
165
181
|
const durationMs =
|
|
166
182
|
details && typeof details.durationMs === "number"
|
|
167
183
|
? details.durationMs
|
|
@@ -238,13 +254,20 @@ export function listOpenClawTranscripts(): OpenClawTranscriptFile[] {
|
|
|
238
254
|
}
|
|
239
255
|
for (const file of files) {
|
|
240
256
|
// Only `<uuid>.jsonl` — not `<uuid>.trajectory.jsonl` / `.trajectory-path.json`.
|
|
241
|
-
if (!file.endsWith(".jsonl") || file.endsWith(".trajectory.jsonl"))
|
|
257
|
+
if (!file.endsWith(".jsonl") || file.endsWith(".trajectory.jsonl"))
|
|
258
|
+
continue;
|
|
242
259
|
const sessionId = file.slice(0, -".jsonl".length);
|
|
243
260
|
if (!OPENCLAW_SESSION_ID_RE.test(sessionId)) continue;
|
|
244
261
|
const transcriptPath = join(sessionsDir, file);
|
|
245
262
|
try {
|
|
246
263
|
const st = statSync(transcriptPath);
|
|
247
|
-
out.push({
|
|
264
|
+
out.push({
|
|
265
|
+
agentId,
|
|
266
|
+
sessionId,
|
|
267
|
+
transcriptPath,
|
|
268
|
+
mtimeMs: st.mtimeMs,
|
|
269
|
+
sizeBytes: st.size,
|
|
270
|
+
});
|
|
248
271
|
} catch {
|
|
249
272
|
// skip unreadable
|
|
250
273
|
}
|
|
@@ -276,6 +299,36 @@ export interface OpenClawSessionLogData {
|
|
|
276
299
|
export async function getOpenClawSessionLog(
|
|
277
300
|
sessionId: string,
|
|
278
301
|
): Promise<OpenClawSessionLogData | null> {
|
|
302
|
+
if (!OPENCLAW_SESSION_ID_RE.test(sessionId)) return null;
|
|
303
|
+
|
|
304
|
+
// New OpenClaw versions keep live transcripts in SQLite. Prefer that copy
|
|
305
|
+
// when an archived JSONL with the same UUID also exists.
|
|
306
|
+
const sqlite = await readOpenClawSqliteTranscript(
|
|
307
|
+
openclawHome(),
|
|
308
|
+
listOpenClawAgents(),
|
|
309
|
+
sessionId,
|
|
310
|
+
);
|
|
311
|
+
if (sqlite) {
|
|
312
|
+
const entries = openclawLinesToLogEntries(sqlite.rawLines, "session");
|
|
313
|
+
let cwd: string | undefined;
|
|
314
|
+
for (const line of sqlite.rawLines) {
|
|
315
|
+
if (
|
|
316
|
+
line.type === "session" &&
|
|
317
|
+
typeof line.cwd === "string" &&
|
|
318
|
+
line.cwd.length > 0
|
|
319
|
+
) {
|
|
320
|
+
cwd = line.cwd;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
entries,
|
|
326
|
+
rawLines: sqlite.rawLines,
|
|
327
|
+
cwd,
|
|
328
|
+
filePath: sqlite.filePath,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
279
332
|
const filePath = findOpenClawTranscript(sessionId);
|
|
280
333
|
if (!filePath) return null;
|
|
281
334
|
let content: string;
|
|
@@ -289,7 +342,12 @@ export async function getOpenClawSessionLog(
|
|
|
289
342
|
// cwd lives on the `type:"session"` header line.
|
|
290
343
|
let cwd: string | undefined;
|
|
291
344
|
for (const line of rawLines) {
|
|
292
|
-
if (
|
|
345
|
+
if (
|
|
346
|
+
isPlainObject(line) &&
|
|
347
|
+
line.type === "session" &&
|
|
348
|
+
typeof line.cwd === "string" &&
|
|
349
|
+
line.cwd.length > 0
|
|
350
|
+
) {
|
|
293
351
|
cwd = line.cwd;
|
|
294
352
|
break;
|
|
295
353
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "1.0.4-beta.
|
|
3
|
+
"version": "1.0.4-beta.8",
|
|
4
4
|
"description": "Observability and enforcement for AI agent harnesses. 39 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs",
|
|
@@ -121,15 +121,15 @@
|
|
|
121
121
|
"browserslist": "4.28.8"
|
|
122
122
|
},
|
|
123
123
|
"optionalDependencies": {
|
|
124
|
-
"@failproofai/failproofaid-linux-x64": "1.0.4-beta.
|
|
125
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.4-beta.
|
|
126
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.4-beta.
|
|
127
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.4-beta.
|
|
124
|
+
"@failproofai/failproofaid-linux-x64": "1.0.4-beta.8",
|
|
125
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.4-beta.8",
|
|
126
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.4-beta.8",
|
|
127
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.4-beta.8"
|
|
128
128
|
},
|
|
129
129
|
"failproofaidBinaries": {
|
|
130
|
-
"linux-x64": "
|
|
131
|
-
"linux-arm64": "
|
|
132
|
-
"darwin-x64": "
|
|
133
|
-
"darwin-arm64": "
|
|
130
|
+
"linux-x64": "17274c1da30675b49de74953a4ce2777e09ba07b9da79250ab155e5fdb316a0a",
|
|
131
|
+
"linux-arm64": "a07b228d899914baca1c75024be19c0319eb4fc40c6d5a303ba733bf6721924a",
|
|
132
|
+
"darwin-x64": "d170fd5604cb419bc83f4a5ab1a6cb5be2f9aec957aed99ce237990aa5d3966f",
|
|
133
|
+
"darwin-arm64": "0e2bde5bd0762641fa072a6640e485c637b2102f2fb6c1afeca679f005e89679"
|
|
134
134
|
}
|
|
135
135
|
}
|