@palettelab/cli 0.3.48 → 0.3.50
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 +56 -8
- package/backend-sdk/palette_sdk/__init__.py +12 -0
- package/backend-sdk/palette_sdk/apps.py +12 -0
- package/backend-sdk/palette_sdk/events.py +35 -1
- package/backend-sdk/palette_sdk/manifest.py +48 -3
- package/backend-sdk/palette_sdk/services.py +147 -0
- package/docs/python-backend-sdk.md +65 -35
- package/lib/bundler.js +58 -5
- package/lib/cli.js +10 -0
- package/lib/commands/dev.js +4 -1
- package/lib/commands/doctor.js +9 -5
- package/lib/commands/package.js +4 -1
- package/lib/commands/publish.js +97 -19
- package/lib/commands/services.js +426 -0
- package/lib/commands/test.js +21 -11
- package/lib/css-scope.js +124 -0
- package/lib/dev-simulator.js +34 -4
- package/lib/manifest.js +120 -5
- package/lib/secrets.js +83 -8
- package/package.json +4 -2
package/lib/dev-simulator.js
CHANGED
|
@@ -8,7 +8,7 @@ const { spawn, spawnSync } = require("child_process")
|
|
|
8
8
|
const { loadManifest } = require("./manifest")
|
|
9
9
|
const { frontendBuildConfig } = require("./bundler")
|
|
10
10
|
const { generatePaletteAppEntry } = require("./app-router")
|
|
11
|
-
const { loadLocalEnv } = require("./secrets")
|
|
11
|
+
const { declaredSecrets, loadLocalEnv } = require("./secrets")
|
|
12
12
|
|
|
13
13
|
function loadEsbuild() {
|
|
14
14
|
try {
|
|
@@ -51,6 +51,13 @@ function needsDatabase(manifest) {
|
|
|
51
51
|
return Boolean(manifest.database || manifest.capabilities?.database)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function databaseDriverDependencies(databaseUrl) {
|
|
55
|
+
const url = String(databaseUrl || "")
|
|
56
|
+
if (/^postgres(?:ql)?(?:\+asyncpg)?:/i.test(url)) return ["asyncpg>=0.29.0"]
|
|
57
|
+
if (/^mysql\+aiomysql:/i.test(url)) return ["aiomysql>=0.2.0"]
|
|
58
|
+
return []
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
function loadLocalConnections(cwd, manifest) {
|
|
55
62
|
const declared = Array.isArray(manifest.connections) ? manifest.connections : []
|
|
56
63
|
const out = {}
|
|
@@ -103,13 +110,22 @@ function loadLocalAppServiceMocks(cwd) {
|
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
|
|
113
|
+
function resolveDevSecrets(cwd, manifest) {
|
|
114
|
+
const values = loadLocalEnv(cwd, { apply: false })
|
|
115
|
+
for (const name of Object.keys(declaredSecrets(manifest))) {
|
|
116
|
+
if (process.env[name] !== undefined) values[name] = process.env[name]
|
|
117
|
+
}
|
|
118
|
+
return values
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
function ensurePythonEnv(cwd, devDir, manifest) {
|
|
107
122
|
const hostPython = process.env.PALETTE_PYTHON || "python3"
|
|
108
123
|
const venvDir = path.join(devDir, "backend-venv")
|
|
109
124
|
const venvPython = path.join(venvDir, "bin", "python")
|
|
110
125
|
const lockPath = path.join(venvDir, ".palette-dev-deps-lock")
|
|
111
126
|
const dbDeps = needsDatabase(manifest) ? ["aiosqlite>=0.20.0", "greenlet>=3.0.0"] : []
|
|
112
|
-
const
|
|
127
|
+
const dbDriverDeps = needsDatabase(manifest) ? databaseDriverDependencies(process.env.PALETTE_DEV_DATABASE_URL) : []
|
|
128
|
+
const deps = Array.from(new Set([...pyprojectDependencies(cwd), ...dbDeps, ...dbDriverDeps, "uvicorn>=0.30.0"]))
|
|
113
129
|
const lock = JSON.stringify(deps)
|
|
114
130
|
|
|
115
131
|
if (!fs.existsSync(venvPython)) {
|
|
@@ -149,7 +165,7 @@ function writeBackendRunner(cwd, devDir, manifest, backendEntry, backendPort) {
|
|
|
149
165
|
const runner = path.join(devDir, "backend_runner.py")
|
|
150
166
|
const sdkPath = localBackendSdkPath()
|
|
151
167
|
const databasePath = path.join(devDir, `${manifest.id}.sqlite3`)
|
|
152
|
-
const devSecrets =
|
|
168
|
+
const devSecrets = resolveDevSecrets(cwd, manifest)
|
|
153
169
|
const devConnections = loadLocalConnections(cwd, manifest)
|
|
154
170
|
const devAppMocks = loadLocalAppServiceMocks(cwd)
|
|
155
171
|
const content = `from __future__ import annotations
|
|
@@ -615,6 +631,7 @@ function indexHtml(manifest) {
|
|
|
615
631
|
.palette-local-toasts { position: fixed; right: 16px; bottom: 16px; display: grid; gap: 8px; z-index: 50; }
|
|
616
632
|
.palette-local-toast { background: #1d1b18; color: white; padding: 10px 12px; font-size: 13px; box-shadow: 0 10px 30px rgba(0,0,0,.15); }
|
|
617
633
|
</style>
|
|
634
|
+
<link rel="stylesheet" href="/simulator.css" />
|
|
618
635
|
</head>
|
|
619
636
|
<body>
|
|
620
637
|
<div id="root"></div>
|
|
@@ -642,6 +659,7 @@ async function startFrontend(cwd, devDir, manifest, frontendPort, backendPort) {
|
|
|
642
659
|
if (!fs.existsSync(absEntry)) throw new Error(`frontend entry not found: ${entry}`)
|
|
643
660
|
const generatedEntry = path.join(devDir, "simulator-entry.jsx")
|
|
644
661
|
const bundlePath = path.join(devDir, "simulator.js")
|
|
662
|
+
const cssPath = path.join(devDir, "simulator.css")
|
|
645
663
|
fs.writeFileSync(generatedEntry, simulatorEntrySource(cwd, absEntry, manifest, backendPort))
|
|
646
664
|
const buildConfig = frontendBuildConfig(cwd, { ...(manifest.frontend || {}), entry })
|
|
647
665
|
|
|
@@ -658,6 +676,7 @@ async function startFrontend(cwd, devDir, manifest, frontendPort, backendPort) {
|
|
|
658
676
|
define: buildConfig.define,
|
|
659
677
|
absWorkingDir: cwd,
|
|
660
678
|
sourcemap: "inline",
|
|
679
|
+
metafile: true,
|
|
661
680
|
logLevel: "silent",
|
|
662
681
|
plugins: [
|
|
663
682
|
...buildConfig.plugins,
|
|
@@ -688,6 +707,17 @@ async function startFrontend(cwd, devDir, manifest, frontendPort, backendPort) {
|
|
|
688
707
|
res.end(fs.readFileSync(bundlePath))
|
|
689
708
|
return
|
|
690
709
|
}
|
|
710
|
+
if (url.pathname === "/simulator.css") {
|
|
711
|
+
console.log(`[pltt] frontend GET ${url.pathname} -> ${fs.existsSync(cssPath) ? 200 : 204}`)
|
|
712
|
+
if (!fs.existsSync(cssPath)) {
|
|
713
|
+
res.writeHead(204, { "Cache-Control": "no-store" })
|
|
714
|
+
res.end()
|
|
715
|
+
return
|
|
716
|
+
}
|
|
717
|
+
res.writeHead(200, { "Content-Type": "text/css; charset=utf-8", "Cache-Control": "no-store" })
|
|
718
|
+
res.end(fs.readFileSync(cssPath))
|
|
719
|
+
return
|
|
720
|
+
}
|
|
691
721
|
console.log(`[pltt] frontend GET ${url.pathname} -> 200`)
|
|
692
722
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" })
|
|
693
723
|
res.end(indexHtml(manifest))
|
|
@@ -736,4 +766,4 @@ async function startSimulator({ cwd, frontendPort, backendPort }) {
|
|
|
736
766
|
await stop()
|
|
737
767
|
}
|
|
738
768
|
|
|
739
|
-
module.exports = { startSimulator }
|
|
769
|
+
module.exports = { databaseDriverDependencies, resolveDevSecrets, startSimulator }
|
package/lib/manifest.js
CHANGED
|
@@ -53,6 +53,7 @@ const TOP_LEVEL_KEYS = new Set([
|
|
|
53
53
|
"platform_services",
|
|
54
54
|
"provides",
|
|
55
55
|
"requires",
|
|
56
|
+
"consumes",
|
|
56
57
|
])
|
|
57
58
|
|
|
58
59
|
function loadManifest(cwd) {
|
|
@@ -237,6 +238,10 @@ function isCapabilityId(value) {
|
|
|
237
238
|
return typeof value === "string" && /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(value)
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
function isBrokerTarget(value) {
|
|
242
|
+
return typeof value === "string" && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+#[A-Za-z0-9_.-]+$/.test(value)
|
|
243
|
+
}
|
|
244
|
+
|
|
240
245
|
function validateServiceRoutes(value, label, errors) {
|
|
241
246
|
if (value === undefined) return
|
|
242
247
|
if (!Array.isArray(value)) {
|
|
@@ -262,13 +267,63 @@ function validateServiceRoutes(value, label, errors) {
|
|
|
262
267
|
})
|
|
263
268
|
}
|
|
264
269
|
|
|
270
|
+
function validateServiceMethods(value, label, errors) {
|
|
271
|
+
if (value === undefined) return
|
|
272
|
+
if (!Array.isArray(value)) {
|
|
273
|
+
errors.push(`${label}.methods must be an array`)
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
const methods = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"])
|
|
277
|
+
const seen = new Set()
|
|
278
|
+
value.forEach((method, i) => {
|
|
279
|
+
const methodLabel = `${label}.methods[${i}]`
|
|
280
|
+
if (!isObject(method)) {
|
|
281
|
+
errors.push(`${methodLabel} must be an object`)
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
unknownKeys(
|
|
285
|
+
method,
|
|
286
|
+
new Set([
|
|
287
|
+
"name",
|
|
288
|
+
"scope",
|
|
289
|
+
"label",
|
|
290
|
+
"description",
|
|
291
|
+
"input_schema",
|
|
292
|
+
"input",
|
|
293
|
+
"output_schema",
|
|
294
|
+
"output",
|
|
295
|
+
"route_method",
|
|
296
|
+
"route_path",
|
|
297
|
+
]),
|
|
298
|
+
methodLabel,
|
|
299
|
+
errors,
|
|
300
|
+
)
|
|
301
|
+
if (typeof method.name !== "string" || !/^[A-Za-z0-9_.-]+$/.test(method.name)) {
|
|
302
|
+
errors.push(`${methodLabel}.name must be a broker method name`)
|
|
303
|
+
} else if (seen.has(method.name)) {
|
|
304
|
+
errors.push(`duplicate provided method name: ${method.name}`)
|
|
305
|
+
}
|
|
306
|
+
seen.add(method.name)
|
|
307
|
+
for (const key of ["scope", "label", "description", "input_schema", "input", "output_schema", "output", "route_path"]) {
|
|
308
|
+
requireString(method, key, methodLabel, errors)
|
|
309
|
+
}
|
|
310
|
+
if (method.route_method !== undefined && !methods.has(String(method.route_method).toUpperCase())) {
|
|
311
|
+
errors.push(`${methodLabel}.route_method must be one of ${Array.from(methods).join(", ")}`)
|
|
312
|
+
}
|
|
313
|
+
if (method.route_path !== undefined && (typeof method.route_path !== "string" || !method.route_path.startsWith("/"))) {
|
|
314
|
+
errors.push(`${methodLabel}.route_path must start with '/'`)
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
265
319
|
function validateProvides(value, errors) {
|
|
266
320
|
if (value === undefined) return
|
|
267
321
|
if (!isObject(value)) {
|
|
268
322
|
errors.push("provides must be an object")
|
|
269
323
|
return
|
|
270
324
|
}
|
|
271
|
-
unknownKeys(value, new Set(["services", "events"]), "provides", errors)
|
|
325
|
+
unknownKeys(value, new Set(["namespace", "services", "events"]), "provides", errors)
|
|
326
|
+
requireString(value, "namespace", "provides", errors)
|
|
272
327
|
if (value.services !== undefined) {
|
|
273
328
|
if (!Array.isArray(value.services)) {
|
|
274
329
|
errors.push("provides.services must be an array")
|
|
@@ -280,7 +335,7 @@ function validateProvides(value, errors) {
|
|
|
280
335
|
errors.push(`${label} must be an object`)
|
|
281
336
|
return
|
|
282
337
|
}
|
|
283
|
-
unknownKeys(service, new Set(["id", "version", "label", "description", "permissions", "routes"]), label, errors)
|
|
338
|
+
unknownKeys(service, new Set(["id", "version", "label", "description", "permissions", "routes", "methods"]), label, errors)
|
|
284
339
|
if (!isCapabilityId(service.id)) {
|
|
285
340
|
errors.push(`${label}.id must be a dotted lowercase capability id`)
|
|
286
341
|
} else if (seen.has(service.id)) {
|
|
@@ -302,6 +357,7 @@ function validateProvides(value, errors) {
|
|
|
302
357
|
}
|
|
303
358
|
}
|
|
304
359
|
validateServiceRoutes(service.routes, label, errors)
|
|
360
|
+
validateServiceMethods(service.methods, label, errors)
|
|
305
361
|
})
|
|
306
362
|
}
|
|
307
363
|
}
|
|
@@ -309,9 +365,29 @@ function validateProvides(value, errors) {
|
|
|
309
365
|
if (!Array.isArray(value.events)) {
|
|
310
366
|
errors.push("provides.events must be an array")
|
|
311
367
|
} else {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
368
|
+
const seen = new Set()
|
|
369
|
+
value.events.forEach((event, i) => {
|
|
370
|
+
const label = `provides.events[${i}]`
|
|
371
|
+
let topic
|
|
372
|
+
if (typeof event === "string") {
|
|
373
|
+
topic = event
|
|
374
|
+
} else if (isObject(event)) {
|
|
375
|
+
unknownKeys(event, new Set(["topic", "name", "version", "payload_schema", "schema", "description"]), label, errors)
|
|
376
|
+
topic = event.topic || event.name
|
|
377
|
+
for (const key of ["version", "payload_schema", "schema", "description"]) {
|
|
378
|
+
requireString(event, key, label, errors)
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
errors.push(`${label} must be a topic string or object`)
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
if (!isCapabilityId(topic)) {
|
|
385
|
+
errors.push(`${label}.topic must be a dotted lowercase topic`)
|
|
386
|
+
} else if (seen.has(topic)) {
|
|
387
|
+
errors.push(`duplicate provided event topic: ${topic}`)
|
|
388
|
+
}
|
|
389
|
+
seen.add(topic)
|
|
390
|
+
})
|
|
315
391
|
}
|
|
316
392
|
}
|
|
317
393
|
}
|
|
@@ -375,6 +451,44 @@ function validateRequires(value, errors) {
|
|
|
375
451
|
}
|
|
376
452
|
}
|
|
377
453
|
|
|
454
|
+
function validateConsumes(value, errors) {
|
|
455
|
+
if (value === undefined) return
|
|
456
|
+
if (!isObject(value)) {
|
|
457
|
+
errors.push("consumes must be an object")
|
|
458
|
+
return
|
|
459
|
+
}
|
|
460
|
+
unknownKeys(value, new Set(["services", "events"]), "consumes", errors)
|
|
461
|
+
for (const bucket of ["services", "events"]) {
|
|
462
|
+
if (value[bucket] === undefined) continue
|
|
463
|
+
if (!Array.isArray(value[bucket])) {
|
|
464
|
+
errors.push(`consumes.${bucket} must be an array`)
|
|
465
|
+
continue
|
|
466
|
+
}
|
|
467
|
+
const seen = new Set()
|
|
468
|
+
value[bucket].forEach((entry, i) => {
|
|
469
|
+
const label = `consumes.${bucket}[${i}]`
|
|
470
|
+
let target
|
|
471
|
+
if (typeof entry === "string") {
|
|
472
|
+
target = entry
|
|
473
|
+
} else if (isObject(entry)) {
|
|
474
|
+
unknownKeys(entry, new Set(["target", "required", "reason"]), label, errors)
|
|
475
|
+
target = entry.target
|
|
476
|
+
requireBoolean(entry, "required", label, errors)
|
|
477
|
+
requireString(entry, "reason", label, errors)
|
|
478
|
+
} else {
|
|
479
|
+
errors.push(`${label} must be a broker target string or object`)
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
if (!isBrokerTarget(target)) {
|
|
483
|
+
errors.push(`${label}.target must look like namespace/version#name`)
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
if (seen.has(target)) errors.push(`duplicate consumed ${bucket.slice(0, -1)} target: ${target}`)
|
|
487
|
+
seen.add(target)
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
378
492
|
function validateManifest(m) {
|
|
379
493
|
const errors = []
|
|
380
494
|
if (!isObject(m)) return ["manifest must be an object"]
|
|
@@ -445,6 +559,7 @@ function validateManifest(m) {
|
|
|
445
559
|
validatePlatformServices(m.platform_services, errors)
|
|
446
560
|
validateProvides(m.provides, errors)
|
|
447
561
|
validateRequires(m.requires, errors)
|
|
562
|
+
validateConsumes(m.consumes, errors)
|
|
448
563
|
|
|
449
564
|
if (m.sdk) {
|
|
450
565
|
if (!isObject(m.sdk)) errors.push("sdk must be an object")
|
package/lib/secrets.js
CHANGED
|
@@ -6,6 +6,22 @@ const path = require("path")
|
|
|
6
6
|
const LOCAL_ENV_PATH = path.join(".palette", ".env.local")
|
|
7
7
|
const EXAMPLE_ENV_PATH = path.join(".palette", ".env.example")
|
|
8
8
|
const SECRET_SCOPES = new Set(["dev", "plugin", "install", "platform"])
|
|
9
|
+
const RESERVED_AUTO_ENV_KEYS = new Set([
|
|
10
|
+
"CI",
|
|
11
|
+
"HOME",
|
|
12
|
+
"HOST",
|
|
13
|
+
"HOSTNAME",
|
|
14
|
+
"LOGNAME",
|
|
15
|
+
"NODE_ENV",
|
|
16
|
+
"OLDPWD",
|
|
17
|
+
"PATH",
|
|
18
|
+
"PORT",
|
|
19
|
+
"PWD",
|
|
20
|
+
"SHELL",
|
|
21
|
+
"TERM",
|
|
22
|
+
"TMPDIR",
|
|
23
|
+
"USER",
|
|
24
|
+
])
|
|
9
25
|
|
|
10
26
|
function parseDotEnv(src) {
|
|
11
27
|
const values = {}
|
|
@@ -31,6 +47,50 @@ function readDotEnvFile(filePath) {
|
|
|
31
47
|
return parseDotEnv(fs.readFileSync(filePath, "utf8"))
|
|
32
48
|
}
|
|
33
49
|
|
|
50
|
+
function unique(items) {
|
|
51
|
+
return Array.from(new Set(items.filter(Boolean)))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function envFileNames(environment) {
|
|
55
|
+
return unique([
|
|
56
|
+
".env",
|
|
57
|
+
".env.local",
|
|
58
|
+
environment ? `.env.${environment}` : null,
|
|
59
|
+
environment ? `.env.${environment}.local` : null,
|
|
60
|
+
])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function loadRootEnvFiles(cwd, { environment } = {}) {
|
|
64
|
+
const values = {}
|
|
65
|
+
const files = []
|
|
66
|
+
for (const name of envFileNames(environment || process.env.PALETTE_ENV)) {
|
|
67
|
+
const filePath = path.join(cwd, name)
|
|
68
|
+
if (!fs.existsSync(filePath)) continue
|
|
69
|
+
Object.assign(values, readDotEnvFile(filePath))
|
|
70
|
+
files.push(name)
|
|
71
|
+
}
|
|
72
|
+
return { values, files }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function loadLocalEnvDetails(cwd, { apply = true, environment, includePalette = true } = {}) {
|
|
76
|
+
const root = loadRootEnvFiles(cwd, { environment })
|
|
77
|
+
const values = { ...root.values }
|
|
78
|
+
const files = [...root.files]
|
|
79
|
+
if (includePalette) {
|
|
80
|
+
const palettePath = path.join(cwd, LOCAL_ENV_PATH)
|
|
81
|
+
if (fs.existsSync(palettePath)) {
|
|
82
|
+
Object.assign(values, readDotEnvFile(palettePath))
|
|
83
|
+
files.push(LOCAL_ENV_PATH)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (apply) {
|
|
87
|
+
for (const [key, value] of Object.entries(values)) {
|
|
88
|
+
if (process.env[key] === undefined) process.env[key] = value
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { values, files }
|
|
92
|
+
}
|
|
93
|
+
|
|
34
94
|
function formatDotEnvValue(value) {
|
|
35
95
|
if (value === undefined || value === null) return ""
|
|
36
96
|
const str = String(value)
|
|
@@ -132,14 +192,25 @@ function initLocalEnv(cwd, manifest, { overwrite = false } = {}) {
|
|
|
132
192
|
return { localPath, examplePath, declared }
|
|
133
193
|
}
|
|
134
194
|
|
|
135
|
-
function loadLocalEnv(cwd, { apply = true } = {}) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
195
|
+
function loadLocalEnv(cwd, { apply = true, environment, includePalette = true } = {}) {
|
|
196
|
+
return loadLocalEnvDetails(cwd, { apply, environment, includePalette }).values
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function isPublicEnvKey(key) {
|
|
200
|
+
return key.startsWith("NEXT_PUBLIC_")
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isReservedAutoEnvKey(key) {
|
|
204
|
+
return (
|
|
205
|
+
RESERVED_AUTO_ENV_KEYS.has(key) ||
|
|
206
|
+
key.startsWith("PALETTE_") ||
|
|
207
|
+
key.startsWith("npm_") ||
|
|
208
|
+
key.startsWith("NPM_")
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function canAutoUploadEnvKey(key) {
|
|
213
|
+
return /^[A-Z_][A-Z0-9_]*$/.test(key) && !isPublicEnvKey(key) && !isReservedAutoEnvKey(key)
|
|
143
214
|
}
|
|
144
215
|
|
|
145
216
|
function redactValue(value) {
|
|
@@ -156,9 +227,13 @@ module.exports = {
|
|
|
156
227
|
declaredSecrets,
|
|
157
228
|
ensureGitignore,
|
|
158
229
|
initLocalEnv,
|
|
230
|
+
loadLocalEnvDetails,
|
|
159
231
|
loadLocalEnv,
|
|
160
232
|
parseDotEnv,
|
|
161
233
|
readDotEnvFile,
|
|
162
234
|
redactValue,
|
|
163
235
|
secretsForScope,
|
|
236
|
+
canAutoUploadEnvKey,
|
|
237
|
+
isPublicEnvKey,
|
|
238
|
+
isReservedAutoEnvKey,
|
|
164
239
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@palettelab/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.50",
|
|
4
4
|
"description": "Developer CLI for building Palette platform plugins — no platform source access required.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pltt": "bin/pltt.js"
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"node": ">=18"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"esbuild": "^0.24.0"
|
|
26
|
+
"esbuild": "^0.24.0",
|
|
27
|
+
"postcss": "^8.5.15",
|
|
28
|
+
"postcss-selector-parser": "^7.1.1"
|
|
27
29
|
},
|
|
28
30
|
"publishConfig": {
|
|
29
31
|
"registry": "https://registry.npmjs.org"
|