@meetploy/cli 1.17.0 → 1.17.1

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.
@@ -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-duAiLjPq.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/main-B-euJxpr.css">
7
+ <script type="module" crossorigin src="/assets/main-Ch3tPHX5.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
@@ -49,6 +49,62 @@ 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
+ }
52
108
  function getDataDir(projectDir) {
53
109
  return join(projectDir, ".ploy");
54
110
  }
@@ -381,6 +437,7 @@ function createDashboardRoutes(app, dbManager2, config) {
381
437
  fs: config.fs,
382
438
  workflow: config.workflow,
383
439
  cron: config.cron,
440
+ alarm: config.alarm,
384
441
  auth: config.auth
385
442
  });
386
443
  });
@@ -935,6 +992,93 @@ function createDashboardRoutes(app, dbManager2, config) {
935
992
  return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
936
993
  }
937
994
  });
995
+ app.get("/api/alarm/:binding/entries", (c) => {
996
+ const binding = c.req.param("binding");
997
+ const alarmName = config.alarm?.[binding];
998
+ const limit = parseInt(c.req.query("limit") ?? "20", 10);
999
+ const offset = parseInt(c.req.query("offset") ?? "0", 10);
1000
+ if (!alarmName) {
1001
+ return c.json({ error: "Alarm binding not found" }, 404);
1002
+ }
1003
+ try {
1004
+ const db = dbManager2.emulatorDb;
1005
+ const total = db.prepare(`SELECT COUNT(*) as count FROM alarm_entries WHERE alarm_name = ?`).get(alarmName).count;
1006
+ const entries = db.prepare(`SELECT id, scheduled_time, payload, interval_ms, status, created_at
1007
+ FROM alarm_entries
1008
+ WHERE alarm_name = ?
1009
+ ORDER BY scheduled_time DESC
1010
+ LIMIT ? OFFSET ?`).all(alarmName, limit, offset);
1011
+ return c.json({
1012
+ entries: entries.map((e) => ({
1013
+ id: e.id,
1014
+ scheduledTime: new Date(e.scheduled_time * 1e3).toISOString(),
1015
+ payload: e.payload ? JSON.parse(e.payload) : null,
1016
+ intervalMs: e.interval_ms,
1017
+ status: e.status.toUpperCase(),
1018
+ createdAt: new Date(e.created_at * 1e3).toISOString()
1019
+ })),
1020
+ total,
1021
+ limit,
1022
+ offset
1023
+ });
1024
+ } catch (err) {
1025
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
1026
+ }
1027
+ });
1028
+ app.get("/api/alarm/:binding/metrics", (c) => {
1029
+ const binding = c.req.param("binding");
1030
+ const alarmName = config.alarm?.[binding];
1031
+ if (!alarmName) {
1032
+ return c.json({ error: "Alarm binding not found" }, 404);
1033
+ }
1034
+ try {
1035
+ const db = dbManager2.emulatorDb;
1036
+ const metrics = { pending: 0, fired: 0 };
1037
+ const statusCounts = db.prepare(`SELECT status, COUNT(*) as count
1038
+ FROM alarm_entries
1039
+ WHERE alarm_name = ?
1040
+ GROUP BY status`).all(alarmName);
1041
+ for (const row of statusCounts) {
1042
+ if (row.status === "pending") {
1043
+ metrics.pending = row.count;
1044
+ } else if (row.status === "fired") {
1045
+ metrics.fired = row.count;
1046
+ }
1047
+ }
1048
+ return c.json({ metrics });
1049
+ } catch (err) {
1050
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
1051
+ }
1052
+ });
1053
+ app.get("/api/alarm/:binding/executions", (c) => {
1054
+ const binding = c.req.param("binding");
1055
+ const alarmName = config.alarm?.[binding];
1056
+ const limit = parseInt(c.req.query("limit") ?? "20", 10);
1057
+ if (!alarmName) {
1058
+ return c.json({ error: "Alarm binding not found" }, 404);
1059
+ }
1060
+ try {
1061
+ const db = dbManager2.emulatorDb;
1062
+ const executions = db.prepare(`SELECT id, alarm_id, scheduled_time, status, error, duration_ms, created_at
1063
+ FROM alarm_executions
1064
+ WHERE alarm_name = ?
1065
+ ORDER BY created_at DESC
1066
+ LIMIT ?`).all(alarmName, limit);
1067
+ return c.json({
1068
+ executions: executions.map((e) => ({
1069
+ id: e.id,
1070
+ alarmId: e.alarm_id,
1071
+ scheduledTime: new Date(e.scheduled_time * 1e3).toISOString(),
1072
+ status: e.status.toUpperCase(),
1073
+ error: e.error,
1074
+ durationMs: e.duration_ms,
1075
+ createdAt: new Date(e.created_at * 1e3).toISOString()
1076
+ }))
1077
+ });
1078
+ } catch (err) {
1079
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
1080
+ }
1081
+ });
938
1082
  if (hasDashboard) {
939
1083
  app.get("/assets/*", (c) => {
940
1084
  const path = c.req.path;
@@ -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.alarm) {
1686
+ const alarmHandlers = createAlarmHandlers(dbManager2.emulatorDb);
1687
+ app.post("/alarm/set", alarmHandlers.setHandler);
1688
+ app.post("/alarm/get", alarmHandlers.getHandler);
1689
+ app.post("/alarm/delete", alarmHandlers.deleteHandler);
1690
+ }
1541
1691
  if (config.auth) {
1542
1692
  const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
1543
1693
  app.post("/auth/signup", authHandlers.signupHandler);
@@ -1701,6 +1851,36 @@ CREATE TABLE IF NOT EXISTS cron_executions (
1701
1851
 
1702
1852
  CREATE INDEX IF NOT EXISTS idx_cron_executions_name
1703
1853
  ON cron_executions(cron_name, triggered_at);
1854
+
1855
+ -- Alarm entries table (durable timers, optionally recurring)
1856
+ CREATE TABLE IF NOT EXISTS alarm_entries (
1857
+ id TEXT NOT NULL,
1858
+ alarm_name TEXT NOT NULL,
1859
+ scheduled_time INTEGER NOT NULL,
1860
+ payload TEXT,
1861
+ interval_ms INTEGER,
1862
+ status TEXT DEFAULT 'pending',
1863
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
1864
+ PRIMARY KEY (alarm_name, id)
1865
+ );
1866
+
1867
+ CREATE INDEX IF NOT EXISTS idx_alarm_entries_status_time
1868
+ ON alarm_entries(status, scheduled_time);
1869
+
1870
+ -- Alarm execution log
1871
+ CREATE TABLE IF NOT EXISTS alarm_executions (
1872
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1873
+ alarm_id TEXT NOT NULL,
1874
+ alarm_name TEXT NOT NULL,
1875
+ scheduled_time INTEGER NOT NULL,
1876
+ status TEXT DEFAULT 'pending',
1877
+ error TEXT,
1878
+ duration_ms INTEGER,
1879
+ created_at INTEGER DEFAULT (strftime('%s', 'now'))
1880
+ );
1881
+
1882
+ CREATE INDEX IF NOT EXISTS idx_alarm_executions_name
1883
+ ON alarm_executions(alarm_name, created_at);
1704
1884
  `;
1705
1885
  function initializeDatabases(projectDir) {
1706
1886
  const dataDir = ensureDataDir(projectDir);