omp-wechat 1.8.0 → 1.9.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/dist/index.js +124 -56
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
30
30
|
return to;
|
|
31
31
|
};
|
|
32
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __require = import.meta.require;
|
|
33
34
|
|
|
34
35
|
// node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
35
36
|
var require_QRMode = __commonJS((exports, module) => {
|
|
@@ -1503,6 +1504,64 @@ function listAllowed() {
|
|
|
1503
1504
|
return loadAccess().allowFrom;
|
|
1504
1505
|
}
|
|
1505
1506
|
|
|
1507
|
+
// src/utils/runtime-compat.ts
|
|
1508
|
+
import { spawnSync as nodeSpawnSync } from "child_process";
|
|
1509
|
+
import { createServer as createNetServer } from "net";
|
|
1510
|
+
function isBun() {
|
|
1511
|
+
return typeof globalThis.Bun !== "undefined" && typeof globalThis.Bun.sleep === "function";
|
|
1512
|
+
}
|
|
1513
|
+
function sleep(ms) {
|
|
1514
|
+
if (isBun()) {
|
|
1515
|
+
return globalThis.Bun.sleep(ms);
|
|
1516
|
+
}
|
|
1517
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1518
|
+
}
|
|
1519
|
+
function spawnSync(cmd, opts = {}) {
|
|
1520
|
+
if (isBun()) {
|
|
1521
|
+
const res2 = globalThis.Bun.spawnSync(cmd, opts);
|
|
1522
|
+
return {
|
|
1523
|
+
exitCode: res2.exitCode,
|
|
1524
|
+
stdout: Buffer.isBuffer(res2.stdout) ? res2.stdout : Buffer.from(res2.stdout ?? []),
|
|
1525
|
+
stderr: Buffer.isBuffer(res2.stderr) ? res2.stderr : Buffer.from(res2.stderr ?? [])
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
const [command, ...args] = cmd;
|
|
1529
|
+
const res = nodeSpawnSync(command, args, {
|
|
1530
|
+
cwd: opts.cwd,
|
|
1531
|
+
stdio: ["pipe", opts.stdout ?? "pipe", opts.stderr ?? "pipe"]
|
|
1532
|
+
});
|
|
1533
|
+
return {
|
|
1534
|
+
exitCode: res.status ?? (res.error || res.signal ? 1 : 0),
|
|
1535
|
+
stdout: res.stdout ?? Buffer.alloc(0),
|
|
1536
|
+
stderr: res.stderr ?? Buffer.alloc(0)
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
function listenPort(port) {
|
|
1540
|
+
if (isBun()) {
|
|
1541
|
+
const server2 = globalThis.Bun.serve({
|
|
1542
|
+
port,
|
|
1543
|
+
hostname: "127.0.0.1",
|
|
1544
|
+
fetch() {
|
|
1545
|
+
return new Response("ok");
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
return Promise.resolve({ stop: () => server2.stop(true) });
|
|
1549
|
+
}
|
|
1550
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
1551
|
+
const server = createNetServer((socket) => {
|
|
1552
|
+
socket.end("ok");
|
|
1553
|
+
});
|
|
1554
|
+
server.once("error", (err) => {
|
|
1555
|
+
reject(err);
|
|
1556
|
+
});
|
|
1557
|
+
server.listen(port, "127.0.0.1", () => {
|
|
1558
|
+
resolve({
|
|
1559
|
+
stop: () => server.close()
|
|
1560
|
+
});
|
|
1561
|
+
});
|
|
1562
|
+
return promise;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1506
1565
|
// src/ilink/login.ts
|
|
1507
1566
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
1508
1567
|
var DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com/";
|
|
@@ -1523,13 +1582,9 @@ async function login() {
|
|
|
1523
1582
|
}
|
|
1524
1583
|
try {
|
|
1525
1584
|
import_qrcode_terminal.default.generate(qrUrl, { small: true });
|
|
1526
|
-
} catch {
|
|
1527
|
-
logger.
|
|
1528
|
-
|
|
1529
|
-
cwd: process.cwd(),
|
|
1530
|
-
stderr: "inherit"
|
|
1531
|
-
});
|
|
1532
|
-
import_qrcode_terminal.default.generate(qrUrl, { small: true });
|
|
1585
|
+
} catch (err) {
|
|
1586
|
+
logger.error("qrcode-terminal failed to render QR code:", err);
|
|
1587
|
+
throw new Error("Could not render QR code. Please confirm the plugin's dependencies are installed (qrcode-terminal).");
|
|
1533
1588
|
}
|
|
1534
1589
|
process.stderr.write(`
|
|
1535
1590
|
Scan the QR code above with WeChat, or open this link:
|
|
@@ -1572,7 +1627,7 @@ Scan the QR code above with WeChat, or open this link:
|
|
|
1572
1627
|
return;
|
|
1573
1628
|
}
|
|
1574
1629
|
}
|
|
1575
|
-
await
|
|
1630
|
+
await sleep(POLL_INTERVAL_MS);
|
|
1576
1631
|
}
|
|
1577
1632
|
throw new Error("Login timeout, please run login again");
|
|
1578
1633
|
}
|
|
@@ -1920,7 +1975,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
|
|
|
1920
1975
|
lastError = err;
|
|
1921
1976
|
if (attempt < UPLOAD_MAX_RETRIES) {
|
|
1922
1977
|
logger.warn(`CDN upload attempt ${attempt}/${UPLOAD_MAX_RETRIES} failed, retrying:`, err);
|
|
1923
|
-
await
|
|
1978
|
+
await sleep(1000 * attempt);
|
|
1924
1979
|
}
|
|
1925
1980
|
}
|
|
1926
1981
|
}
|
|
@@ -1967,7 +2022,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
|
|
|
1967
2022
|
lastSendError = err;
|
|
1968
2023
|
if (attempt < SEND_MAX_RETRIES) {
|
|
1969
2024
|
logger.warn(`Send retry ${attempt + 1}/${SEND_MAX_RETRIES}:`, err);
|
|
1970
|
-
await
|
|
2025
|
+
await sleep(1000 * (attempt + 1));
|
|
1971
2026
|
}
|
|
1972
2027
|
}
|
|
1973
2028
|
}
|
|
@@ -2008,9 +2063,6 @@ function stripMarkdown(text) {
|
|
|
2008
2063
|
return result.trim();
|
|
2009
2064
|
}
|
|
2010
2065
|
|
|
2011
|
-
// src/engine/session.ts
|
|
2012
|
-
import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent";
|
|
2013
|
-
|
|
2014
2066
|
// src/engine/session-store.ts
|
|
2015
2067
|
import { join as join7 } from "path";
|
|
2016
2068
|
import { homedir as homedir6 } from "os";
|
|
@@ -2148,6 +2200,17 @@ function clearAllSessions() {
|
|
|
2148
2200
|
// src/engine/session.ts
|
|
2149
2201
|
import { mkdirSync as mkdirSync6, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
2150
2202
|
import { join as join8 } from "path";
|
|
2203
|
+
var sdkPromise = null;
|
|
2204
|
+
function loadSdk() {
|
|
2205
|
+
sdkPromise ??= (async () => {
|
|
2206
|
+
try {
|
|
2207
|
+
return await import("@oh-my-pi/pi-coding-agent");
|
|
2208
|
+
} catch {
|
|
2209
|
+
return await import("@earendil-works/pi-coding-agent");
|
|
2210
|
+
}
|
|
2211
|
+
})();
|
|
2212
|
+
return sdkPromise;
|
|
2213
|
+
}
|
|
2151
2214
|
function outboxInstructions(outboxDir) {
|
|
2152
2215
|
return `## File delivery to WeChat
|
|
2153
2216
|
|
|
@@ -2206,9 +2269,10 @@ class ChatSession {
|
|
|
2206
2269
|
}
|
|
2207
2270
|
ensureSessionsDir();
|
|
2208
2271
|
const sessionDir = sessionDirFor(chatId);
|
|
2209
|
-
const
|
|
2272
|
+
const sdk = await loadSdk();
|
|
2273
|
+
const sessionManager = await sdk.SessionManager.continueRecent(config.cwd || process.cwd(), sessionDir);
|
|
2210
2274
|
logger.info(`Session dir: ${sessionDir} (resumed=${sessionManager.getSessionFile() !== null})`);
|
|
2211
|
-
const { session, modelFallbackMessage } = await createAgentSession({
|
|
2275
|
+
const { session, modelFallbackMessage } = await sdk.createAgentSession({
|
|
2212
2276
|
sessionManager,
|
|
2213
2277
|
enableMCP: false,
|
|
2214
2278
|
enableLsp: false,
|
|
@@ -2600,7 +2664,7 @@ class WeChatBridge {
|
|
|
2600
2664
|
this.commands.register(new ModelCommand);
|
|
2601
2665
|
this.commands.register(new NewSessionCommand);
|
|
2602
2666
|
}
|
|
2603
|
-
start() {
|
|
2667
|
+
async start() {
|
|
2604
2668
|
const config = loadConfig();
|
|
2605
2669
|
let creds;
|
|
2606
2670
|
try {
|
|
@@ -2611,7 +2675,7 @@ class WeChatBridge {
|
|
|
2611
2675
|
if (this.pollActive) {
|
|
2612
2676
|
return this.state;
|
|
2613
2677
|
}
|
|
2614
|
-
if (!this.acquireLock()) {
|
|
2678
|
+
if (!await this.acquireLock()) {
|
|
2615
2679
|
logger.debug("Another poll loop is running (port lock held), skipping");
|
|
2616
2680
|
return { running: false, config, creds, lastError: "another instance running" };
|
|
2617
2681
|
}
|
|
@@ -2664,15 +2728,9 @@ class WeChatBridge {
|
|
|
2664
2728
|
getPoolStatus() {
|
|
2665
2729
|
return this.pool?.getPoolStatus() ?? { count: 0, max: 0, chats: [] };
|
|
2666
2730
|
}
|
|
2667
|
-
acquireLock() {
|
|
2731
|
+
async acquireLock() {
|
|
2668
2732
|
try {
|
|
2669
|
-
this.lockServer =
|
|
2670
|
-
port: LOCK_PORT,
|
|
2671
|
-
hostname: "127.0.0.1",
|
|
2672
|
-
fetch() {
|
|
2673
|
-
return new Response("ok");
|
|
2674
|
-
}
|
|
2675
|
-
});
|
|
2733
|
+
this.lockServer = await listenPort(LOCK_PORT);
|
|
2676
2734
|
return true;
|
|
2677
2735
|
} catch {
|
|
2678
2736
|
return false;
|
|
@@ -2680,7 +2738,7 @@ class WeChatBridge {
|
|
|
2680
2738
|
}
|
|
2681
2739
|
releaseLock() {
|
|
2682
2740
|
if (this.lockServer) {
|
|
2683
|
-
this.lockServer.stop(
|
|
2741
|
+
this.lockServer.stop();
|
|
2684
2742
|
this.lockServer = null;
|
|
2685
2743
|
}
|
|
2686
2744
|
}
|
|
@@ -2696,9 +2754,9 @@ class WeChatBridge {
|
|
|
2696
2754
|
logger.warn(`getupdates error ret=${resp.ret} errmsg=${resp.errmsg ?? ""} (${failures}/${MAX_FAILURES})`);
|
|
2697
2755
|
if (failures >= MAX_FAILURES) {
|
|
2698
2756
|
failures = 0;
|
|
2699
|
-
await
|
|
2757
|
+
await sleep(BACKOFF_MS);
|
|
2700
2758
|
} else {
|
|
2701
|
-
await
|
|
2759
|
+
await sleep(RETRY_MS);
|
|
2702
2760
|
}
|
|
2703
2761
|
continue;
|
|
2704
2762
|
}
|
|
@@ -2719,9 +2777,9 @@ class WeChatBridge {
|
|
|
2719
2777
|
logger.error(`Poll error (${failures}/${MAX_FAILURES}):`, err);
|
|
2720
2778
|
if (failures >= MAX_FAILURES) {
|
|
2721
2779
|
failures = 0;
|
|
2722
|
-
await
|
|
2780
|
+
await sleep(BACKOFF_MS);
|
|
2723
2781
|
} else {
|
|
2724
|
-
await
|
|
2782
|
+
await sleep(RETRY_MS);
|
|
2725
2783
|
}
|
|
2726
2784
|
}
|
|
2727
2785
|
}
|
|
@@ -2890,7 +2948,7 @@ ${truncated}
|
|
|
2890
2948
|
return;
|
|
2891
2949
|
}
|
|
2892
2950
|
logger.warn(`[${chatId}] Send retry ${retries}/${MAX_SEND_RETRIES}:`, err);
|
|
2893
|
-
await
|
|
2951
|
+
await sleep(1000 * retries);
|
|
2894
2952
|
}
|
|
2895
2953
|
}
|
|
2896
2954
|
}
|
|
@@ -2969,21 +3027,21 @@ function installLaunchd() {
|
|
|
2969
3027
|
mkdirSync7(getLogDir(), { recursive: true });
|
|
2970
3028
|
const plist = plistPath();
|
|
2971
3029
|
if (existsSync3(plist)) {
|
|
2972
|
-
|
|
3030
|
+
spawnSync(["launchctl", "unload", plist], { stderr: "ignore" });
|
|
2973
3031
|
}
|
|
2974
3032
|
writeFileSync4(plist, generatePlist());
|
|
2975
|
-
const result =
|
|
3033
|
+
const result = spawnSync(["launchctl", "load", plist], { stderr: "inherit" });
|
|
2976
3034
|
if (result.exitCode !== 0) {
|
|
2977
3035
|
throw new Error("launchctl load failed");
|
|
2978
3036
|
}
|
|
2979
|
-
|
|
3037
|
+
spawnSync(["launchctl", "start", PLIST_LABEL], { stderr: "inherit" });
|
|
2980
3038
|
}
|
|
2981
3039
|
function uninstallLaunchd() {
|
|
2982
3040
|
const plist = plistPath();
|
|
2983
3041
|
if (!existsSync3(plist)) {
|
|
2984
3042
|
throw new Error("No launchd service found (may not be installed)");
|
|
2985
3043
|
}
|
|
2986
|
-
|
|
3044
|
+
spawnSync(["launchctl", "unload", plist], { stderr: "inherit" });
|
|
2987
3045
|
rmSync3(plist);
|
|
2988
3046
|
}
|
|
2989
3047
|
function servicePath() {
|
|
@@ -3026,20 +3084,20 @@ function installSystemd() {
|
|
|
3026
3084
|
const svc = servicePath();
|
|
3027
3085
|
mkdirSync7(getLogDir(), { recursive: true });
|
|
3028
3086
|
if (existsSync3(svc)) {
|
|
3029
|
-
|
|
3087
|
+
spawnSync(["sudo", "systemctl", "stop", SERVICE_NAME], { stderr: "inherit" });
|
|
3030
3088
|
}
|
|
3031
3089
|
const tmp = `/tmp/${SERVICE_NAME}.service`;
|
|
3032
3090
|
writeFileSync4(tmp, generateService());
|
|
3033
|
-
let result =
|
|
3091
|
+
let result = spawnSync(["sudo", "te", tmp, svc], { stderr: "inherit" });
|
|
3034
3092
|
if (result.exitCode !== 0) {
|
|
3035
|
-
result =
|
|
3093
|
+
result = spawnSync(["sudo", "cp", tmp, svc], { stderr: "inherit" });
|
|
3036
3094
|
}
|
|
3037
3095
|
rmSync3(tmp);
|
|
3038
3096
|
if (result.exitCode !== 0) {
|
|
3039
3097
|
throw new Error("Failed to write service file (need sudo)");
|
|
3040
3098
|
}
|
|
3041
|
-
|
|
3042
|
-
result =
|
|
3099
|
+
spawnSync(["sudo", "systemctl", "daemon-reload"], { stderr: "inherit" });
|
|
3100
|
+
result = spawnSync(["sudo", "systemctl", "enable", "--now", SERVICE_NAME], { stderr: "inherit" });
|
|
3043
3101
|
if (result.exitCode !== 0) {
|
|
3044
3102
|
throw new Error("systemctl enable failed");
|
|
3045
3103
|
}
|
|
@@ -3049,10 +3107,10 @@ function uninstallSystemd() {
|
|
|
3049
3107
|
if (!existsSync3(svc)) {
|
|
3050
3108
|
throw new Error("No systemd service found (may not be installed)");
|
|
3051
3109
|
}
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3110
|
+
spawnSync(["sudo", "systemctl", "stop", SERVICE_NAME], { stderr: "inherit" });
|
|
3111
|
+
spawnSync(["sudo", "systemctl", "disable", SERVICE_NAME], { stderr: "inherit" });
|
|
3112
|
+
spawnSync(["sudo", "rm", svc], { stderr: "inherit" });
|
|
3113
|
+
spawnSync(["sudo", "systemctl", "daemon-reload"], { stderr: "inherit" });
|
|
3056
3114
|
}
|
|
3057
3115
|
var WIN_TASK_NAME = "OMP-Wechat";
|
|
3058
3116
|
function winScriptPath() {
|
|
@@ -3102,22 +3160,22 @@ function installWinTask() {
|
|
|
3102
3160
|
mkdirSync7(join9(homedir7(), ".omp-wechat"), { recursive: true });
|
|
3103
3161
|
mkdirSync7(getLogDir(), { recursive: true });
|
|
3104
3162
|
writeFileSync4(winScriptPath(), generateWinScript());
|
|
3105
|
-
|
|
3106
|
-
|
|
3163
|
+
spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
|
|
3164
|
+
spawnSync(["schtasks", "/delete", "/tn", WIN_TASK_NAME, "/f"], { stderr: "ignore" });
|
|
3107
3165
|
const scriptPath = winScriptPath();
|
|
3108
3166
|
const taskCmd = `powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "${scriptPath}"`;
|
|
3109
|
-
const result =
|
|
3167
|
+
const result = spawnSync(["schtasks", "/create", "/tn", WIN_TASK_NAME, "/tr", taskCmd, "/sc", "onlogon", "/rl", "limited", "/f"], { stderr: "inherit" });
|
|
3110
3168
|
if (result.exitCode !== 0) {
|
|
3111
3169
|
throw new Error("schtasks /create failed");
|
|
3112
3170
|
}
|
|
3113
|
-
const runResult =
|
|
3171
|
+
const runResult = spawnSync(["schtasks", "/run", "/tn", WIN_TASK_NAME], { stderr: "inherit" });
|
|
3114
3172
|
if (runResult.exitCode !== 0) {
|
|
3115
3173
|
logger.warn(`schtasks /run failed (exit ${runResult.exitCode}) \u2014 task will start at next logon`);
|
|
3116
3174
|
}
|
|
3117
3175
|
}
|
|
3118
3176
|
function uninstallWinTask() {
|
|
3119
|
-
|
|
3120
|
-
const result =
|
|
3177
|
+
spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
|
|
3178
|
+
const result = spawnSync(["schtasks", "/delete", "/tn", WIN_TASK_NAME, "/f"], { stderr: "inherit" });
|
|
3121
3179
|
if (result.exitCode !== 0) {
|
|
3122
3180
|
throw new Error("Failed to delete scheduled task (may not be installed)");
|
|
3123
3181
|
}
|
|
@@ -3127,7 +3185,7 @@ function uninstallWinTask() {
|
|
|
3127
3185
|
}
|
|
3128
3186
|
}
|
|
3129
3187
|
function winTaskExists() {
|
|
3130
|
-
const r =
|
|
3188
|
+
const r = spawnSync(["schtasks", "/query", "/tn", WIN_TASK_NAME, "/fo", "list"], {
|
|
3131
3189
|
stdout: "ignore",
|
|
3132
3190
|
stderr: "ignore"
|
|
3133
3191
|
});
|
|
@@ -3182,20 +3240,25 @@ function isServiceInstalled() {
|
|
|
3182
3240
|
// src/index.ts
|
|
3183
3241
|
var bridge = null;
|
|
3184
3242
|
var daemonState = null;
|
|
3185
|
-
function wechatExtension(pi) {
|
|
3243
|
+
async function wechatExtension(pi) {
|
|
3186
3244
|
if (pi.pi && typeof pi.setLabel === "function") {
|
|
3187
3245
|
pi.setLabel("OMP-Wechat Bridge");
|
|
3188
3246
|
}
|
|
3189
3247
|
bridge = new WeChatBridge;
|
|
3190
|
-
daemonState = bridge.start();
|
|
3248
|
+
daemonState = await bridge.start();
|
|
3191
3249
|
if (daemonState.running) {
|
|
3192
3250
|
logger.info("WeChat bridge started at extension load");
|
|
3193
3251
|
} else {
|
|
3194
3252
|
logger.debug("WeChat bridge not running, starting 30s retry", { lastError: daemonState.lastError });
|
|
3195
|
-
setInterval(() => {
|
|
3253
|
+
setInterval(async () => {
|
|
3196
3254
|
if (daemonState?.running)
|
|
3197
3255
|
return;
|
|
3198
|
-
|
|
3256
|
+
try {
|
|
3257
|
+
daemonState = await bridge.start();
|
|
3258
|
+
} catch (err) {
|
|
3259
|
+
logger.error("WeChat bridge restart failed:", err);
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3199
3262
|
if (daemonState.running) {
|
|
3200
3263
|
logger.info("WeChat bridge: took over from failed instance");
|
|
3201
3264
|
}
|
|
@@ -3203,7 +3266,12 @@ function wechatExtension(pi) {
|
|
|
3203
3266
|
}
|
|
3204
3267
|
pi.on("session_start", async (_event, ctx) => {
|
|
3205
3268
|
if (bridge && !daemonState?.running) {
|
|
3206
|
-
|
|
3269
|
+
try {
|
|
3270
|
+
daemonState = await bridge.start();
|
|
3271
|
+
} catch (err) {
|
|
3272
|
+
logger.error("WeChat bridge start failed:", err);
|
|
3273
|
+
return;
|
|
3274
|
+
}
|
|
3207
3275
|
if (daemonState.running) {
|
|
3208
3276
|
ctx.ui.notify("WeChat bridge started", "info");
|
|
3209
3277
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omp-wechat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "OMP/Pi extension: bridge WeChat messages to OMP's AI engine via the iLink Bot API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"README.md"
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "bun build src/index.ts --outdir dist --target bun --external omp-legacy-pi-modules --external '@oh-my-pi/*'",
|
|
12
|
+
"build": "bun build src/index.ts --outdir dist --target bun --external omp-legacy-pi-modules --external '@oh-my-pi/*' --external '@earendil-works/*'",
|
|
13
13
|
"typecheck": "tsc --noEmit"
|
|
14
14
|
},
|
|
15
15
|
"omp": {
|