@palettelab/cli 0.3.40 → 0.3.41
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/README.md +5 -2
- package/lib/commands/dev.js +18 -3
- package/lib/commands/logs.js +42 -26
- package/lib/dev-simulator.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -474,7 +474,9 @@ By default this starts:
|
|
|
474
474
|
|
|
475
475
|
Your frontend entry is bundled and watched. Your backend entry is loaded under
|
|
476
476
|
`/api/v1/plugins/<your-id>/*` with a dev `PluginContext`, so normal SDK calls
|
|
477
|
-
work without Docker or platform source.
|
|
477
|
+
work without Docker or platform source. The terminal streams local frontend
|
|
478
|
+
requests, frontend rebuilds, and backend process output while `pltt dev` is
|
|
479
|
+
running.
|
|
478
480
|
|
|
479
481
|
The simulator also provides the same language fields that Palette OS provides:
|
|
480
482
|
`language`, `fallbackLanguage`, `supportedLanguages`, and `setLanguage()`.
|
|
@@ -489,7 +491,7 @@ the declared Alembic migrations and platform-managed Postgres/RLS.
|
|
|
489
491
|
|
|
490
492
|
`7321` and `8732` are preferred defaults, not hard requirements. If either port is already in use, `pltt dev` automatically picks the next free port and prints the actual URLs.
|
|
491
493
|
|
|
492
|
-
`pltt dev --sandbox` skips Docker and publishes a reviewable preview to a configured Palette sandbox. This is the payment-gateway-style flow: your app uses local SDKs, while real platform services such as login, Data Room, storage, database policies, logs, review, and publish lifecycle run in the hosted sandbox. It defaults to `--env staging` unless `--env` or `PALETTE_ENV` is set, adds a 24-hour preview TTL by default,
|
|
494
|
+
`pltt dev --sandbox` skips Docker and publishes a reviewable preview to a configured Palette sandbox. This is the payment-gateway-style flow: your app uses local SDKs, while real platform services such as login, Data Room, storage, database policies, logs, review, and publish lifecycle run in the hosted sandbox. It defaults to `--env staging` unless `--env` or `PALETTE_ENV` is set, adds a 24-hour preview TTL by default, prints the preview/status URLs, then tails hosted app logs in the same terminal. Set `PALETTE_DEV_LOG_TAIL` to change the initial hosted log tail size.
|
|
493
495
|
|
|
494
496
|
For developer sandboxes where manual approval should not block OS testing, run the sandbox backend with `APPSTORE_AUTO_APPROVE_SANDBOX_PREVIEWS=true`. Preview publishes are still checked by the automated review gates; passing preview publishes are marked active and synced immediately so backend routes, installs, logs, permissions, and platform APIs can be tested in the OS without Docker.
|
|
495
497
|
|
|
@@ -511,6 +513,7 @@ Environment variables:
|
|
|
511
513
|
| `PALETTE_FRONTEND_PORT` | `7321` | Preferred starting host port for the frontend |
|
|
512
514
|
| `PALETTE_BACKEND_PORT` | `8732` | Preferred starting host port for the backend |
|
|
513
515
|
| `PALETTE_DEV_DATABASE_URL` | `.palette/dev/<plugin-id>.sqlite3` | Override the local dev database URL |
|
|
516
|
+
| `PALETTE_DEV_LOG_TAIL` | `200` | Initial hosted log events shown by `pltt dev --sandbox` / `--cloud` |
|
|
514
517
|
| `APPSTORE_AUTO_APPROVE_SANDBOX_PREVIEWS` | `false` | Backend setting for hosted sandboxes; auto-approve passing preview publishes so developers can test full OS behavior without manual review |
|
|
515
518
|
|
|
516
519
|
### `pltt secrets`
|
package/lib/commands/dev.js
CHANGED
|
@@ -4,11 +4,12 @@ const path = require("path")
|
|
|
4
4
|
const { spawn, spawnSync } = require("child_process")
|
|
5
5
|
const { loadManifest } = require("../manifest")
|
|
6
6
|
const { watchFrontend } = require("../bundler")
|
|
7
|
-
const { parseFlags } = require("../environments")
|
|
7
|
+
const { parseFlags, resolveEnvironment } = require("../environments")
|
|
8
8
|
const { resolveDevPorts } = require("../ports")
|
|
9
9
|
const { startSimulator } = require("../dev-simulator")
|
|
10
10
|
const { loadLocalEnv } = require("../secrets")
|
|
11
11
|
const publish = require("./publish")
|
|
12
|
+
const logs = require("./logs")
|
|
12
13
|
|
|
13
14
|
const DEFAULT_PLATFORM_IMAGE = "ghcr.io/palette-lab/platform-dev:latest"
|
|
14
15
|
const DEFAULT_IMAGE = process.env.PALETTE_DEV_IMAGE || DEFAULT_PLATFORM_IMAGE
|
|
@@ -102,8 +103,22 @@ async function run(args, { cwd }) {
|
|
|
102
103
|
console.log(`[pltt] preview URL: ${record.preview_url}`)
|
|
103
104
|
}
|
|
104
105
|
if (!json && record?.id) {
|
|
105
|
-
|
|
106
|
-
console.log(
|
|
106
|
+
const envName = flags.env || process.env.PALETTE_ENV || "staging"
|
|
107
|
+
console.log(`[pltt] status: pltt status ${record.id} --env ${envName}`)
|
|
108
|
+
const logPluginId = record.plugin_id || loadManifest(cwd).id
|
|
109
|
+
console.log(`[pltt] logs: streaming hosted app logs for ${logPluginId} (Ctrl+C to stop)`)
|
|
110
|
+
try {
|
|
111
|
+
const env = resolveEnvironment({ cwd, flags: { ...flags, env: envName } })
|
|
112
|
+
await logs.streamPluginLogs({
|
|
113
|
+
env,
|
|
114
|
+
pluginId: logPluginId,
|
|
115
|
+
tail: Number(process.env.PALETTE_DEV_LOG_TAIL || "200") || 200,
|
|
116
|
+
follow: true,
|
|
117
|
+
})
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.warn(`[pltt] could not stream hosted logs: ${err instanceof Error ? err.message : String(err)}`)
|
|
120
|
+
console.warn(`[pltt] run manually: pltt logs ${logPluginId} --follow --env ${envName}`)
|
|
121
|
+
}
|
|
107
122
|
}
|
|
108
123
|
return
|
|
109
124
|
}
|
package/lib/commands/logs.js
CHANGED
|
@@ -31,6 +31,39 @@ async function fetchLogs(env, pluginId, tail) {
|
|
|
31
31
|
return res.json()
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function formatLogEvent(e) {
|
|
35
|
+
const ts = e.created_at
|
|
36
|
+
const tag = String(e.event_type || "event").padEnd(11)
|
|
37
|
+
const route = e.route || "-"
|
|
38
|
+
const code = e.status_code != null ? `[${e.status_code}]` : ""
|
|
39
|
+
const err = e.error_message ? ` err=${e.error_message}` : ""
|
|
40
|
+
return `${ts} ${tag} ${route} ${code}${err}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function streamPluginLogs({
|
|
44
|
+
env,
|
|
45
|
+
pluginId,
|
|
46
|
+
tail = 50,
|
|
47
|
+
follow = false,
|
|
48
|
+
json = false,
|
|
49
|
+
intervalMs = 3000,
|
|
50
|
+
log = console.log,
|
|
51
|
+
}) {
|
|
52
|
+
let lastSeenId = 0
|
|
53
|
+
do {
|
|
54
|
+
const data = await fetchLogs(env, pluginId, tail)
|
|
55
|
+
const events = data.events || []
|
|
56
|
+
const fresh = events.filter((e) => e.id > lastSeenId)
|
|
57
|
+
if (fresh.length) lastSeenId = Math.max(...fresh.map((e) => e.id))
|
|
58
|
+
if (json) {
|
|
59
|
+
for (const e of fresh) log(JSON.stringify(e))
|
|
60
|
+
} else {
|
|
61
|
+
for (const e of fresh) log(formatLogEvent(e))
|
|
62
|
+
}
|
|
63
|
+
if (follow) await new Promise((r) => setTimeout(r, intervalMs))
|
|
64
|
+
} while (follow)
|
|
65
|
+
}
|
|
66
|
+
|
|
34
67
|
async function run(argv, { cwd }) {
|
|
35
68
|
const json = argv.includes("--json")
|
|
36
69
|
const follow = argv.includes("--follow") || argv.includes("-f")
|
|
@@ -68,32 +101,15 @@ async function run(argv, { cwd }) {
|
|
|
68
101
|
process.exit(1)
|
|
69
102
|
}
|
|
70
103
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.error(`[pltt] ${err.message}`)
|
|
78
|
-
process.exit(1)
|
|
79
|
-
}
|
|
80
|
-
const events = data.events || []
|
|
81
|
-
const fresh = events.filter((e) => e.id > lastSeenId)
|
|
82
|
-
if (fresh.length) lastSeenId = Math.max(...fresh.map((e) => e.id))
|
|
83
|
-
if (json) {
|
|
84
|
-
for (const e of fresh) console.log(JSON.stringify(e))
|
|
85
|
-
} else {
|
|
86
|
-
for (const e of fresh) {
|
|
87
|
-
const ts = e.created_at
|
|
88
|
-
const tag = e.event_type.padEnd(11)
|
|
89
|
-
const route = e.route || "-"
|
|
90
|
-
const code = e.status_code != null ? `[${e.status_code}]` : ""
|
|
91
|
-
const err = e.error_message ? ` err=${e.error_message}` : ""
|
|
92
|
-
console.log(`${ts} ${tag} ${route} ${code}${err}`)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (follow) await new Promise((r) => setTimeout(r, 3000))
|
|
96
|
-
} while (follow)
|
|
104
|
+
try {
|
|
105
|
+
await streamPluginLogs({ env, pluginId, tail, follow, json })
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(`[pltt] ${err.message}`)
|
|
108
|
+
process.exit(1)
|
|
109
|
+
}
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
module.exports = run
|
|
113
|
+
module.exports.fetchLogs = fetchLogs
|
|
114
|
+
module.exports.formatLogEvent = formatLogEvent
|
|
115
|
+
module.exports.streamPluginLogs = streamPluginLogs
|
package/lib/dev-simulator.js
CHANGED
|
@@ -242,6 +242,7 @@ function startBackend(cwd, devDir, manifest, backendPort) {
|
|
|
242
242
|
if (sdkPath) {
|
|
243
243
|
env.PYTHONPATH = [sdkPath, env.PYTHONPATH].filter(Boolean).join(path.delimiter)
|
|
244
244
|
}
|
|
245
|
+
console.log(`[pltt] backend logs: ${backendEntry} via uvicorn on http://localhost:${backendPort}`)
|
|
245
246
|
const child = spawn(
|
|
246
247
|
python,
|
|
247
248
|
["-m", "uvicorn", `${path.basename(runner, ".py")}:app`, "--host", "127.0.0.1", "--port", String(backendPort), "--reload", "--reload-dir", path.dirname(absEntry)],
|
|
@@ -444,10 +445,12 @@ async function startFrontend(cwd, devDir, manifest, frontendPort, backendPort) {
|
|
|
444
445
|
const server = http.createServer((req, res) => {
|
|
445
446
|
const url = new URL(req.url || "/", `http://127.0.0.1:${frontendPort}`)
|
|
446
447
|
if (url.pathname === "/simulator.js") {
|
|
448
|
+
console.log(`[pltt] frontend GET ${url.pathname} -> 200`)
|
|
447
449
|
res.writeHead(200, { "Content-Type": "text/javascript; charset=utf-8", "Cache-Control": "no-store" })
|
|
448
450
|
res.end(fs.readFileSync(bundlePath))
|
|
449
451
|
return
|
|
450
452
|
}
|
|
453
|
+
console.log(`[pltt] frontend GET ${url.pathname} -> 200`)
|
|
451
454
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" })
|
|
452
455
|
res.end(indexHtml(manifest))
|
|
453
456
|
})
|
|
@@ -467,6 +470,7 @@ async function startSimulator({ cwd, frontendPort, backendPort }) {
|
|
|
467
470
|
console.log("[pltt] running local SDK simulator (no Docker)")
|
|
468
471
|
console.log(`[pltt] app: http://localhost:${frontendPort}/`)
|
|
469
472
|
if (backend) console.log(`[pltt] backend: http://localhost:${backendPort}/api/v1/plugins/${manifest.id}`)
|
|
473
|
+
console.log("[pltt] logs: streaming frontend requests, frontend bundle rebuilds, and backend output")
|
|
470
474
|
console.log("[pltt] use `pltt dev --platform` for full Docker platform parity")
|
|
471
475
|
|
|
472
476
|
const stop = async () => {
|