agentlas 1.0.64 → 1.0.66
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 +87 -0
- package/README.md +8 -6
- package/bin/agentlas.cjs +341 -16
- package/engine/acp/server.cjs +27 -1
- package/engine/agentlas-api-agent.cjs +151 -120
- package/engine/agentlas-banner.cjs +6 -2
- package/engine/agentlas-capabilities.cjs +17 -10
- package/engine/agentlas-cloud-runtime.cjs +4 -5
- package/engine/agentlas-config.cjs +19 -54
- package/engine/agentlas-core-harness.cjs +59 -5
- package/engine/agentlas-desktop-loadout.cjs +17 -3
- package/engine/agentlas-evolution.cjs +75 -18
- package/engine/agentlas-experience-exchange.cjs +145 -14
- package/engine/agentlas-experience-intake.cjs +2 -2
- package/engine/agentlas-i18n.cjs +0 -2
- package/engine/agentlas-input.cjs +273 -20
- package/engine/agentlas-judgment.cjs +0 -0
- package/engine/agentlas-mcp-env.cjs +84 -23
- package/engine/agentlas-mcp-wrapper.cjs +36 -4
- package/engine/agentlas-memory-governance.cjs +2 -1
- package/engine/agentlas-memory-import.cjs +49 -12
- package/engine/agentlas-native-host.cjs +279 -44
- package/engine/agentlas-onboard.cjs +23 -1
- package/engine/agentlas-permissions.cjs +5 -1
- package/engine/agentlas-sqlite-policy.cjs +40 -2
- package/engine/agentlas-tasks.cjs +7 -0
- package/engine/agentlas-tools.cjs +37 -10
- package/engine/agentlas-ui.cjs +26 -4
- package/engine/agentlas-workforce.cjs +29 -5
- package/engine/agentlas-workload-routing.cjs +83 -10
- package/engine/agentlas.cjs +10 -3
- package/engine/agents/builder.cjs +35 -10
- package/engine/agents/files.cjs +81 -10
- package/engine/agents/import-local.cjs +10 -2
- package/engine/agents/registry.cjs +10 -3
- package/engine/agents/router.cjs +7 -7
- package/engine/agents/routes.cjs +31 -9
- package/engine/automation/daemon.cjs +154 -40
- package/engine/automation/launchd.cjs +395 -16
- package/engine/automation/schedule.cjs +90 -3
- package/engine/automation/store.cjs +163 -59
- package/engine/bootstrap-schema.sql +4 -4
- package/engine/browser/cdp.cjs +315 -32
- package/engine/browser/vault.cjs +1 -0
- package/engine/cli-output.cjs +80 -28
- package/engine/cloud/auth.cjs +131 -13
- package/engine/cloud/hub-client.cjs +41 -16
- package/engine/cloud-assets/cas.cjs +18 -9
- package/engine/cloud-assets/commands.cjs +108 -30
- package/engine/cloud-assets/package.cjs +66 -3
- package/engine/cloud-assets/restore.cjs +12 -9
- package/engine/cloud-assets/state.cjs +308 -113
- package/engine/commands/acp.cjs +13 -2
- package/engine/commands/automation.cjs +75 -24
- package/engine/commands/billing.cjs +6 -1
- package/engine/commands/browser.cjs +67 -34
- package/engine/commands/build.cjs +42 -12
- package/engine/commands/call.cjs +1 -1
- package/engine/commands/cd.cjs +1 -1
- package/engine/commands/cloud.cjs +2 -1
- package/engine/commands/connect.cjs +38 -13
- package/engine/commands/creds.cjs +74 -29
- package/engine/commands/doctor.cjs +17 -1
- package/engine/commands/document.cjs +40 -12
- package/engine/commands/env.cjs +6 -1
- package/engine/commands/firm.cjs +42 -12
- package/engine/commands/graph.cjs +36 -32
- package/engine/commands/help.cjs +11 -1
- package/engine/commands/hep.cjs +1 -1
- package/engine/commands/import.cjs +5 -4
- package/engine/commands/index.cjs +1 -1
- package/engine/commands/install.cjs +6 -4
- package/engine/commands/list.cjs +56 -16
- package/engine/commands/login.cjs +5 -0
- package/engine/commands/logout.cjs +6 -1
- package/engine/commands/mcp.cjs +8 -2
- package/engine/commands/memory.cjs +3 -2
- package/engine/commands/multimodal.cjs +12 -0
- package/engine/commands/native.cjs +5 -1
- package/engine/commands/one.cjs +78 -33
- package/engine/commands/plugin.cjs +13 -2
- package/engine/commands/project.cjs +15 -12
- package/engine/commands/research.cjs +18 -7
- package/engine/commands/roles.cjs +104 -28
- package/engine/commands/route.cjs +2 -2
- package/engine/commands/run.cjs +42 -9
- package/engine/commands/search.cjs +23 -2
- package/engine/commands/setup.cjs +6 -1
- package/engine/commands/storm.cjs +44 -6
- package/engine/commands/swarm.cjs +47 -9
- package/engine/commands/telegram.cjs +6 -1
- package/engine/commands/uninstall.cjs +12 -6
- package/engine/commands/update.cjs +5 -0
- package/engine/commands/upload.cjs +2 -1
- package/engine/commands/usage.cjs +6 -1
- package/engine/commands/version.cjs +6 -1
- package/engine/commands/whoami.cjs +6 -1
- package/engine/commands/workforce.cjs +60 -16
- package/engine/core/capability-grants.cjs +7 -2
- package/engine/core/daemon-client.cjs +36 -7
- package/engine/core/db.cjs +10 -2
- package/engine/core/desktop-core-fetch.cjs +477 -77
- package/engine/core/schema-ensure.cjs +23 -11
- package/engine/experience/build.cjs +5 -1
- package/engine/experience/intents.cjs +29 -82
- package/engine/experience/runtime.cjs +7 -1
- package/engine/experience/variant.cjs +58 -4
- package/engine/firms/orchestrate.cjs +9 -6
- package/engine/graph/ask-model.cjs +22 -11
- package/engine/graph/interview.cjs +74 -23
- package/engine/graph/package.cjs +63 -8
- package/engine/hephaestus/local-core.cjs +18 -5
- package/engine/hephaestus/runtime.cjs +48 -43
- package/engine/hub/install.cjs +230 -61
- package/engine/hub/plugins.cjs +391 -29
- package/engine/mcp/consent.cjs +25 -17
- package/engine/mcp/contract.cjs +229 -24
- package/engine/mcp/inventory.cjs +15 -5
- package/engine/mcp/plan.cjs +5 -1
- package/engine/mcp/probe.cjs +24 -9
- package/engine/memory-cli/curate.cjs +74 -16
- package/engine/oberon/common.cjs +37 -12
- package/engine/oberon/manifest.cjs +25 -2
- package/engine/oberon/outputs.cjs +49 -19
- package/engine/oberon/render.cjs +665 -40
- package/engine/project/career-graph.cjs +15 -2
- package/engine/project/controller.cjs +133 -15
- package/engine/project/credentials.cjs +212 -34
- package/engine/project/env-file.cjs +93 -8
- package/engine/project/memory-context.cjs +11 -57
- package/engine/project/ontology.cjs +66 -25
- package/engine/project/paths.cjs +42 -8
- package/engine/project/seed.cjs +88 -51
- package/engine/project/state.cjs +41 -16
- package/engine/project/team.cjs +26 -1
- package/engine/runtime-refusal.cjs +4 -1
- package/engine/runtimes/acp-driver.cjs +56 -9
- package/engine/runtimes/auth-evidence.cjs +21 -2
- package/engine/runtimes/detect.cjs +28 -8
- package/engine/runtimes/kinds.cjs +12 -4
- package/engine/runtimes/overrides.cjs +4 -3
- package/engine/runtimes/resolve.cjs +8 -7
- package/engine/runtimes/roles.cjs +7 -5
- package/engine/sessions/apply-fences.cjs +396 -56
- package/engine/sessions/fences.cjs +111 -27
- package/engine/sessions/memory-turn.cjs +62 -3
- package/engine/sessions/orchestrator.cjs +11 -1
- package/engine/sessions/prompt.cjs +66 -9
- package/engine/sessions/session.cjs +70 -10
- package/engine/sessions/sink.cjs +78 -12
- package/engine/sessions/store.cjs +29 -7
- package/engine/storm/storm.cjs +26 -3
- package/engine/storm/swarm.cjs +31 -3
- package/engine/telegram/connect.cjs +187 -24
- package/engine/ui/commands-catalog.cjs +1 -1
- package/engine/ui/palette.cjs +12 -2
- package/engine/ui/repl.cjs +57 -9
- package/engine/ui/screens.cjs +2 -4
- package/engine/vendor/desktop-core.manifest.json +5 -5
- package/engine/workforce/capture.cjs +9 -27
- package/engine/workforce/deps.cjs +18 -6
- package/engine/workforce/local-core-transport.cjs +68 -5
- package/package.json +1 -1
- package/engine/commands/career-graph.cjs +0 -51
|
@@ -27,9 +27,9 @@ function resolvePermission(ctx) {
|
|
|
27
27
|
// persistent()가 fail-closed 로 강등한다.
|
|
28
28
|
try {
|
|
29
29
|
const policy = require("../agentlas-permissions.cjs");
|
|
30
|
-
return policy.persistent(ctx && ctx.prefs && ctx.prefs.permission, "
|
|
30
|
+
return policy.persistent(ctx && ctx.prefs && ctx.prefs.permission, "read");
|
|
31
31
|
} catch {
|
|
32
|
-
return "
|
|
32
|
+
return "read";
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -37,16 +37,56 @@ function resolvePermission(ctx) {
|
|
|
37
37
|
function splitRuntimeOverride(args) {
|
|
38
38
|
const rest = [];
|
|
39
39
|
let runtimeOverride = null;
|
|
40
|
+
let seen = false;
|
|
41
|
+
let passthrough = false;
|
|
40
42
|
for (let i = 0; i < args.length; i += 1) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const token = String(args[i]);
|
|
44
|
+
if (passthrough) { rest.push(token); continue; }
|
|
45
|
+
if (token === "--") { passthrough = true; rest.push(token); continue; }
|
|
46
|
+
if (token === "--runtime" || token.startsWith("--runtime=")) {
|
|
47
|
+
if (seen) throw new Error("duplicate option: --runtime");
|
|
48
|
+
seen = true;
|
|
49
|
+
const inline = token.startsWith("--runtime=");
|
|
50
|
+
const value = inline ? token.slice(10) : args[++i];
|
|
51
|
+
if (value === undefined || value === "" || (!inline && String(value).startsWith("--"))) {
|
|
52
|
+
throw new Error("--runtime requires a value");
|
|
53
|
+
}
|
|
54
|
+
runtimeOverride = String(value);
|
|
43
55
|
continue;
|
|
44
56
|
}
|
|
45
|
-
rest.push(
|
|
57
|
+
rest.push(token);
|
|
46
58
|
}
|
|
47
59
|
return { rest, runtimeOverride };
|
|
48
60
|
}
|
|
49
61
|
|
|
62
|
+
function validateWorkforceArgs(args) {
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
let passthrough = false;
|
|
65
|
+
let hasTask = false;
|
|
66
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
67
|
+
const token = String(args[i]);
|
|
68
|
+
if (passthrough) { hasTask = true; continue; }
|
|
69
|
+
if (token === "--") { passthrough = true; continue; }
|
|
70
|
+
if (token === "--benchmark" || token === "--json") {
|
|
71
|
+
const key = token.slice(2);
|
|
72
|
+
if (seen.has(key)) return { error: `duplicate option: ${token}`, hasTask };
|
|
73
|
+
seen.add(key);
|
|
74
|
+
} else if (token === "--parallel" || token === "-n" || token.startsWith("--parallel=")) {
|
|
75
|
+
if (seen.has("parallel")) return { error: "duplicate option: --parallel", hasTask };
|
|
76
|
+
seen.add("parallel");
|
|
77
|
+
const inline = token.startsWith("--parallel=");
|
|
78
|
+
const raw = inline ? token.slice(11) : args[++i];
|
|
79
|
+
const value = Number(raw);
|
|
80
|
+
if (raw === undefined || raw === "" || (!inline && String(raw).startsWith("--")) || !Number.isInteger(value) || value < 1 || value > 8) {
|
|
81
|
+
return { error: "--parallel requires a positive integer (maximum 8)", hasTask };
|
|
82
|
+
}
|
|
83
|
+
} else if (token.startsWith("-")) {
|
|
84
|
+
return { error: `unknown option: ${token} (use -- before a task token that starts with '-')`, hasTask };
|
|
85
|
+
} else hasTask = true;
|
|
86
|
+
}
|
|
87
|
+
return { error: null, hasTask };
|
|
88
|
+
}
|
|
89
|
+
|
|
50
90
|
/**
|
|
51
91
|
* command 별 디스패치. commands/index.cjs 는 명령 이름을 별도 인자로 넘기지 않으므로
|
|
52
92
|
* run()이 기본 "workforce" 로 진입하고, 별칭 배선 시 이 dispatch 를 직접 쓴다.
|
|
@@ -56,13 +96,15 @@ async function dispatch(ctx, command, args) {
|
|
|
56
96
|
case "workforce":
|
|
57
97
|
case "network":
|
|
58
98
|
case "taskforce": {
|
|
59
|
-
|
|
99
|
+
let parsed;
|
|
100
|
+
try { parsed = splitRuntimeOverride(args); }
|
|
101
|
+
catch (error) { ctx.err(String((error && error.message) || error)); return 1; }
|
|
102
|
+
const { rest, runtimeOverride } = parsed;
|
|
103
|
+
const validation = validateWorkforceArgs(rest);
|
|
104
|
+
if (validation.error) { ctx.err(validation.error); return 1; }
|
|
60
105
|
// usage 경로(작업 텍스트 없음)는 SQLite를 열 이유가 없다 — cmdWorkforce 의
|
|
61
106
|
// parseArgs 와 동일한 판정: 플래그가 아닌 토큰이 하나도 없으면 빈 작업이다.
|
|
62
|
-
|
|
63
|
-
!["--benchmark", "--json", "--parallel", "-n"].includes(String(token))
|
|
64
|
-
&& !(i > 0 && ["--parallel", "-n"].includes(String(rest[i - 1]))));
|
|
65
|
-
if (!hasTask) {
|
|
107
|
+
if (!validation.hasTask) {
|
|
66
108
|
ctx.err("usage: agentlas workforce <task> [--parallel N] [--benchmark] [--json] [--runtime <kind>]");
|
|
67
109
|
return 1;
|
|
68
110
|
}
|
|
@@ -107,11 +149,13 @@ async function dispatch(ctx, command, args) {
|
|
|
107
149
|
case "hep-hub": {
|
|
108
150
|
const sourceScope = command.slice(4); // network|local|cloud|hub
|
|
109
151
|
const ko = ctx.lang !== "en";
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
152
|
+
let parsed;
|
|
153
|
+
try { parsed = splitRuntimeOverride(args); }
|
|
154
|
+
catch (error) { ctx.err(String((error && error.message) || error)); return 1; }
|
|
155
|
+
const { rest, runtimeOverride } = parsed;
|
|
156
|
+
const validation = validateWorkforceArgs(rest);
|
|
157
|
+
if (validation.error) { ctx.err(validation.error); return 1; }
|
|
158
|
+
if (!validation.hasTask) {
|
|
115
159
|
ctx.err(`usage: agentlas ${command} "<task>" [--parallel N] [--json] [--runtime <kind>]`);
|
|
116
160
|
return 1;
|
|
117
161
|
}
|
|
@@ -162,4 +206,4 @@ function run(ctx, args) {
|
|
|
162
206
|
return dispatch(ctx, "workforce", args);
|
|
163
207
|
}
|
|
164
208
|
|
|
165
|
-
module.exports = { run, dispatch };
|
|
209
|
+
module.exports = { run, dispatch, splitRuntimeOverride, validateWorkforceArgs };
|
|
@@ -55,7 +55,9 @@ function patternMatches(pattern, detail) {
|
|
|
55
55
|
if (pattern === null || pattern === undefined || pattern === "") return true;
|
|
56
56
|
if (!detail) return false;
|
|
57
57
|
const text = String(pattern);
|
|
58
|
-
|
|
58
|
+
// Keep the separator before `*`. Removing it turns `git push *` into the
|
|
59
|
+
// broader prefix `git push`, which also authorizes unrelated `git pushx`.
|
|
60
|
+
if (text.endsWith("*")) return String(detail).startsWith(text.slice(0, -1));
|
|
59
61
|
return String(detail) === text;
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -126,7 +128,10 @@ function recordCapabilityGrant(db, input) {
|
|
|
126
128
|
if (!capabilityGrantsAvailable(db)) {
|
|
127
129
|
return { ok: false, available: false, reason: UNAVAILABLE_REASON };
|
|
128
130
|
}
|
|
129
|
-
const decision = input && input.decision
|
|
131
|
+
const decision = input && input.decision;
|
|
132
|
+
if (decision !== "allow" && decision !== "deny") {
|
|
133
|
+
return { ok: false, available: true, reason: "capability_grants decision must be allow or deny" };
|
|
134
|
+
}
|
|
130
135
|
try {
|
|
131
136
|
db.prepare(
|
|
132
137
|
`INSERT INTO ${TABLE} (capability, pattern, decision, scope, source, created_at)
|
|
@@ -17,9 +17,12 @@
|
|
|
17
17
|
*/
|
|
18
18
|
const net = require("node:net");
|
|
19
19
|
const crypto = require("node:crypto");
|
|
20
|
+
const fs = require("node:fs");
|
|
20
21
|
const path = require("node:path");
|
|
21
22
|
const { userDataDir } = require("./paths.cjs");
|
|
22
23
|
|
|
24
|
+
const DAEMON_MAX_RESPONSE_BYTES = 16 * 1024 * 1024;
|
|
25
|
+
|
|
23
26
|
/** 데몬 소켓 주소 — control-socket.ts 의 defaultControlSocketPath 와 같은 규칙. */
|
|
24
27
|
function daemonSocketPath() {
|
|
25
28
|
const dir = userDataDir();
|
|
@@ -41,11 +44,13 @@ function callDaemon(method, params, timeoutMs) {
|
|
|
41
44
|
return new Promise((resolve, reject) => {
|
|
42
45
|
const socket = net.connect(daemonSocketPath());
|
|
43
46
|
const id = crypto.randomUUID();
|
|
44
|
-
let buffer =
|
|
47
|
+
let buffer = Buffer.alloc(0);
|
|
48
|
+
let responseBytes = 0;
|
|
45
49
|
let settled = false;
|
|
46
50
|
const finish = (fn) => {
|
|
47
51
|
if (settled) return;
|
|
48
52
|
settled = true;
|
|
53
|
+
buffer = Buffer.alloc(0);
|
|
49
54
|
clearTimeout(timer);
|
|
50
55
|
socket.destroy();
|
|
51
56
|
fn();
|
|
@@ -56,10 +61,19 @@ function callDaemon(method, params, timeoutMs) {
|
|
|
56
61
|
);
|
|
57
62
|
socket.on("connect", () => socket.write(`${JSON.stringify({ id, method, params })}\n`));
|
|
58
63
|
socket.on("data", (chunk) => {
|
|
59
|
-
|
|
64
|
+
if (settled) return;
|
|
65
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
66
|
+
responseBytes += bytes.length;
|
|
67
|
+
if (responseBytes > DAEMON_MAX_RESPONSE_BYTES) {
|
|
68
|
+
const error = new Error(`daemon response exceeded ${DAEMON_MAX_RESPONSE_BYTES} bytes`);
|
|
69
|
+
error.code = "DAEMON_RESPONSE_BODY_LIMIT";
|
|
70
|
+
finish(() => reject(error));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
buffer = buffer.length ? Buffer.concat([buffer, bytes]) : bytes;
|
|
60
74
|
let nl;
|
|
61
|
-
while ((nl = buffer.indexOf(
|
|
62
|
-
const line = buffer.
|
|
75
|
+
while (!settled && (nl = buffer.indexOf(0x0a)) >= 0) {
|
|
76
|
+
const line = buffer.subarray(0, nl).toString("utf8").trim();
|
|
63
77
|
buffer = buffer.slice(nl + 1);
|
|
64
78
|
if (!line) continue;
|
|
65
79
|
let message;
|
|
@@ -109,8 +123,23 @@ function sharesOurStore(pong) {
|
|
|
109
123
|
);
|
|
110
124
|
return false;
|
|
111
125
|
}
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
let oursStat;
|
|
127
|
+
let theirsStat;
|
|
128
|
+
try {
|
|
129
|
+
oursStat = fs.statSync(path.resolve(ours));
|
|
130
|
+
theirsStat = fs.statSync(path.resolve(theirs));
|
|
131
|
+
} catch {
|
|
132
|
+
console.error(
|
|
133
|
+
`[daemon] could not verify the database identity (${theirs}) against this command (${ours}).\n`
|
|
134
|
+
+ "[daemon] Running the graph locally instead, so the work cannot land in a different store.",
|
|
135
|
+
);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
if (
|
|
139
|
+
oursStat.isFile() && theirsStat.isFile() &&
|
|
140
|
+
String(oursStat.dev) === String(theirsStat.dev) &&
|
|
141
|
+
String(oursStat.ino) === String(theirsStat.ino)
|
|
142
|
+
) return true;
|
|
114
143
|
console.error(
|
|
115
144
|
`[daemon] the daemon opened a different database (${theirs}) than this command (${ours}).\n`
|
|
116
145
|
+ "[daemon] Running the graph locally instead, so the work cannot land in a different store.",
|
|
@@ -118,4 +147,4 @@ function sharesOurStore(pong) {
|
|
|
118
147
|
return false;
|
|
119
148
|
}
|
|
120
149
|
|
|
121
|
-
module.exports = { daemonSocketPath, callDaemon, daemonAvailable, sharesOurStore };
|
|
150
|
+
module.exports = { daemonSocketPath, callDaemon, daemonAvailable, sharesOurStore, DAEMON_MAX_RESPONSE_BYTES };
|
package/engine/core/db.cjs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* 정직하게 거절한다(승급은 여전히 데스크탑의 일이다).
|
|
15
15
|
*/
|
|
16
16
|
const fs = require("node:fs");
|
|
17
|
+
const { pathToFileURL } = require("node:url");
|
|
17
18
|
const { configureSqliteConnection } = require("../agentlas-sqlite-policy.cjs");
|
|
18
19
|
const { parseSemVer, compareSemVer } = require("../semver.cjs");
|
|
19
20
|
const { dbPath } = require("./paths.cjs");
|
|
@@ -38,13 +39,16 @@ function loadNodeSqliteQuietly() {
|
|
|
38
39
|
function openRaw(file) {
|
|
39
40
|
try {
|
|
40
41
|
const Database = require("better-sqlite3");
|
|
41
|
-
const db = new Database(file);
|
|
42
|
+
const db = new Database(file, { fileMustExist: true });
|
|
42
43
|
configureSqliteConnection(db);
|
|
43
44
|
db.__driver = "better-sqlite3";
|
|
44
45
|
return db;
|
|
45
46
|
} catch { /* optional dep 미설치/ABI 불일치 → node:sqlite */ }
|
|
46
47
|
const { DatabaseSync } = loadNodeSqliteQuietly();
|
|
47
|
-
|
|
48
|
+
// DatabaseSync does not expose better-sqlite3's `fileMustExist` option. A
|
|
49
|
+
// SQLite URI with mode=rw opens an existing file read/write but refuses to
|
|
50
|
+
// create it when an existsSync check races with deletion.
|
|
51
|
+
const db = new DatabaseSync(`${pathToFileURL(file).href}?mode=rw`);
|
|
48
52
|
configureSqliteConnection(db);
|
|
49
53
|
db.__driver = "node:sqlite";
|
|
50
54
|
return db;
|
|
@@ -125,9 +129,13 @@ function columnExists(db, table, col) {
|
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
function runWriteTransaction(db, fn) {
|
|
132
|
+
if (!db || typeof fn !== "function") throw new TypeError("SQLite connection and transaction callback are required");
|
|
128
133
|
db.exec("BEGIN IMMEDIATE");
|
|
129
134
|
try {
|
|
130
135
|
const result = fn();
|
|
136
|
+
if (result && typeof result.then === "function") {
|
|
137
|
+
throw new TypeError("SQLite write transactions must be synchronous");
|
|
138
|
+
}
|
|
131
139
|
db.exec("COMMIT");
|
|
132
140
|
return result;
|
|
133
141
|
} catch (e) {
|