botinabox 1.0.0 → 1.2.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/README.md +130 -205
- package/dist/channels/slack/index.d.ts +27 -1
- package/dist/channels/slack/index.js +4 -2
- package/dist/chunk-2LGXQPEA.js +41 -0
- package/dist/chunk-3X3YKI4T.js +357 -0
- package/dist/chunk-D47AIFOD.js +351 -0
- package/dist/chunk-DSNJKNEW.js +328 -0
- package/dist/connector-DDahQw-2.d.ts +63 -0
- package/dist/gmail-connector-2FVYTQJH.js +6 -0
- package/dist/gmail-connector-MNUBRNFM.js +6 -0
- package/dist/gmail-connector-URRFX6A3.js +6 -0
- package/dist/inbound-CGIXRXGC.js +8 -0
- package/dist/index.d.ts +41 -20
- package/dist/index.js +211 -43
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,8 +35,6 @@ var EVENTS = {
|
|
|
35
35
|
var DEFAULTS = {
|
|
36
36
|
TASK_POLL_INTERVAL_MS: 3e4,
|
|
37
37
|
NOTIFICATION_POLL_INTERVAL_MS: 5e3,
|
|
38
|
-
HEARTBEAT_INTERVAL_MS: 3e5,
|
|
39
|
-
// 5 minutes
|
|
40
38
|
ORPHAN_REAP_INTERVAL_MS: 3e5,
|
|
41
39
|
// 5 minutes
|
|
42
40
|
STALE_RUN_THRESHOLD_MS: 18e5,
|
|
@@ -77,6 +75,15 @@ var RUN_STATUSES = [
|
|
|
77
75
|
"cancelled"
|
|
78
76
|
];
|
|
79
77
|
|
|
78
|
+
// src/shared/utils.ts
|
|
79
|
+
function truncateAtWord(text, maxLen) {
|
|
80
|
+
if (text.length <= maxLen) return text;
|
|
81
|
+
const truncated = text.slice(0, maxLen);
|
|
82
|
+
const lastSpace = truncated.lastIndexOf(" ");
|
|
83
|
+
const cutPoint = lastSpace > maxLen * 0.5 ? lastSpace : maxLen;
|
|
84
|
+
return truncated.slice(0, cutPoint) + "...";
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
// src/core/hooks/hook-bus.ts
|
|
81
88
|
var HookBus = class {
|
|
82
89
|
registrations = /* @__PURE__ */ new Map();
|
|
@@ -1155,15 +1162,17 @@ var DataStore = class {
|
|
|
1155
1162
|
outputFile: def.indexFile,
|
|
1156
1163
|
render: def.indexRender ?? ((rows) => {
|
|
1157
1164
|
const active = rows.filter((r) => r.deleted_at == null);
|
|
1158
|
-
const
|
|
1165
|
+
const dir = def.directory;
|
|
1166
|
+
const title = dir.charAt(0).toUpperCase() + dir.slice(1);
|
|
1159
1167
|
if (!active.length) return `# ${title}
|
|
1160
1168
|
|
|
1161
1169
|
None.
|
|
1162
1170
|
`;
|
|
1163
1171
|
const lines = active.map((r) => {
|
|
1164
|
-
const
|
|
1172
|
+
const slug = String(r[def.slugColumn] ?? r.name ?? r.id ?? "unknown");
|
|
1173
|
+
const name2 = String(r.name ?? slug);
|
|
1165
1174
|
const status = r.status ? ` (${r.status})` : "";
|
|
1166
|
-
return `-
|
|
1175
|
+
return `- [${name2}](${dir}/${slug}/)${status}`;
|
|
1167
1176
|
});
|
|
1168
1177
|
return `# ${title}
|
|
1169
1178
|
|
|
@@ -1763,7 +1772,7 @@ ${s.definition}` : null,
|
|
|
1763
1772
|
const dir = r.direction === "outbound" ? "\u2192" : "\u2190";
|
|
1764
1773
|
const who = r.from_agent ?? r.from_user ?? "unknown";
|
|
1765
1774
|
const time = (r.created_at ?? "").slice(0, 16);
|
|
1766
|
-
const preview = (r.body ?? ""
|
|
1775
|
+
const preview = truncateAtWord(r.body ?? "", 80);
|
|
1767
1776
|
return `- ${dir} **${who}** (${time}): ${preview}`;
|
|
1768
1777
|
});
|
|
1769
1778
|
return `# Messages
|
|
@@ -1810,6 +1819,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
1810
1819
|
channels: true,
|
|
1811
1820
|
rules: true,
|
|
1812
1821
|
events: true,
|
|
1822
|
+
junctions: true,
|
|
1813
1823
|
...options
|
|
1814
1824
|
};
|
|
1815
1825
|
db.define("org", {
|
|
@@ -1837,6 +1847,8 @@ function defineDomainTables(db, options = {}) {
|
|
|
1837
1847
|
deploy_target: "TEXT",
|
|
1838
1848
|
production_url: "TEXT",
|
|
1839
1849
|
branch_strategy: "TEXT",
|
|
1850
|
+
repo_path: "TEXT",
|
|
1851
|
+
codename: "TEXT",
|
|
1840
1852
|
notes: "TEXT",
|
|
1841
1853
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1842
1854
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
@@ -1877,6 +1889,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
1877
1889
|
contact_name: "TEXT",
|
|
1878
1890
|
contact_email: "TEXT",
|
|
1879
1891
|
phone: "TEXT",
|
|
1892
|
+
address: "TEXT",
|
|
1880
1893
|
status: "TEXT NOT NULL DEFAULT 'active'",
|
|
1881
1894
|
notes: "TEXT",
|
|
1882
1895
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
@@ -1954,6 +1967,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
1954
1967
|
project_id: "TEXT",
|
|
1955
1968
|
access_level: "TEXT NOT NULL DEFAULT 'org'",
|
|
1956
1969
|
description: "TEXT",
|
|
1970
|
+
tags: "TEXT",
|
|
1957
1971
|
notes: "TEXT",
|
|
1958
1972
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1959
1973
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
@@ -1987,6 +2001,8 @@ function defineDomainTables(db, options = {}) {
|
|
|
1987
2001
|
scope: "TEXT NOT NULL DEFAULT 'org'",
|
|
1988
2002
|
category: "TEXT NOT NULL DEFAULT 'process'",
|
|
1989
2003
|
priority: "INTEGER NOT NULL DEFAULT 50",
|
|
2004
|
+
rationale: "TEXT",
|
|
2005
|
+
enforcement: "TEXT DEFAULT 'advisory'",
|
|
1990
2006
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1991
2007
|
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1992
2008
|
deleted_at: "TEXT"
|
|
@@ -2030,6 +2046,7 @@ function defineDomainTables(db, options = {}) {
|
|
|
2030
2046
|
actor_user_id: "TEXT",
|
|
2031
2047
|
project_id: "TEXT",
|
|
2032
2048
|
channel_id: "TEXT",
|
|
2049
|
+
source: "TEXT",
|
|
2033
2050
|
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
2034
2051
|
deleted_at: "TEXT"
|
|
2035
2052
|
},
|
|
@@ -2039,6 +2056,58 @@ function defineDomainTables(db, options = {}) {
|
|
|
2039
2056
|
]
|
|
2040
2057
|
});
|
|
2041
2058
|
}
|
|
2059
|
+
if (opts.junctions) {
|
|
2060
|
+
db.define("secret_client", {
|
|
2061
|
+
columns: {
|
|
2062
|
+
secret_id: "TEXT NOT NULL",
|
|
2063
|
+
client_id: "TEXT NOT NULL",
|
|
2064
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2065
|
+
},
|
|
2066
|
+
primaryKey: ["secret_id", "client_id"]
|
|
2067
|
+
});
|
|
2068
|
+
db.define("secret_user", {
|
|
2069
|
+
columns: {
|
|
2070
|
+
secret_id: "TEXT NOT NULL",
|
|
2071
|
+
user_id: "TEXT NOT NULL",
|
|
2072
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2073
|
+
},
|
|
2074
|
+
primaryKey: ["secret_id", "user_id"]
|
|
2075
|
+
});
|
|
2076
|
+
db.define("secret_repository", {
|
|
2077
|
+
columns: {
|
|
2078
|
+
secret_id: "TEXT NOT NULL",
|
|
2079
|
+
repository_id: "TEXT NOT NULL",
|
|
2080
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2081
|
+
},
|
|
2082
|
+
primaryKey: ["secret_id", "repository_id"]
|
|
2083
|
+
});
|
|
2084
|
+
db.define("file_agent", {
|
|
2085
|
+
columns: {
|
|
2086
|
+
file_id: "TEXT NOT NULL",
|
|
2087
|
+
agent_id: "TEXT NOT NULL",
|
|
2088
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2089
|
+
},
|
|
2090
|
+
primaryKey: ["file_id", "agent_id"]
|
|
2091
|
+
});
|
|
2092
|
+
db.define("user_channel", {
|
|
2093
|
+
columns: {
|
|
2094
|
+
user_id: "TEXT NOT NULL",
|
|
2095
|
+
channel_id: "TEXT NOT NULL",
|
|
2096
|
+
role: "TEXT",
|
|
2097
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2098
|
+
},
|
|
2099
|
+
primaryKey: ["user_id", "channel_id"]
|
|
2100
|
+
});
|
|
2101
|
+
db.define("user_project", {
|
|
2102
|
+
columns: {
|
|
2103
|
+
user_id: "TEXT NOT NULL",
|
|
2104
|
+
project_id: "TEXT NOT NULL",
|
|
2105
|
+
role: "TEXT",
|
|
2106
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
2107
|
+
},
|
|
2108
|
+
primaryKey: ["user_id", "project_id"]
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2042
2111
|
}
|
|
2043
2112
|
|
|
2044
2113
|
// src/core/data/domain-entity-contexts.ts
|
|
@@ -2151,7 +2220,32 @@ ${lines.join("\n\n")}
|
|
|
2151
2220
|
},
|
|
2152
2221
|
omitIfEmpty: true
|
|
2153
2222
|
}
|
|
2154
|
-
} : {}
|
|
2223
|
+
} : {},
|
|
2224
|
+
"MESSAGES.md": {
|
|
2225
|
+
source: {
|
|
2226
|
+
type: "hasMany",
|
|
2227
|
+
table: "messages",
|
|
2228
|
+
foreignKey: "project_id",
|
|
2229
|
+
orderBy: "created_at",
|
|
2230
|
+
limit: 100
|
|
2231
|
+
},
|
|
2232
|
+
render: (rows) => {
|
|
2233
|
+
if (!rows.length) return "# Messages\n\nNo messages.\n";
|
|
2234
|
+
const lines = rows.map((r) => {
|
|
2235
|
+
const dir = r.direction === "inbound" ? "\u2192" : "\u2190";
|
|
2236
|
+
const ts = (r.created_at ?? "").slice(0, 16);
|
|
2237
|
+
const agent = r.from_agent ? ` [${r.from_agent}]` : "";
|
|
2238
|
+
const body = r.body ?? "";
|
|
2239
|
+
const preview = truncateAtWord(body, 150);
|
|
2240
|
+
return `- ${dir} **${ts}**${agent} ${preview}`;
|
|
2241
|
+
});
|
|
2242
|
+
return `# Messages
|
|
2243
|
+
|
|
2244
|
+
${lines.join("\n")}
|
|
2245
|
+
`;
|
|
2246
|
+
},
|
|
2247
|
+
omitIfEmpty: false
|
|
2248
|
+
}
|
|
2155
2249
|
}
|
|
2156
2250
|
});
|
|
2157
2251
|
if (opts.clients) {
|
|
@@ -2633,6 +2727,73 @@ async function runPackageMigrations(db, migrations) {
|
|
|
2633
2727
|
);
|
|
2634
2728
|
}
|
|
2635
2729
|
|
|
2730
|
+
// src/core/update/auto-update.ts
|
|
2731
|
+
import { execSync as execSync2 } from "child_process";
|
|
2732
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2733
|
+
import { join as join5 } from "path";
|
|
2734
|
+
function getInstalledVersion(pkgName) {
|
|
2735
|
+
try {
|
|
2736
|
+
const pkgPath = join5(process.cwd(), "node_modules", pkgName, "package.json");
|
|
2737
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
2738
|
+
return pkg.version;
|
|
2739
|
+
} catch {
|
|
2740
|
+
return null;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
async function getLatestVersion(pkgName) {
|
|
2744
|
+
try {
|
|
2745
|
+
const res = await fetch(`https://registry.npmjs.org/${pkgName}/latest`, {
|
|
2746
|
+
headers: { accept: "application/json" },
|
|
2747
|
+
signal: AbortSignal.timeout(5e3)
|
|
2748
|
+
});
|
|
2749
|
+
if (!res.ok) return null;
|
|
2750
|
+
const data = await res.json();
|
|
2751
|
+
return data.version;
|
|
2752
|
+
} catch {
|
|
2753
|
+
return null;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
function isNewer(latest, current) {
|
|
2757
|
+
const a = latest.split(".").map(Number);
|
|
2758
|
+
const b = current.split(".").map(Number);
|
|
2759
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
2760
|
+
if ((a[i] ?? 0) > (b[i] ?? 0)) return true;
|
|
2761
|
+
if ((a[i] ?? 0) < (b[i] ?? 0)) return false;
|
|
2762
|
+
}
|
|
2763
|
+
return false;
|
|
2764
|
+
}
|
|
2765
|
+
async function autoUpdate(packages = ["botinabox", "latticesql"], opts) {
|
|
2766
|
+
const log = opts?.quiet ? () => {
|
|
2767
|
+
} : console.log;
|
|
2768
|
+
const result = { updated: false, packages: [], restartRequired: false };
|
|
2769
|
+
const toInstall = [];
|
|
2770
|
+
for (const pkg of packages) {
|
|
2771
|
+
const installed = getInstalledVersion(pkg);
|
|
2772
|
+
if (!installed) continue;
|
|
2773
|
+
const latest = await getLatestVersion(pkg);
|
|
2774
|
+
if (!latest) continue;
|
|
2775
|
+
if (isNewer(latest, installed)) {
|
|
2776
|
+
toInstall.push(`${pkg}@${latest}`);
|
|
2777
|
+
result.packages.push({ name: pkg, from: installed, to: latest });
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
if (toInstall.length === 0) return result;
|
|
2781
|
+
log(`[autoUpdate] Updating: ${toInstall.join(", ")}`);
|
|
2782
|
+
try {
|
|
2783
|
+
execSync2(`npm install ${toInstall.join(" ")}`, {
|
|
2784
|
+
cwd: process.cwd(),
|
|
2785
|
+
stdio: opts?.quiet ? "ignore" : "inherit",
|
|
2786
|
+
timeout: 6e4
|
|
2787
|
+
});
|
|
2788
|
+
result.updated = true;
|
|
2789
|
+
result.restartRequired = true;
|
|
2790
|
+
log(`[autoUpdate] Updated successfully. Restart required for changes to take effect.`);
|
|
2791
|
+
} catch (err) {
|
|
2792
|
+
console.error("[autoUpdate] Failed to install updates:", err);
|
|
2793
|
+
}
|
|
2794
|
+
return result;
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2636
2797
|
// src/core/orchestrator/config-revisions.ts
|
|
2637
2798
|
async function createConfigRevision(db, agentId, before, after) {
|
|
2638
2799
|
const existing = await db.query("config_revisions", {
|
|
@@ -3406,38 +3567,6 @@ var NdjsonLogger = class {
|
|
|
3406
3567
|
}
|
|
3407
3568
|
};
|
|
3408
3569
|
|
|
3409
|
-
// src/core/orchestrator/heartbeat-scheduler.ts
|
|
3410
|
-
var HeartbeatScheduler = class {
|
|
3411
|
-
constructor(wakeupQueue, hooks) {
|
|
3412
|
-
this.wakeupQueue = wakeupQueue;
|
|
3413
|
-
this.hooks = hooks;
|
|
3414
|
-
}
|
|
3415
|
-
wakeupQueue;
|
|
3416
|
-
hooks;
|
|
3417
|
-
timers = /* @__PURE__ */ new Map();
|
|
3418
|
-
start(agents) {
|
|
3419
|
-
for (const agent of agents) {
|
|
3420
|
-
let config;
|
|
3421
|
-
try {
|
|
3422
|
-
config = JSON.parse(agent.heartbeat_config);
|
|
3423
|
-
} catch {
|
|
3424
|
-
continue;
|
|
3425
|
-
}
|
|
3426
|
-
if (!config.enabled || !config.intervalSec) continue;
|
|
3427
|
-
const timer = setInterval(() => {
|
|
3428
|
-
void this.wakeupQueue.enqueue(agent.id, "heartbeat");
|
|
3429
|
-
}, config.intervalSec * 1e3);
|
|
3430
|
-
this.timers.set(agent.id, timer);
|
|
3431
|
-
}
|
|
3432
|
-
}
|
|
3433
|
-
stop() {
|
|
3434
|
-
for (const timer of this.timers.values()) {
|
|
3435
|
-
clearInterval(timer);
|
|
3436
|
-
}
|
|
3437
|
-
this.timers.clear();
|
|
3438
|
-
}
|
|
3439
|
-
};
|
|
3440
|
-
|
|
3441
3570
|
// src/core/orchestrator/scheduler.ts
|
|
3442
3571
|
import cronParser from "cron-parser";
|
|
3443
3572
|
import { v4 as uuid } from "uuid";
|
|
@@ -3627,7 +3756,7 @@ var Scheduler = class {
|
|
|
3627
3756
|
};
|
|
3628
3757
|
|
|
3629
3758
|
// src/core/orchestrator/adapters/api-adapter.ts
|
|
3630
|
-
import { readFileSync as
|
|
3759
|
+
import { readFileSync as readFileSync4, existsSync as existsSync2 } from "fs";
|
|
3631
3760
|
|
|
3632
3761
|
// src/core/orchestrator/adapters/tool-loop.ts
|
|
3633
3762
|
async function* toolLoop(params, callLLM, executeTool) {
|
|
@@ -3714,7 +3843,7 @@ var ApiExecutionAdapter = class {
|
|
|
3714
3843
|
const fileContents = [];
|
|
3715
3844
|
for (const filePath of ctx.contextFiles) {
|
|
3716
3845
|
if (existsSync2(filePath)) {
|
|
3717
|
-
const content =
|
|
3846
|
+
const content = readFileSync4(filePath, "utf8");
|
|
3718
3847
|
fileContents.push(`<file path="${filePath}">
|
|
3719
3848
|
${content}
|
|
3720
3849
|
</file>`);
|
|
@@ -3830,7 +3959,7 @@ function killProcessGroup(pid, signal = "SIGTERM") {
|
|
|
3830
3959
|
}
|
|
3831
3960
|
|
|
3832
3961
|
// src/core/orchestrator/adapters/output-extractor.ts
|
|
3833
|
-
import { readFileSync as
|
|
3962
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3834
3963
|
var MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
3835
3964
|
function extractOutput(ndjsonContent) {
|
|
3836
3965
|
const lines = ndjsonContent.split("\n").filter((l) => l.trim().length > 0);
|
|
@@ -4071,6 +4200,44 @@ var SecretStore = class {
|
|
|
4071
4200
|
});
|
|
4072
4201
|
await this.hooks.emit("secret.deleted", { name, environment });
|
|
4073
4202
|
}
|
|
4203
|
+
// ── Cursor persistence helpers ──────────────────────────────────
|
|
4204
|
+
/**
|
|
4205
|
+
* Load a sync cursor by key. Returns undefined if not found.
|
|
4206
|
+
* Cursors are stored as secrets with type='sync_cursor'.
|
|
4207
|
+
*/
|
|
4208
|
+
async loadCursor(key) {
|
|
4209
|
+
const name = `sync-cursor:${key}`;
|
|
4210
|
+
const rows = await this.db.query("secrets", {
|
|
4211
|
+
where: { name },
|
|
4212
|
+
filters: [{ col: "deleted_at", op: "isNull" }],
|
|
4213
|
+
limit: 1
|
|
4214
|
+
});
|
|
4215
|
+
return rows[0]?.value ?? void 0;
|
|
4216
|
+
}
|
|
4217
|
+
/**
|
|
4218
|
+
* Persist a sync cursor by key. Creates or updates the secret.
|
|
4219
|
+
*/
|
|
4220
|
+
async saveCursor(key, value) {
|
|
4221
|
+
const name = `sync-cursor:${key}`;
|
|
4222
|
+
const rows = await this.db.query("secrets", {
|
|
4223
|
+
where: { name },
|
|
4224
|
+
filters: [{ col: "deleted_at", op: "isNull" }],
|
|
4225
|
+
limit: 1
|
|
4226
|
+
});
|
|
4227
|
+
if (rows.length > 0) {
|
|
4228
|
+
await this.db.update("secrets", rows[0].id, {
|
|
4229
|
+
value,
|
|
4230
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4231
|
+
});
|
|
4232
|
+
} else {
|
|
4233
|
+
await this.db.insert("secrets", {
|
|
4234
|
+
id: uuidv42(),
|
|
4235
|
+
name,
|
|
4236
|
+
type: "sync_cursor",
|
|
4237
|
+
value
|
|
4238
|
+
});
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4074
4241
|
_toMeta(row) {
|
|
4075
4242
|
return {
|
|
4076
4243
|
id: row.id,
|
|
@@ -4192,7 +4359,6 @@ export {
|
|
|
4192
4359
|
DataStore,
|
|
4193
4360
|
DataStoreError,
|
|
4194
4361
|
EVENTS,
|
|
4195
|
-
HeartbeatScheduler,
|
|
4196
4362
|
HookBus,
|
|
4197
4363
|
MAX_CHAIN_DEPTH,
|
|
4198
4364
|
MessagePipeline,
|
|
@@ -4215,6 +4381,7 @@ export {
|
|
|
4215
4381
|
WorkflowEngine,
|
|
4216
4382
|
_resetConfig,
|
|
4217
4383
|
areDependenciesMet,
|
|
4384
|
+
autoUpdate,
|
|
4218
4385
|
buildAgentBindings,
|
|
4219
4386
|
buildChainOrigin,
|
|
4220
4387
|
buildProcessEnv,
|
|
@@ -4246,5 +4413,6 @@ export {
|
|
|
4246
4413
|
runPackageMigrations,
|
|
4247
4414
|
sanitize,
|
|
4248
4415
|
topologicalSort,
|
|
4416
|
+
truncateAtWord,
|
|
4249
4417
|
validateConfig
|
|
4250
4418
|
};
|