@meetploy/cli 1.17.1 → 1.18.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/dashboard-dist/assets/{main-Ch3tPHX5.js → main-BFhQn9QT.js} +34 -34
- package/dist/dashboard-dist/index.html +1 -1
- package/dist/dev.js +162 -103
- package/dist/index.js +344 -282
- package/package.json +1 -3
|
@@ -4,7 +4,7 @@
|
|
|
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-
|
|
7
|
+
<script type="module" crossorigin src="/assets/main-BFhQn9QT.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/main-5Kt9I_hM.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
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
|
|
|
@@ -49,62 +49,6 @@ function readPloyConfig(projectDir, configPath) {
|
|
|
49
49
|
}
|
|
50
50
|
return config;
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
// ../emulator/dist/services/alarm-service.js
|
|
54
|
-
function createAlarmHandlers(db) {
|
|
55
|
-
const setHandler = async (c) => {
|
|
56
|
-
try {
|
|
57
|
-
const body = await c.req.json();
|
|
58
|
-
const { alarmName, id, scheduledTime, payload, intervalMs } = body;
|
|
59
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
60
|
-
const scheduledTimeSec = Math.floor(scheduledTime / 1e3);
|
|
61
|
-
db.prepare(`INSERT OR REPLACE INTO alarm_entries (id, alarm_name, scheduled_time, payload, interval_ms, status, created_at)
|
|
62
|
-
VALUES (?, ?, ?, ?, ?, 'pending', ?)`).run(id, alarmName, scheduledTimeSec, payload !== void 0 ? JSON.stringify(payload) : null, intervalMs ?? null, now);
|
|
63
|
-
return c.json({ success: true });
|
|
64
|
-
} catch (err) {
|
|
65
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
66
|
-
return c.json({ success: false, error: message }, 500);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const getHandler = async (c) => {
|
|
70
|
-
try {
|
|
71
|
-
const body = await c.req.json();
|
|
72
|
-
const { alarmName, id } = body;
|
|
73
|
-
const row = db.prepare(`SELECT id, scheduled_time, payload, interval_ms FROM alarm_entries
|
|
74
|
-
WHERE alarm_name = ? AND id = ? AND status = 'pending'`).get(alarmName, id);
|
|
75
|
-
if (!row) {
|
|
76
|
-
return c.json({ alarm: null });
|
|
77
|
-
}
|
|
78
|
-
return c.json({
|
|
79
|
-
alarm: {
|
|
80
|
-
id: row.id,
|
|
81
|
-
scheduledTime: row.scheduled_time * 1e3,
|
|
82
|
-
payload: row.payload ? JSON.parse(row.payload) : void 0,
|
|
83
|
-
intervalMs: row.interval_ms ?? void 0
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
} catch (err) {
|
|
87
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
88
|
-
return c.json({ success: false, error: message }, 500);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
const deleteHandler = async (c) => {
|
|
92
|
-
try {
|
|
93
|
-
const body = await c.req.json();
|
|
94
|
-
const { alarmName, id } = body;
|
|
95
|
-
db.prepare(`DELETE FROM alarm_entries WHERE alarm_name = ? AND id = ?`).run(alarmName, id);
|
|
96
|
-
return c.json({ success: true });
|
|
97
|
-
} catch (err) {
|
|
98
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
99
|
-
return c.json({ success: false, error: message }, 500);
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
return {
|
|
103
|
-
setHandler,
|
|
104
|
-
getHandler,
|
|
105
|
-
deleteHandler
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
52
|
function getDataDir(projectDir) {
|
|
109
53
|
return join(projectDir, ".ploy");
|
|
110
54
|
}
|
|
@@ -437,7 +381,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
437
381
|
fs: config.fs,
|
|
438
382
|
workflow: config.workflow,
|
|
439
383
|
cron: config.cron,
|
|
440
|
-
|
|
384
|
+
timer: config.timer,
|
|
441
385
|
auth: config.auth
|
|
442
386
|
});
|
|
443
387
|
});
|
|
@@ -992,22 +936,22 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
992
936
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
993
937
|
}
|
|
994
938
|
});
|
|
995
|
-
app.get("/api/
|
|
939
|
+
app.get("/api/timer/:binding/entries", (c) => {
|
|
996
940
|
const binding = c.req.param("binding");
|
|
997
|
-
const
|
|
941
|
+
const timerName = config.timer?.[binding];
|
|
998
942
|
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
999
943
|
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
1000
|
-
if (!
|
|
1001
|
-
return c.json({ error: "
|
|
944
|
+
if (!timerName) {
|
|
945
|
+
return c.json({ error: "Timer binding not found" }, 404);
|
|
1002
946
|
}
|
|
1003
947
|
try {
|
|
1004
948
|
const db = dbManager2.emulatorDb;
|
|
1005
|
-
const total = db.prepare(`SELECT COUNT(*) as count FROM
|
|
949
|
+
const total = db.prepare(`SELECT COUNT(*) as count FROM timer_entries WHERE timer_name = ?`).get(timerName).count;
|
|
1006
950
|
const entries = db.prepare(`SELECT id, scheduled_time, payload, interval_ms, status, created_at
|
|
1007
|
-
FROM
|
|
1008
|
-
WHERE
|
|
951
|
+
FROM timer_entries
|
|
952
|
+
WHERE timer_name = ?
|
|
1009
953
|
ORDER BY scheduled_time DESC
|
|
1010
|
-
LIMIT ? OFFSET ?`).all(
|
|
954
|
+
LIMIT ? OFFSET ?`).all(timerName, limit, offset);
|
|
1011
955
|
return c.json({
|
|
1012
956
|
entries: entries.map((e) => ({
|
|
1013
957
|
id: e.id,
|
|
@@ -1025,19 +969,19 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1025
969
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
1026
970
|
}
|
|
1027
971
|
});
|
|
1028
|
-
app.get("/api/
|
|
972
|
+
app.get("/api/timer/:binding/metrics", (c) => {
|
|
1029
973
|
const binding = c.req.param("binding");
|
|
1030
|
-
const
|
|
1031
|
-
if (!
|
|
1032
|
-
return c.json({ error: "
|
|
974
|
+
const timerName = config.timer?.[binding];
|
|
975
|
+
if (!timerName) {
|
|
976
|
+
return c.json({ error: "Timer binding not found" }, 404);
|
|
1033
977
|
}
|
|
1034
978
|
try {
|
|
1035
979
|
const db = dbManager2.emulatorDb;
|
|
1036
980
|
const metrics = { pending: 0, fired: 0 };
|
|
1037
981
|
const statusCounts = db.prepare(`SELECT status, COUNT(*) as count
|
|
1038
|
-
FROM
|
|
1039
|
-
WHERE
|
|
1040
|
-
GROUP BY status`).all(
|
|
982
|
+
FROM timer_entries
|
|
983
|
+
WHERE timer_name = ?
|
|
984
|
+
GROUP BY status`).all(timerName);
|
|
1041
985
|
for (const row of statusCounts) {
|
|
1042
986
|
if (row.status === "pending") {
|
|
1043
987
|
metrics.pending = row.count;
|
|
@@ -1050,24 +994,24 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
1050
994
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
1051
995
|
}
|
|
1052
996
|
});
|
|
1053
|
-
app.get("/api/
|
|
997
|
+
app.get("/api/timer/:binding/executions", (c) => {
|
|
1054
998
|
const binding = c.req.param("binding");
|
|
1055
|
-
const
|
|
999
|
+
const timerName = config.timer?.[binding];
|
|
1056
1000
|
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
1057
|
-
if (!
|
|
1058
|
-
return c.json({ error: "
|
|
1001
|
+
if (!timerName) {
|
|
1002
|
+
return c.json({ error: "Timer binding not found" }, 404);
|
|
1059
1003
|
}
|
|
1060
1004
|
try {
|
|
1061
1005
|
const db = dbManager2.emulatorDb;
|
|
1062
|
-
const executions = db.prepare(`SELECT id,
|
|
1063
|
-
FROM
|
|
1064
|
-
WHERE
|
|
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 = ?
|
|
1065
1009
|
ORDER BY created_at DESC
|
|
1066
|
-
LIMIT ?`).all(
|
|
1010
|
+
LIMIT ?`).all(timerName, limit);
|
|
1067
1011
|
return c.json({
|
|
1068
1012
|
executions: executions.map((e) => ({
|
|
1069
1013
|
id: e.id,
|
|
1070
|
-
|
|
1014
|
+
timerId: e.timer_id,
|
|
1071
1015
|
scheduledTime: new Date(e.scheduled_time * 1e3).toISOString(),
|
|
1072
1016
|
status: e.status.toUpperCase(),
|
|
1073
1017
|
error: e.error,
|
|
@@ -1192,7 +1136,7 @@ function createDbHandler(getDatabase) {
|
|
|
1192
1136
|
return c.json(results);
|
|
1193
1137
|
}
|
|
1194
1138
|
if (method === "dump") {
|
|
1195
|
-
const buffer = db.
|
|
1139
|
+
const buffer = await db.dumpToBuffer();
|
|
1196
1140
|
return new Response(new Uint8Array(buffer), {
|
|
1197
1141
|
headers: {
|
|
1198
1142
|
"Content-Type": "application/octet-stream"
|
|
@@ -1461,6 +1405,62 @@ function createStateHandlers(db) {
|
|
|
1461
1405
|
deleteHandler
|
|
1462
1406
|
};
|
|
1463
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
|
+
}
|
|
1464
1464
|
function createWorkflowHandlers(db, workerUrl) {
|
|
1465
1465
|
const triggerHandler = async (c) => {
|
|
1466
1466
|
try {
|
|
@@ -1682,11 +1682,11 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1682
1682
|
app.post("/fs/delete", fsHandlers.deleteHandler);
|
|
1683
1683
|
app.post("/fs/list", fsHandlers.listHandler);
|
|
1684
1684
|
}
|
|
1685
|
-
if (config.
|
|
1686
|
-
const
|
|
1687
|
-
app.post("/
|
|
1688
|
-
app.post("/
|
|
1689
|
-
app.post("/
|
|
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
1690
|
}
|
|
1691
1691
|
if (config.auth) {
|
|
1692
1692
|
const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
|
|
@@ -1716,6 +1716,65 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1716
1716
|
});
|
|
1717
1717
|
});
|
|
1718
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
|
|
1719
1778
|
var EMULATOR_SCHEMA = `
|
|
1720
1779
|
-- Queue messages table
|
|
1721
1780
|
CREATE TABLE IF NOT EXISTS queue_messages (
|
|
@@ -1852,26 +1911,26 @@ CREATE TABLE IF NOT EXISTS cron_executions (
|
|
|
1852
1911
|
CREATE INDEX IF NOT EXISTS idx_cron_executions_name
|
|
1853
1912
|
ON cron_executions(cron_name, triggered_at);
|
|
1854
1913
|
|
|
1855
|
-
--
|
|
1856
|
-
CREATE TABLE IF NOT EXISTS
|
|
1914
|
+
-- Timer entries table (durable timers, optionally recurring)
|
|
1915
|
+
CREATE TABLE IF NOT EXISTS timer_entries (
|
|
1857
1916
|
id TEXT NOT NULL,
|
|
1858
|
-
|
|
1917
|
+
timer_name TEXT NOT NULL,
|
|
1859
1918
|
scheduled_time INTEGER NOT NULL,
|
|
1860
1919
|
payload TEXT,
|
|
1861
1920
|
interval_ms INTEGER,
|
|
1862
1921
|
status TEXT DEFAULT 'pending',
|
|
1863
1922
|
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
1864
|
-
PRIMARY KEY (
|
|
1923
|
+
PRIMARY KEY (timer_name, id)
|
|
1865
1924
|
);
|
|
1866
1925
|
|
|
1867
|
-
CREATE INDEX IF NOT EXISTS
|
|
1868
|
-
ON
|
|
1926
|
+
CREATE INDEX IF NOT EXISTS idx_timer_entries_status_time
|
|
1927
|
+
ON timer_entries(status, scheduled_time);
|
|
1869
1928
|
|
|
1870
|
-
--
|
|
1871
|
-
CREATE TABLE IF NOT EXISTS
|
|
1929
|
+
-- Timer execution log
|
|
1930
|
+
CREATE TABLE IF NOT EXISTS timer_executions (
|
|
1872
1931
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1873
|
-
|
|
1874
|
-
|
|
1932
|
+
timer_id TEXT NOT NULL,
|
|
1933
|
+
timer_name TEXT NOT NULL,
|
|
1875
1934
|
scheduled_time INTEGER NOT NULL,
|
|
1876
1935
|
status TEXT DEFAULT 'pending',
|
|
1877
1936
|
error TEXT,
|
|
@@ -1879,19 +1938,19 @@ CREATE TABLE IF NOT EXISTS alarm_executions (
|
|
|
1879
1938
|
created_at INTEGER DEFAULT (strftime('%s', 'now'))
|
|
1880
1939
|
);
|
|
1881
1940
|
|
|
1882
|
-
CREATE INDEX IF NOT EXISTS
|
|
1883
|
-
ON
|
|
1941
|
+
CREATE INDEX IF NOT EXISTS idx_timer_executions_name
|
|
1942
|
+
ON timer_executions(timer_name, created_at);
|
|
1884
1943
|
`;
|
|
1885
1944
|
function initializeDatabases(projectDir) {
|
|
1886
1945
|
const dataDir = ensureDataDir(projectDir);
|
|
1887
1946
|
const d1Databases = /* @__PURE__ */ new Map();
|
|
1888
|
-
const emulatorDb =
|
|
1947
|
+
const emulatorDb = openDatabase(join(dataDir, "emulator.db"));
|
|
1889
1948
|
emulatorDb.pragma("journal_mode = WAL");
|
|
1890
1949
|
emulatorDb.exec(EMULATOR_SCHEMA);
|
|
1891
1950
|
function getD1Database(bindingName) {
|
|
1892
1951
|
let db = d1Databases.get(bindingName);
|
|
1893
1952
|
if (!db) {
|
|
1894
|
-
db =
|
|
1953
|
+
db = openDatabase(join(dataDir, "db", `${bindingName}.db`));
|
|
1895
1954
|
db.pragma("journal_mode = WAL");
|
|
1896
1955
|
d1Databases.set(bindingName, db);
|
|
1897
1956
|
}
|