@stackbilt/aegis-core 0.1.0
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/package.json +96 -0
- package/schema.sql +586 -0
- package/src/adapters/voice/cloudflare-agent.ts +34 -0
- package/src/auth.ts +124 -0
- package/src/bluesky.ts +464 -0
- package/src/claude-tools/content.ts +188 -0
- package/src/claude-tools/email.ts +69 -0
- package/src/claude-tools/github.ts +440 -0
- package/src/claude-tools/goals.ts +116 -0
- package/src/claude-tools/index.ts +353 -0
- package/src/claude-tools/web.ts +59 -0
- package/src/claude.ts +406 -0
- package/src/codebeast.ts +200 -0
- package/src/composite.ts +715 -0
- package/src/content/column.ts +80 -0
- package/src/content/hero-image.ts +47 -0
- package/src/content/index.ts +27 -0
- package/src/content/journal.ts +91 -0
- package/src/content/roundtable.ts +163 -0
- package/src/core.ts +309 -0
- package/src/dashboard.ts +620 -0
- package/src/decision-docs.ts +284 -0
- package/src/dispatch.ts +13 -0
- package/src/edge-env.ts +58 -0
- package/src/email.ts +850 -0
- package/src/exports.ts +156 -0
- package/src/github-projects.ts +312 -0
- package/src/github.ts +670 -0
- package/src/groq.ts +247 -0
- package/src/health-page.ts +578 -0
- package/src/index.ts +89 -0
- package/src/kernel/argus-actions.ts +397 -0
- package/src/kernel/argus-correlation.ts +639 -0
- package/src/kernel/board.ts +91 -0
- package/src/kernel/briefing.ts +177 -0
- package/src/kernel/classify-memory-topic.ts +166 -0
- package/src/kernel/cognition.ts +377 -0
- package/src/kernel/court-cards.ts +163 -0
- package/src/kernel/dispatch.ts +587 -0
- package/src/kernel/domain.ts +50 -0
- package/src/kernel/dynamic-tools.ts +322 -0
- package/src/kernel/executor-port.ts +45 -0
- package/src/kernel/executors/claude.ts +73 -0
- package/src/kernel/executors/direct.ts +237 -0
- package/src/kernel/executors/groq.ts +18 -0
- package/src/kernel/executors/index.ts +87 -0
- package/src/kernel/executors/tarotscript.ts +104 -0
- package/src/kernel/executors/workers-ai.ts +54 -0
- package/src/kernel/insight-cache.ts +76 -0
- package/src/kernel/memory/agenda.ts +200 -0
- package/src/kernel/memory/blocks.ts +188 -0
- package/src/kernel/memory/consolidation.ts +194 -0
- package/src/kernel/memory/episodic.ts +241 -0
- package/src/kernel/memory/goals.ts +156 -0
- package/src/kernel/memory/graph.ts +290 -0
- package/src/kernel/memory/index.ts +11 -0
- package/src/kernel/memory/insights.ts +316 -0
- package/src/kernel/memory/procedural.ts +467 -0
- package/src/kernel/memory/pruning.ts +67 -0
- package/src/kernel/memory/recall.ts +367 -0
- package/src/kernel/memory/semantic.ts +315 -0
- package/src/kernel/memory/synthesis.ts +161 -0
- package/src/kernel/memory-adapter.ts +369 -0
- package/src/kernel/memory-guardrails.ts +76 -0
- package/src/kernel/port.ts +23 -0
- package/src/kernel/resilience.ts +322 -0
- package/src/kernel/router.ts +471 -0
- package/src/kernel/scheduled/agent-dispatch.ts +252 -0
- package/src/kernel/scheduled/argus-analytics.ts +247 -0
- package/src/kernel/scheduled/argus-heartbeat.ts +320 -0
- package/src/kernel/scheduled/argus-notify.ts +348 -0
- package/src/kernel/scheduled/board-sync.ts +110 -0
- package/src/kernel/scheduled/ci-watcher.ts +125 -0
- package/src/kernel/scheduled/cognitive-metrics.ts +377 -0
- package/src/kernel/scheduled/consolidation.ts +229 -0
- package/src/kernel/scheduled/content-drip.ts +47 -0
- package/src/kernel/scheduled/content.ts +6 -0
- package/src/kernel/scheduled/conversation-facts.ts +204 -0
- package/src/kernel/scheduled/cost-report.ts +84 -0
- package/src/kernel/scheduled/curiosity.ts +219 -0
- package/src/kernel/scheduled/dev-activity.ts +44 -0
- package/src/kernel/scheduled/digest.ts +317 -0
- package/src/kernel/scheduled/dreaming/agenda-triage.ts +115 -0
- package/src/kernel/scheduled/dreaming/facts.ts +239 -0
- package/src/kernel/scheduled/dreaming/index.ts +8 -0
- package/src/kernel/scheduled/dreaming/llm.ts +33 -0
- package/src/kernel/scheduled/dreaming/pattern-synthesis.ts +124 -0
- package/src/kernel/scheduled/dreaming/persona.ts +75 -0
- package/src/kernel/scheduled/dreaming/symbolic.ts +31 -0
- package/src/kernel/scheduled/dreaming/task-proposals.ts +80 -0
- package/src/kernel/scheduled/dreaming.ts +66 -0
- package/src/kernel/scheduled/entropy.ts +149 -0
- package/src/kernel/scheduled/escalation.ts +192 -0
- package/src/kernel/scheduled/feed-watcher.ts +206 -0
- package/src/kernel/scheduled/goals.ts +214 -0
- package/src/kernel/scheduled/governance.ts +41 -0
- package/src/kernel/scheduled/heartbeat.ts +220 -0
- package/src/kernel/scheduled/inbox-processor.ts +174 -0
- package/src/kernel/scheduled/index.ts +245 -0
- package/src/kernel/scheduled/issue-proposer.ts +478 -0
- package/src/kernel/scheduled/issue-watcher.ts +128 -0
- package/src/kernel/scheduled/pr-automerge.ts +213 -0
- package/src/kernel/scheduled/product-health.ts +107 -0
- package/src/kernel/scheduled/reflection.ts +373 -0
- package/src/kernel/scheduled/self-improvement.ts +114 -0
- package/src/kernel/scheduled/social-engage.ts +175 -0
- package/src/kernel/scheduled/task-audit.ts +60 -0
- package/src/kernel/symbolic.ts +156 -0
- package/src/kernel/types.ts +145 -0
- package/src/landing.ts +1190 -0
- package/src/lib/audit-chain/chain.ts +28 -0
- package/src/lib/audit-chain/types.ts +12 -0
- package/src/lib/observability/errors.ts +55 -0
- package/src/markdown.ts +164 -0
- package/src/mcp/handlers.ts +647 -0
- package/src/mcp/server.ts +184 -0
- package/src/mcp/tools.ts +316 -0
- package/src/mcp-client.ts +275 -0
- package/src/mcp-server.ts +2 -0
- package/src/operator/config.example.ts +60 -0
- package/src/operator/config.ts +60 -0
- package/src/operator/index.ts +46 -0
- package/src/operator/persona.example.ts +34 -0
- package/src/operator/persona.ts +34 -0
- package/src/operator/prompt-builder.ts +190 -0
- package/src/operator/types.ts +43 -0
- package/src/pulse.ts +1179 -0
- package/src/routes/bluesky.ts +116 -0
- package/src/routes/cc-tasks.ts +328 -0
- package/src/routes/codebeast.ts +1 -0
- package/src/routes/content.ts +194 -0
- package/src/routes/conversations.ts +25 -0
- package/src/routes/dynamic-tools.ts +111 -0
- package/src/routes/feedback.ts +192 -0
- package/src/routes/health.ts +147 -0
- package/src/routes/messages.ts +228 -0
- package/src/routes/observability.ts +82 -0
- package/src/routes/operator-logs.ts +42 -0
- package/src/routes/pages.ts +96 -0
- package/src/routes/sessions.ts +54 -0
- package/src/sanitize.ts +73 -0
- package/src/schema-enums.ts +155 -0
- package/src/search.ts +112 -0
- package/src/task-intelligence.ts +497 -0
- package/src/types.ts +194 -0
- package/src/ui.ts +5 -0
- package/src/version.ts +3 -0
- package/src/workers-ai-chat.ts +333 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// Stub — full implementation not yet extracted to OSS
|
|
2
|
+
|
|
3
|
+
import { recordMemory } from './index.js';
|
|
4
|
+
|
|
5
|
+
export type InsightType = 'pattern' | 'heuristic' | 'procedure' | 'principle';
|
|
6
|
+
|
|
7
|
+
export interface InsightPayload {
|
|
8
|
+
fact: string;
|
|
9
|
+
insight_type: InsightType;
|
|
10
|
+
origin_repo: string;
|
|
11
|
+
keywords: string[];
|
|
12
|
+
confidence: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ─── Context-specific detection ─────────────────────────────
|
|
16
|
+
|
|
17
|
+
const CONTEXT_PATTERNS = [
|
|
18
|
+
/\/home\/|\/Users\/|\/tmp\/|[A-Z]:\\/i, // hardcoded paths
|
|
19
|
+
/\$[A-Z_]+/, // env vars like $CLOUDFLARE_API_TOKEN
|
|
20
|
+
/process\.env\./, // process.env references
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const GENERAL_PATTERN_SIGNALS = [
|
|
24
|
+
/always/i, /never/i, /when using/i, /to prevent/i, /best practice/i,
|
|
25
|
+
/validate.*before/i, /ensure/i,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function isContextSpecific(fact: string): boolean {
|
|
29
|
+
const hasContextMarker = CONTEXT_PATTERNS.some(p => p.test(fact));
|
|
30
|
+
if (!hasContextMarker) {
|
|
31
|
+
// Check for single-file references without general pattern language
|
|
32
|
+
const fileRef = /\b\w+\.\w{1,4}\b/.test(fact) && /line \d+|off-by-one|bug found in/i.test(fact);
|
|
33
|
+
if (fileRef) {
|
|
34
|
+
const hasGeneralLanguage = GENERAL_PATTERN_SIGNALS.some(p => p.test(fact));
|
|
35
|
+
return !hasGeneralLanguage;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function hashFact(fact: string): string {
|
|
43
|
+
let hash = 0;
|
|
44
|
+
for (let i = 0; i < fact.length; i++) {
|
|
45
|
+
const char = fact.charCodeAt(i);
|
|
46
|
+
hash = ((hash << 5) - hash) + char;
|
|
47
|
+
hash |= 0;
|
|
48
|
+
}
|
|
49
|
+
return Math.abs(hash).toString(36);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── publishInsight ─────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
export async function publishInsight(
|
|
55
|
+
db: D1Database,
|
|
56
|
+
payload: InsightPayload,
|
|
57
|
+
memBinding?: any,
|
|
58
|
+
): Promise<{ published: boolean; reason?: string }> {
|
|
59
|
+
if (payload.confidence < 0.75) {
|
|
60
|
+
return { published: false, reason: 'confidence too low (min 0.75)' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (isContextSpecific(payload.fact)) {
|
|
64
|
+
return { published: false, reason: 'context-specific insight rejected' };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const factHash = hashFact(payload.fact);
|
|
68
|
+
|
|
69
|
+
// Check for duplicates
|
|
70
|
+
const existing = await db.prepare(
|
|
71
|
+
'SELECT id FROM memory WHERE fact_hash = ?'
|
|
72
|
+
).bind(factHash).first();
|
|
73
|
+
|
|
74
|
+
if (existing) {
|
|
75
|
+
return { published: false, reason: 'duplicate insight (hash match)' };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (memBinding) {
|
|
79
|
+
await memBinding.store('default', [{
|
|
80
|
+
fact: payload.fact,
|
|
81
|
+
keywords: payload.keywords,
|
|
82
|
+
confidence: payload.confidence,
|
|
83
|
+
}]);
|
|
84
|
+
await db.prepare(
|
|
85
|
+
'INSERT INTO memory (fact_hash, fact, confidence) VALUES (?, ?, ?)'
|
|
86
|
+
).bind(factHash, payload.fact, payload.confidence).run();
|
|
87
|
+
} else {
|
|
88
|
+
await recordMemory(db, 'insight', payload.fact, payload.confidence, 'insights');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { published: true };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── validateInsight ────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
const TERMINAL_STAGES = ['canonical', 'refuted'];
|
|
97
|
+
const STAGE_ORDER = ['candidate', 'validated', 'expert', 'canonical', 'refuted'];
|
|
98
|
+
|
|
99
|
+
export async function validateInsight(
|
|
100
|
+
db: D1Database,
|
|
101
|
+
factHash: string,
|
|
102
|
+
repo: string,
|
|
103
|
+
confirmed: boolean,
|
|
104
|
+
): Promise<{ stage: string; transitioned: boolean }> {
|
|
105
|
+
const row = await db.prepare(
|
|
106
|
+
'SELECT id, validation_stage, validators, fact FROM memory WHERE fact_hash = ?'
|
|
107
|
+
).bind(factHash).first<{ id: number; validation_stage: string; validators: string; fact: string }>();
|
|
108
|
+
|
|
109
|
+
if (!row) {
|
|
110
|
+
return { stage: 'candidate', transitioned: false };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (TERMINAL_STAGES.includes(row.validation_stage)) {
|
|
114
|
+
return { stage: row.validation_stage, transitioned: false };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let validators: Array<{ repo: string; confirmed: boolean; date: string }> = [];
|
|
118
|
+
try {
|
|
119
|
+
validators = JSON.parse(row.validators);
|
|
120
|
+
if (!Array.isArray(validators)) validators = [];
|
|
121
|
+
} catch {
|
|
122
|
+
validators = [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!confirmed) {
|
|
126
|
+
await db.prepare(
|
|
127
|
+
'UPDATE memory SET validation_stage = ?, validators = ? WHERE id = ?'
|
|
128
|
+
).bind('refuted', JSON.stringify([...validators, { repo, confirmed, date: new Date().toISOString().slice(0, 10) }]), row.id).run();
|
|
129
|
+
return { stage: 'refuted', transitioned: true };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
validators.push({ repo, confirmed: true, date: new Date().toISOString().slice(0, 10) });
|
|
133
|
+
const confirmedCount = validators.filter(v => v.confirmed).length;
|
|
134
|
+
|
|
135
|
+
let newStage = row.validation_stage;
|
|
136
|
+
let transitioned = false;
|
|
137
|
+
|
|
138
|
+
if (row.validation_stage === 'candidate') {
|
|
139
|
+
newStage = 'validated';
|
|
140
|
+
transitioned = true;
|
|
141
|
+
} else if (row.validation_stage === 'validated' && confirmedCount >= 2) {
|
|
142
|
+
newStage = 'expert';
|
|
143
|
+
transitioned = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await db.prepare(
|
|
147
|
+
'UPDATE memory SET validation_stage = ?, validators = ? WHERE id = ?'
|
|
148
|
+
).bind(newStage, JSON.stringify(validators), row.id).run();
|
|
149
|
+
|
|
150
|
+
return { stage: newStage, transitioned };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ─── promoteInsight ─────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
export async function promoteInsight(
|
|
156
|
+
db: D1Database,
|
|
157
|
+
factHash: string,
|
|
158
|
+
targetStage: string,
|
|
159
|
+
): Promise<{ success: boolean; reason?: string }> {
|
|
160
|
+
const row = await db.prepare(
|
|
161
|
+
'SELECT id, validation_stage FROM memory WHERE fact_hash = ?'
|
|
162
|
+
).bind(factHash).first<{ id: number; validation_stage: string }>();
|
|
163
|
+
|
|
164
|
+
if (!row) {
|
|
165
|
+
return { success: false, reason: 'Insight not found' };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const currentIdx = STAGE_ORDER.indexOf(row.validation_stage);
|
|
169
|
+
const targetIdx = STAGE_ORDER.indexOf(targetStage);
|
|
170
|
+
|
|
171
|
+
if (targetIdx <= currentIdx) {
|
|
172
|
+
return { success: false, reason: 'Can only promote forward' };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (targetStage === 'canonical' && row.validation_stage !== 'expert') {
|
|
176
|
+
return { success: false, reason: 'canonical promotion requires expert stage' };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await db.prepare(
|
|
180
|
+
'UPDATE memory SET validation_stage = ? WHERE id = ?'
|
|
181
|
+
).bind(targetStage, row.id).run();
|
|
182
|
+
|
|
183
|
+
return { success: true };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ─── listInsights ───────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
export async function listInsights(
|
|
189
|
+
db: D1Database,
|
|
190
|
+
opts?: { stage?: string },
|
|
191
|
+
): Promise<Array<{
|
|
192
|
+
id: number;
|
|
193
|
+
fact: string;
|
|
194
|
+
fact_hash: string;
|
|
195
|
+
validation_stage: string;
|
|
196
|
+
confidence: number;
|
|
197
|
+
insight_type: string | null;
|
|
198
|
+
origin_repo: string | null;
|
|
199
|
+
validators: Array<{ repo: string; confirmed: boolean; date: string }>;
|
|
200
|
+
created_at: string;
|
|
201
|
+
}>> {
|
|
202
|
+
try {
|
|
203
|
+
let query = 'SELECT id, fact, fact_hash, validation_stage, confidence, validators, created_at FROM memory';
|
|
204
|
+
const bindings: unknown[] = [];
|
|
205
|
+
|
|
206
|
+
if (opts?.stage) {
|
|
207
|
+
query += ' WHERE validation_stage = ?';
|
|
208
|
+
bindings.push(opts.stage);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
query += ' ORDER BY created_at DESC';
|
|
212
|
+
|
|
213
|
+
const stmt = db.prepare(query);
|
|
214
|
+
const result = bindings.length > 0
|
|
215
|
+
? await stmt.bind(...bindings).all<any>()
|
|
216
|
+
: await stmt.all<any>();
|
|
217
|
+
|
|
218
|
+
return result.results.map((r: any) => ({
|
|
219
|
+
...r,
|
|
220
|
+
insight_type: r.insight_type ?? null,
|
|
221
|
+
origin_repo: r.origin_repo ?? null,
|
|
222
|
+
validators: (() => {
|
|
223
|
+
try { const v = JSON.parse(r.validators); return Array.isArray(v) ? v : []; } catch { return []; }
|
|
224
|
+
})(),
|
|
225
|
+
}));
|
|
226
|
+
} catch {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ─── getInsightDetail ───────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
export async function getInsightDetail(
|
|
234
|
+
db: D1Database,
|
|
235
|
+
factHash: string,
|
|
236
|
+
): Promise<{
|
|
237
|
+
id: number;
|
|
238
|
+
fact: string;
|
|
239
|
+
fact_hash: string;
|
|
240
|
+
validation_stage: string;
|
|
241
|
+
confidence: number;
|
|
242
|
+
validators: Array<{ repo: string; confirmed: boolean; date: string }>;
|
|
243
|
+
created_at: string;
|
|
244
|
+
} | null> {
|
|
245
|
+
const row = await db.prepare(
|
|
246
|
+
'SELECT id, fact, fact_hash, validation_stage, confidence, validators, created_at FROM memory WHERE fact_hash = ?'
|
|
247
|
+
).bind(factHash).first<any>();
|
|
248
|
+
|
|
249
|
+
if (!row) return null;
|
|
250
|
+
|
|
251
|
+
let validators: any[] = [];
|
|
252
|
+
try {
|
|
253
|
+
const parsed = JSON.parse(row.validators);
|
|
254
|
+
validators = Array.isArray(parsed) ? parsed : [];
|
|
255
|
+
} catch {
|
|
256
|
+
validators = [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { ...row, validators };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ─── getInsightStats ────────────────────────────────────────
|
|
263
|
+
|
|
264
|
+
export async function getInsightStats(
|
|
265
|
+
db: D1Database,
|
|
266
|
+
): Promise<{
|
|
267
|
+
total: number;
|
|
268
|
+
by_stage: Record<string, number>;
|
|
269
|
+
pending_review: number;
|
|
270
|
+
new_since_last_week: number;
|
|
271
|
+
}> {
|
|
272
|
+
try {
|
|
273
|
+
const totalRow = await db.prepare(
|
|
274
|
+
'SELECT COUNT(*) as cnt FROM memory'
|
|
275
|
+
).first<{ cnt: number }>();
|
|
276
|
+
|
|
277
|
+
const newRow = await db.prepare(
|
|
278
|
+
"SELECT COUNT(*) as cnt FROM memory WHERE created_at > datetime('now', '-7 days')"
|
|
279
|
+
).first<{ cnt: number }>();
|
|
280
|
+
|
|
281
|
+
const byStageRows = await db.prepare(
|
|
282
|
+
'SELECT validation_stage, COUNT(*) as cnt FROM memory GROUP BY validation_stage'
|
|
283
|
+
).all<{ validation_stage: string; cnt: number }>();
|
|
284
|
+
|
|
285
|
+
const by_stage: Record<string, number> = {};
|
|
286
|
+
for (const row of byStageRows.results) {
|
|
287
|
+
by_stage[row.validation_stage] = row.cnt;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
total: totalRow?.cnt ?? 0,
|
|
292
|
+
by_stage,
|
|
293
|
+
pending_review: by_stage['validated'] ?? 0,
|
|
294
|
+
new_since_last_week: newRow?.cnt ?? 0,
|
|
295
|
+
};
|
|
296
|
+
} catch {
|
|
297
|
+
return { total: 0, by_stage: {}, pending_review: 0, new_since_last_week: 0 };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─── archiveInsight ─────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
export async function archiveInsight(
|
|
304
|
+
db: D1Database,
|
|
305
|
+
factHash: string,
|
|
306
|
+
reason: string,
|
|
307
|
+
): Promise<{ success: boolean }> {
|
|
308
|
+
try {
|
|
309
|
+
const result = await db.prepare(
|
|
310
|
+
"UPDATE memory SET validation_stage = 'archived', archive_reason = ? WHERE fact_hash = ?"
|
|
311
|
+
).bind(reason, factHash).run();
|
|
312
|
+
return { success: (result.meta as any)?.changes > 0 };
|
|
313
|
+
} catch {
|
|
314
|
+
return { success: false };
|
|
315
|
+
}
|
|
316
|
+
}
|