@meetploy/cli 1.17.0 → 1.17.2
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/dashboard-dist/assets/{main-B-euJxpr.css → main-5Kt9I_hM.css} +1 -1
- package/dist/dashboard-dist/assets/main-BFhQn9QT.js +354 -0
- package/dist/dashboard-dist/index.html +2 -2
- package/dist/dev.js +244 -5
- package/dist/index.js +464 -6
- package/package.json +1 -3
- package/dist/dashboard-dist/assets/main-duAiLjPq.js +0 -339
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Ploy Dev Dashboard</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/main-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
7
|
+
<script type="module" crossorigin src="/assets/main-BFhQn9QT.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/main-5Kt9I_hM.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/dev.js
CHANGED
|
@@ -10,9 +10,9 @@ import { parse } from 'yaml';
|
|
|
10
10
|
import { randomUUID, createHmac, pbkdf2Sync, timingSafeEqual, randomBytes } from 'crypto';
|
|
11
11
|
import { serve } from '@hono/node-server';
|
|
12
12
|
import { Hono } from 'hono';
|
|
13
|
-
import 'os';
|
|
13
|
+
import { tmpdir } from 'os';
|
|
14
14
|
import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
|
|
15
|
-
import
|
|
15
|
+
import { DatabaseSync, backup } from 'node:sqlite';
|
|
16
16
|
|
|
17
17
|
createRequire(import.meta.url);
|
|
18
18
|
|
|
@@ -381,6 +381,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
381
381
|
fs: config.fs,
|
|
382
382
|
workflow: config.workflow,
|
|
383
383
|
cron: config.cron,
|
|
384
|
+
timer: config.timer,
|
|
384
385
|
auth: config.auth
|
|
385
386
|
});
|
|
386
387
|
});
|
|
@@ -935,6 +936,93 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
935
936
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
936
937
|
}
|
|
937
938
|
});
|
|
939
|
+
app.get("/api/timer/:binding/entries", (c) => {
|
|
940
|
+
const binding = c.req.param("binding");
|
|
941
|
+
const timerName = config.timer?.[binding];
|
|
942
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
943
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
944
|
+
if (!timerName) {
|
|
945
|
+
return c.json({ error: "Timer binding not found" }, 404);
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
const db = dbManager2.emulatorDb;
|
|
949
|
+
const total = db.prepare(`SELECT COUNT(*) as count FROM timer_entries WHERE timer_name = ?`).get(timerName).count;
|
|
950
|
+
const entries = db.prepare(`SELECT id, scheduled_time, payload, interval_ms, status, created_at
|
|
951
|
+
FROM timer_entries
|
|
952
|
+
WHERE timer_name = ?
|
|
953
|
+
ORDER BY scheduled_time DESC
|
|
954
|
+
LIMIT ? OFFSET ?`).all(timerName, limit, offset);
|
|
955
|
+
return c.json({
|
|
956
|
+
entries: entries.map((e) => ({
|
|
957
|
+
id: e.id,
|
|
958
|
+
scheduledTime: new Date(e.scheduled_time * 1e3).toISOString(),
|
|
959
|
+
payload: e.payload ? JSON.parse(e.payload) : null,
|
|
960
|
+
intervalMs: e.interval_ms,
|
|
961
|
+
status: e.status.toUpperCase(),
|
|
962
|
+
createdAt: new Date(e.created_at * 1e3).toISOString()
|
|
963
|
+
})),
|
|
964
|
+
total,
|
|
965
|
+
limit,
|
|
966
|
+
offset
|
|
967
|
+
});
|
|
968
|
+
} catch (err) {
|
|
969
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
app.get("/api/timer/:binding/metrics", (c) => {
|
|
973
|
+
const binding = c.req.param("binding");
|
|
974
|
+
const timerName = config.timer?.[binding];
|
|
975
|
+
if (!timerName) {
|
|
976
|
+
return c.json({ error: "Timer binding not found" }, 404);
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
const db = dbManager2.emulatorDb;
|
|
980
|
+
const metrics = { pending: 0, fired: 0 };
|
|
981
|
+
const statusCounts = db.prepare(`SELECT status, COUNT(*) as count
|
|
982
|
+
FROM timer_entries
|
|
983
|
+
WHERE timer_name = ?
|
|
984
|
+
GROUP BY status`).all(timerName);
|
|
985
|
+
for (const row of statusCounts) {
|
|
986
|
+
if (row.status === "pending") {
|
|
987
|
+
metrics.pending = row.count;
|
|
988
|
+
} else if (row.status === "fired") {
|
|
989
|
+
metrics.fired = row.count;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return c.json({ metrics });
|
|
993
|
+
} catch (err) {
|
|
994
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
app.get("/api/timer/:binding/executions", (c) => {
|
|
998
|
+
const binding = c.req.param("binding");
|
|
999
|
+
const timerName = config.timer?.[binding];
|
|
1000
|
+
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
1001
|
+
if (!timerName) {
|
|
1002
|
+
return c.json({ error: "Timer binding not found" }, 404);
|
|
1003
|
+
}
|
|
1004
|
+
try {
|
|
1005
|
+
const db = dbManager2.emulatorDb;
|
|
1006
|
+
const executions = db.prepare(`SELECT id, timer_id, scheduled_time, status, error, duration_ms, created_at
|
|
1007
|
+
FROM timer_executions
|
|
1008
|
+
WHERE timer_name = ?
|
|
1009
|
+
ORDER BY created_at DESC
|
|
1010
|
+
LIMIT ?`).all(timerName, limit);
|
|
1011
|
+
return c.json({
|
|
1012
|
+
executions: executions.map((e) => ({
|
|
1013
|
+
id: e.id,
|
|
1014
|
+
timerId: e.timer_id,
|
|
1015
|
+
scheduledTime: new Date(e.scheduled_time * 1e3).toISOString(),
|
|
1016
|
+
status: e.status.toUpperCase(),
|
|
1017
|
+
error: e.error,
|
|
1018
|
+
durationMs: e.duration_ms,
|
|
1019
|
+
createdAt: new Date(e.created_at * 1e3).toISOString()
|
|
1020
|
+
}))
|
|
1021
|
+
});
|
|
1022
|
+
} catch (err) {
|
|
1023
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
938
1026
|
if (hasDashboard) {
|
|
939
1027
|
app.get("/assets/*", (c) => {
|
|
940
1028
|
const path = c.req.path;
|
|
@@ -1048,7 +1136,7 @@ function createDbHandler(getDatabase) {
|
|
|
1048
1136
|
return c.json(results);
|
|
1049
1137
|
}
|
|
1050
1138
|
if (method === "dump") {
|
|
1051
|
-
const buffer = db.
|
|
1139
|
+
const buffer = await db.dumpToBuffer();
|
|
1052
1140
|
return new Response(new Uint8Array(buffer), {
|
|
1053
1141
|
headers: {
|
|
1054
1142
|
"Content-Type": "application/octet-stream"
|
|
@@ -1317,6 +1405,62 @@ function createStateHandlers(db) {
|
|
|
1317
1405
|
deleteHandler
|
|
1318
1406
|
};
|
|
1319
1407
|
}
|
|
1408
|
+
|
|
1409
|
+
// ../emulator/dist/services/timer-service.js
|
|
1410
|
+
function createTimerHandlers(db) {
|
|
1411
|
+
const setHandler = async (c) => {
|
|
1412
|
+
try {
|
|
1413
|
+
const body = await c.req.json();
|
|
1414
|
+
const { timerName, id, scheduledTime, payload, intervalMs } = body;
|
|
1415
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1416
|
+
const scheduledTimeSec = Math.floor(scheduledTime / 1e3);
|
|
1417
|
+
db.prepare(`INSERT OR REPLACE INTO timer_entries (id, timer_name, scheduled_time, payload, interval_ms, status, created_at)
|
|
1418
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?)`).run(id, timerName, scheduledTimeSec, payload !== void 0 ? JSON.stringify(payload) : null, intervalMs ?? null, now);
|
|
1419
|
+
return c.json({ success: true });
|
|
1420
|
+
} catch (err) {
|
|
1421
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1422
|
+
return c.json({ success: false, error: message }, 500);
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
const getHandler = async (c) => {
|
|
1426
|
+
try {
|
|
1427
|
+
const body = await c.req.json();
|
|
1428
|
+
const { timerName, id } = body;
|
|
1429
|
+
const row = db.prepare(`SELECT id, scheduled_time, payload, interval_ms FROM timer_entries
|
|
1430
|
+
WHERE timer_name = ? AND id = ? AND status = 'pending'`).get(timerName, id);
|
|
1431
|
+
if (!row) {
|
|
1432
|
+
return c.json({ timer: null });
|
|
1433
|
+
}
|
|
1434
|
+
return c.json({
|
|
1435
|
+
timer: {
|
|
1436
|
+
id: row.id,
|
|
1437
|
+
scheduledTime: row.scheduled_time * 1e3,
|
|
1438
|
+
payload: row.payload ? JSON.parse(row.payload) : void 0,
|
|
1439
|
+
intervalMs: row.interval_ms ?? void 0
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
} catch (err) {
|
|
1443
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1444
|
+
return c.json({ success: false, error: message }, 500);
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
const deleteHandler = async (c) => {
|
|
1448
|
+
try {
|
|
1449
|
+
const body = await c.req.json();
|
|
1450
|
+
const { timerName, id } = body;
|
|
1451
|
+
db.prepare(`DELETE FROM timer_entries WHERE timer_name = ? AND id = ?`).run(timerName, id);
|
|
1452
|
+
return c.json({ success: true });
|
|
1453
|
+
} catch (err) {
|
|
1454
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1455
|
+
return c.json({ success: false, error: message }, 500);
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
return {
|
|
1459
|
+
setHandler,
|
|
1460
|
+
getHandler,
|
|
1461
|
+
deleteHandler
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1320
1464
|
function createWorkflowHandlers(db, workerUrl) {
|
|
1321
1465
|
const triggerHandler = async (c) => {
|
|
1322
1466
|
try {
|
|
@@ -1538,6 +1682,12 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1538
1682
|
app.post("/fs/delete", fsHandlers.deleteHandler);
|
|
1539
1683
|
app.post("/fs/list", fsHandlers.listHandler);
|
|
1540
1684
|
}
|
|
1685
|
+
if (config.timer) {
|
|
1686
|
+
const timerHandlers = createTimerHandlers(dbManager2.emulatorDb);
|
|
1687
|
+
app.post("/timer/set", timerHandlers.setHandler);
|
|
1688
|
+
app.post("/timer/get", timerHandlers.getHandler);
|
|
1689
|
+
app.post("/timer/delete", timerHandlers.deleteHandler);
|
|
1690
|
+
}
|
|
1541
1691
|
if (config.auth) {
|
|
1542
1692
|
const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
|
|
1543
1693
|
app.post("/auth/signup", authHandlers.signupHandler);
|
|
@@ -1566,6 +1716,65 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1566
1716
|
});
|
|
1567
1717
|
});
|
|
1568
1718
|
}
|
|
1719
|
+
function openDatabase(filepath) {
|
|
1720
|
+
const db = new DatabaseSync(filepath);
|
|
1721
|
+
return {
|
|
1722
|
+
prepare(sql) {
|
|
1723
|
+
const stmt = db.prepare(sql);
|
|
1724
|
+
return {
|
|
1725
|
+
run(...params) {
|
|
1726
|
+
const result = stmt.run(...params);
|
|
1727
|
+
return {
|
|
1728
|
+
changes: Number(result.changes),
|
|
1729
|
+
lastInsertRowid: result.lastInsertRowid
|
|
1730
|
+
};
|
|
1731
|
+
},
|
|
1732
|
+
get(...params) {
|
|
1733
|
+
return stmt.get(...params);
|
|
1734
|
+
},
|
|
1735
|
+
all(...params) {
|
|
1736
|
+
return stmt.all(...params);
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
},
|
|
1740
|
+
exec(sql) {
|
|
1741
|
+
db.exec(sql);
|
|
1742
|
+
},
|
|
1743
|
+
close() {
|
|
1744
|
+
db.close();
|
|
1745
|
+
},
|
|
1746
|
+
pragma(pragma) {
|
|
1747
|
+
db.exec(`PRAGMA ${pragma}`);
|
|
1748
|
+
},
|
|
1749
|
+
transaction(fn) {
|
|
1750
|
+
return () => {
|
|
1751
|
+
db.exec("BEGIN");
|
|
1752
|
+
try {
|
|
1753
|
+
const result = fn();
|
|
1754
|
+
db.exec("COMMIT");
|
|
1755
|
+
return result;
|
|
1756
|
+
} catch (e) {
|
|
1757
|
+
db.exec("ROLLBACK");
|
|
1758
|
+
throw e;
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
},
|
|
1762
|
+
async dumpToBuffer() {
|
|
1763
|
+
const tempPath = join(tmpdir(), `ploy-dump-${randomUUID()}.db`);
|
|
1764
|
+
try {
|
|
1765
|
+
await backup(db, tempPath);
|
|
1766
|
+
return readFileSync(tempPath);
|
|
1767
|
+
} finally {
|
|
1768
|
+
try {
|
|
1769
|
+
unlinkSync(tempPath);
|
|
1770
|
+
} catch {
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
// ../emulator/dist/utils/sqlite.js
|
|
1569
1778
|
var EMULATOR_SCHEMA = `
|
|
1570
1779
|
-- Queue messages table
|
|
1571
1780
|
CREATE TABLE IF NOT EXISTS queue_messages (
|
|
@@ -1701,17 +1910,47 @@ CREATE TABLE IF NOT EXISTS cron_executions (
|
|
|
1701
1910
|
|
|
1702
1911
|
CREATE INDEX IF NOT EXISTS idx_cron_executions_name
|
|
1703
1912
|
ON cron_executions(cron_name, triggered_at);
|
|
1913
|
+
|
|
1914
|
+
-- Timer entries table (durable timers, optionally recurring)
|
|
1915
|
+
CREATE TABLE IF NOT EXISTS timer_entries (
|
|
1916
|
+
id TEXT NOT NULL,
|
|
1917
|
+
timer_name TEXT NOT NULL,
|
|
1918
|
+
scheduled_time INTEGER NOT NULL,
|
|
1919
|
+
payload TEXT,
|
|
1920
|
+
interval_ms INTEGER,
|
|
1921
|
+
status TEXT DEFAULT 'pending',
|
|
1922
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
1923
|
+
PRIMARY KEY (timer_name, id)
|
|
1924
|
+
);
|
|
1925
|
+
|
|
1926
|
+
CREATE INDEX IF NOT EXISTS idx_timer_entries_status_time
|
|
1927
|
+
ON timer_entries(status, scheduled_time);
|
|
1928
|
+
|
|
1929
|
+
-- Timer execution log
|
|
1930
|
+
CREATE TABLE IF NOT EXISTS timer_executions (
|
|
1931
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1932
|
+
timer_id TEXT NOT NULL,
|
|
1933
|
+
timer_name TEXT NOT NULL,
|
|
1934
|
+
scheduled_time INTEGER NOT NULL,
|
|
1935
|
+
status TEXT DEFAULT 'pending',
|
|
1936
|
+
error TEXT,
|
|
1937
|
+
duration_ms INTEGER,
|
|
1938
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now'))
|
|
1939
|
+
);
|
|
1940
|
+
|
|
1941
|
+
CREATE INDEX IF NOT EXISTS idx_timer_executions_name
|
|
1942
|
+
ON timer_executions(timer_name, created_at);
|
|
1704
1943
|
`;
|
|
1705
1944
|
function initializeDatabases(projectDir) {
|
|
1706
1945
|
const dataDir = ensureDataDir(projectDir);
|
|
1707
1946
|
const d1Databases = /* @__PURE__ */ new Map();
|
|
1708
|
-
const emulatorDb =
|
|
1947
|
+
const emulatorDb = openDatabase(join(dataDir, "emulator.db"));
|
|
1709
1948
|
emulatorDb.pragma("journal_mode = WAL");
|
|
1710
1949
|
emulatorDb.exec(EMULATOR_SCHEMA);
|
|
1711
1950
|
function getD1Database(bindingName) {
|
|
1712
1951
|
let db = d1Databases.get(bindingName);
|
|
1713
1952
|
if (!db) {
|
|
1714
|
-
db =
|
|
1953
|
+
db = openDatabase(join(dataDir, "db", `${bindingName}.db`));
|
|
1715
1954
|
db.pragma("journal_mode = WAL");
|
|
1716
1955
|
d1Databases.set(bindingName, db);
|
|
1717
1956
|
}
|