@meetploy/cli 1.14.1 → 1.16.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.
@@ -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-UY1Z1kG0.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/main-qA3kxECS.css">
7
+ <script type="module" crossorigin src="/assets/main-duAiLjPq.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/main-B-euJxpr.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/dist/dev.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from 'module';
2
2
  import 'child_process';
3
- import { readFile, existsSync, readFileSync, mkdirSync } from 'fs';
3
+ import { readFile, existsSync, readFileSync, mkdirSync, unlinkSync, writeFileSync } from 'fs';
4
4
  import { dirname, join } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import 'esbuild';
@@ -10,11 +10,23 @@ import { parse } from 'yaml';
10
10
  import { serve } from '@hono/node-server';
11
11
  import { Hono } from 'hono';
12
12
  import { randomUUID, createHmac, pbkdf2Sync, timingSafeEqual, randomBytes } from 'crypto';
13
- import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
14
13
  import 'os';
14
+ import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
15
15
  import Database from 'better-sqlite3';
16
16
 
17
17
  createRequire(import.meta.url);
18
+
19
+ // ../emulator/dist/utils/logger.js
20
+ var COLORS = {
21
+ reset: "\x1B[0m",
22
+ dim: "\x1B[2m",
23
+ red: "\x1B[31m"};
24
+ function timestamp() {
25
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
26
+ }
27
+ function error(message) {
28
+ console.error(`${COLORS.dim}[${timestamp()}]${COLORS.reset} ${COLORS.red}[ploy]${COLORS.reset} ${message}`);
29
+ }
18
30
  promisify(readFile);
19
31
  function readPloyConfigSync(projectDir, configPath) {
20
32
  const configFile = configPath ?? "ploy.yaml";
@@ -37,6 +49,18 @@ function readPloyConfig(projectDir, configPath) {
37
49
  }
38
50
  return config;
39
51
  }
52
+ function getDataDir(projectDir) {
53
+ return join(projectDir, ".ploy");
54
+ }
55
+ function ensureDir(dir) {
56
+ mkdirSync(dir, { recursive: true });
57
+ }
58
+ function ensureDataDir(projectDir) {
59
+ const dataDir = getDataDir(projectDir);
60
+ ensureDir(dataDir);
61
+ ensureDir(join(dataDir, "db"));
62
+ return dataDir;
63
+ }
40
64
  function generateId() {
41
65
  return randomBytes(16).toString("hex");
42
66
  }
@@ -354,6 +378,7 @@ function createDashboardRoutes(app, dbManager2, config) {
354
378
  queue: config.queue,
355
379
  cache: config.cache,
356
380
  state: config.state,
381
+ fs: config.fs,
357
382
  workflow: config.workflow,
358
383
  auth: config.auth
359
384
  });
@@ -848,6 +873,37 @@ function createDashboardRoutes(app, dbManager2, config) {
848
873
  return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
849
874
  }
850
875
  });
876
+ app.get("/api/fs/:binding/entries", (c) => {
877
+ const binding = c.req.param("binding");
878
+ const fsName = config.fs?.[binding];
879
+ const limit = parseInt(c.req.query("limit") || "20", 10);
880
+ const offset = parseInt(c.req.query("offset") || "0", 10);
881
+ if (!fsName) {
882
+ return c.json({ error: "File storage binding not found" }, 404);
883
+ }
884
+ try {
885
+ const db = dbManager2.emulatorDb;
886
+ const total = db.prepare(`SELECT COUNT(*) as count FROM fs_entries WHERE fs_name = ?`).get(fsName).count;
887
+ const entries = db.prepare(`SELECT key, size, content_type, created_at
888
+ FROM fs_entries
889
+ WHERE fs_name = ?
890
+ ORDER BY key ASC
891
+ LIMIT ? OFFSET ?`).all(fsName, limit, offset);
892
+ return c.json({
893
+ entries: entries.map((e) => ({
894
+ key: e.key,
895
+ size: e.size,
896
+ contentType: e.content_type,
897
+ createdAt: new Date(e.created_at * 1e3).toISOString()
898
+ })),
899
+ total,
900
+ limit,
901
+ offset
902
+ });
903
+ } catch (err) {
904
+ return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
905
+ }
906
+ });
851
907
  if (hasDashboard) {
852
908
  app.get("/assets/*", (c) => {
853
909
  const path = c.req.path;
@@ -975,6 +1031,120 @@ function createDbHandler(getDatabase) {
975
1031
  }
976
1032
  };
977
1033
  }
1034
+ function createFsHandlers(db, dataDir) {
1035
+ const fsBaseDir = join(dataDir, "fs");
1036
+ mkdirSync(fsBaseDir, { recursive: true });
1037
+ function getFsDir(fsName) {
1038
+ const dir = join(fsBaseDir, fsName);
1039
+ mkdirSync(dir, { recursive: true });
1040
+ return dir;
1041
+ }
1042
+ function encodedKeyPath(fsDir, key) {
1043
+ return join(fsDir, encodeURIComponent(key));
1044
+ }
1045
+ const putHandler = async (c) => {
1046
+ try {
1047
+ const body = await c.req.json();
1048
+ const { fsName, key, value, contentType } = body;
1049
+ if (!fsName || !key) {
1050
+ return c.json({ error: "Missing required fields: fsName, key, value" }, 400);
1051
+ }
1052
+ const fsDir = getFsDir(fsName);
1053
+ const filePath = encodedKeyPath(fsDir, key);
1054
+ writeFileSync(filePath, value, "utf-8");
1055
+ const size = Buffer.byteLength(value, "utf-8");
1056
+ const now = Math.floor(Date.now() / 1e3);
1057
+ db.prepare(`INSERT OR REPLACE INTO fs_entries (fs_name, key, content_type, size, created_at) VALUES (?, ?, ?, ?, ?)`).run(fsName, key, contentType ?? "application/octet-stream", size, now);
1058
+ return c.json({ success: true });
1059
+ } catch (err) {
1060
+ error(`[fs-service] put error: ${err instanceof Error ? err.message : String(err)}`);
1061
+ return c.json({
1062
+ error: `put failed: ${err instanceof Error ? err.message : String(err)}`
1063
+ }, 500);
1064
+ }
1065
+ };
1066
+ const getHandler = async (c) => {
1067
+ try {
1068
+ const body = await c.req.json();
1069
+ const { fsName, key } = body;
1070
+ if (!fsName || !key) {
1071
+ return c.json({ error: "Missing required fields: fsName, key" }, 400);
1072
+ }
1073
+ const row = db.prepare(`SELECT content_type, size FROM fs_entries WHERE fs_name = ? AND key = ?`).get(fsName, key);
1074
+ if (!row) {
1075
+ return c.json({ found: false });
1076
+ }
1077
+ const fsDir = getFsDir(fsName);
1078
+ const filePath = encodedKeyPath(fsDir, key);
1079
+ if (!existsSync(filePath)) {
1080
+ db.prepare(`DELETE FROM fs_entries WHERE fs_name = ? AND key = ?`).run(fsName, key);
1081
+ return c.json({ found: false });
1082
+ }
1083
+ const fileContent = readFileSync(filePath, "utf-8");
1084
+ return c.json({
1085
+ found: true,
1086
+ body: fileContent,
1087
+ contentType: row.content_type,
1088
+ size: row.size
1089
+ });
1090
+ } catch (err) {
1091
+ error(`[fs-service] get error: ${err instanceof Error ? err.message : String(err)}`);
1092
+ return c.json({
1093
+ error: `get failed: ${err instanceof Error ? err.message : String(err)}`
1094
+ }, 500);
1095
+ }
1096
+ };
1097
+ const deleteHandler = async (c) => {
1098
+ try {
1099
+ const body = await c.req.json();
1100
+ const { fsName, key } = body;
1101
+ if (!fsName || !key) {
1102
+ return c.json({ error: "Missing required fields: fsName, key" }, 400);
1103
+ }
1104
+ db.prepare(`DELETE FROM fs_entries WHERE fs_name = ? AND key = ?`).run(fsName, key);
1105
+ const fsDir = getFsDir(fsName);
1106
+ const filePath = encodedKeyPath(fsDir, key);
1107
+ if (existsSync(filePath)) {
1108
+ unlinkSync(filePath);
1109
+ }
1110
+ return c.json({ success: true });
1111
+ } catch (err) {
1112
+ error(`[fs-service] delete error: ${err instanceof Error ? err.message : String(err)}`);
1113
+ return c.json({
1114
+ error: `delete failed: ${err instanceof Error ? err.message : String(err)}`
1115
+ }, 500);
1116
+ }
1117
+ };
1118
+ const listHandler = async (c) => {
1119
+ try {
1120
+ const body = await c.req.json();
1121
+ const { fsName, prefix, limit } = body;
1122
+ if (!fsName) {
1123
+ return c.json({ error: "Missing required field: fsName" }, 400);
1124
+ }
1125
+ const effectiveLimit = limit ?? 1e3;
1126
+ const keys = prefix ? db.prepare(`SELECT key, size, content_type FROM fs_entries WHERE fs_name = ? AND key LIKE ? ORDER BY key ASC LIMIT ?`).all(fsName, `${prefix}%`, effectiveLimit) : db.prepare(`SELECT key, size, content_type FROM fs_entries WHERE fs_name = ? ORDER BY key ASC LIMIT ?`).all(fsName, effectiveLimit);
1127
+ return c.json({
1128
+ keys: keys.map((k) => ({
1129
+ key: k.key,
1130
+ size: k.size,
1131
+ contentType: k.content_type
1132
+ }))
1133
+ });
1134
+ } catch (err) {
1135
+ error(`[fs-service] list error: ${err instanceof Error ? err.message : String(err)}`);
1136
+ return c.json({
1137
+ error: `list failed: ${err instanceof Error ? err.message : String(err)}`
1138
+ }, 500);
1139
+ }
1140
+ };
1141
+ return {
1142
+ putHandler,
1143
+ getHandler,
1144
+ deleteHandler,
1145
+ listHandler
1146
+ };
1147
+ }
978
1148
  function createQueueHandlers(db) {
979
1149
  const sendHandler = async (c) => {
980
1150
  try {
@@ -1329,6 +1499,14 @@ async function startMockServer(dbManager2, config, options = {}) {
1329
1499
  app.post("/state/set", stateHandlers.setHandler);
1330
1500
  app.post("/state/delete", stateHandlers.deleteHandler);
1331
1501
  }
1502
+ if (config.fs) {
1503
+ const dataDir = getDataDir(process.cwd());
1504
+ const fsHandlers = createFsHandlers(dbManager2.emulatorDb, dataDir);
1505
+ app.post("/fs/put", fsHandlers.putHandler);
1506
+ app.post("/fs/get", fsHandlers.getHandler);
1507
+ app.post("/fs/delete", fsHandlers.deleteHandler);
1508
+ app.post("/fs/list", fsHandlers.listHandler);
1509
+ }
1332
1510
  if (config.auth) {
1333
1511
  const authHandlers = createAuthHandlers(dbManager2.emulatorDb);
1334
1512
  app.post("/auth/signup", authHandlers.signupHandler);
@@ -1357,18 +1535,6 @@ async function startMockServer(dbManager2, config, options = {}) {
1357
1535
  });
1358
1536
  });
1359
1537
  }
1360
- function getDataDir(projectDir) {
1361
- return join(projectDir, ".ploy");
1362
- }
1363
- function ensureDir(dir) {
1364
- mkdirSync(dir, { recursive: true });
1365
- }
1366
- function ensureDataDir(projectDir) {
1367
- const dataDir = getDataDir(projectDir);
1368
- ensureDir(dataDir);
1369
- ensureDir(join(dataDir, "db"));
1370
- return dataDir;
1371
- }
1372
1538
  var EMULATOR_SCHEMA = `
1373
1539
  -- Queue messages table
1374
1540
  CREATE TABLE IF NOT EXISTS queue_messages (
@@ -1478,6 +1644,16 @@ CREATE TABLE IF NOT EXISTS state_entries (
1478
1644
  value TEXT NOT NULL,
1479
1645
  PRIMARY KEY (state_name, key)
1480
1646
  );
1647
+
1648
+ -- File storage entries table (metadata for stored files)
1649
+ CREATE TABLE IF NOT EXISTS fs_entries (
1650
+ fs_name TEXT NOT NULL,
1651
+ key TEXT NOT NULL,
1652
+ content_type TEXT NOT NULL DEFAULT 'application/octet-stream',
1653
+ size INTEGER NOT NULL DEFAULT 0,
1654
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
1655
+ PRIMARY KEY (fs_name, key)
1656
+ );
1481
1657
  `;
1482
1658
  function initializeDatabases(projectDir) {
1483
1659
  const dataDir = ensureDataDir(projectDir);