@palettelab/cli 0.3.52 → 0.3.54

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 CHANGED
@@ -502,11 +502,14 @@ work without Docker or platform source. The terminal streams local frontend
502
502
  requests, frontend rebuilds, and backend process output while `pltt dev` is
503
503
  running.
504
504
 
505
- The simulator also provides the same language fields that Palette OS provides:
506
- `language`, `fallbackLanguage`, `supportedLanguages`, and `setLanguage()`.
507
- Generated frontend templates include app-owned `frontend/src/translations.ts`
508
- files wired through `usePluginTranslations()`, so apps can switch when the OS
509
- language changes without storing copy in the platform.
505
+ The simulator also provides the same OS context fields that Palette OS provides:
506
+ `language`, `fallbackLanguage`, `supportedLanguages`, `setLanguage()`,
507
+ `colorMode`, and `setColorMode()`. Generated frontend templates include
508
+ app-owned `frontend/src/translations.ts` files wired through
509
+ `usePluginTranslations()`, so apps can switch when the OS language changes
510
+ without storing copy in the platform. Apps can read `usePlatform().colorMode`
511
+ or listen for the `palette:theme-change` browser event to react when Palette OS
512
+ changes between light and dark mode.
510
513
 
511
514
  For Python apps with database tables, `ctx.db` is an async SQLAlchemy session in
512
515
  local dev. The simulator imports `backend/api/models.py` when present and creates
@@ -783,6 +786,10 @@ If no plugin ID is provided, the CLI uses the current `palette-plugin.json` or `
783
786
  Inspect and generate OS-broker service integrations declared through
784
787
  `provides` and `consumes` in `palette-plugin.json`.
785
788
 
789
+ Use this command family when one Palette app needs governed data, approvals, or
790
+ events from another app. The CLI keeps the app manifest, local mocks, provider
791
+ schema files, and generated clients aligned with the broker contract.
792
+
786
793
  ```bash
787
794
  pltt init employee-management --template provider-app
788
795
  pltt init leave-management --template consumer-app
@@ -810,6 +817,38 @@ Generated TypeScript clients use the SDK's default `palette` client. Generated
810
817
  Python clients call `palette_sdk.services(ctx)`, so backend routes still pass
811
818
  through Palette's broker permission and install checks.
812
819
 
820
+ Recommended workflow:
821
+
822
+ 1. Provider app: implement backend methods with `@service(...)` and declare
823
+ event topics in `provides.events`.
824
+ 2. Provider app: run `pltt services sync` or `pltt services scaffold <method>`
825
+ to keep manifest methods and schema files current.
826
+ 3. Consumer app: run `pltt services add <target>` for each consumed service or
827
+ event, including a `--reason` for review and install UI.
828
+ 4. Consumer app: use `pltt services mock <target>` and `pltt services test
829
+ --offline` during local development.
830
+ 5. Consumer app: use `pltt services pull --env staging` to generate typed
831
+ clients from the live `/api/v1/os-broker/schemas` endpoint.
832
+ 6. Run `pltt test` before publish; contract checks include local
833
+ `provides`/`consumes`, mocks, schemas, and dependency-policy validation.
834
+
835
+ Runtime behavior:
836
+
837
+ - Service calls go through `POST /api/v1/os-broker/dispatch`.
838
+ - Event publishes go through `POST /api/v1/os-broker/events/emit`.
839
+ - Event subscriptions use `GET /api/v1/os-broker/events/stream`.
840
+ - Catalog and generated client metadata come from `/api/v1/os-broker/catalog`
841
+ and `/api/v1/os-broker/schemas`.
842
+ - Installs use `/api/v1/app-installs/<app_id>/dependency-plan` and can include
843
+ required provider apps with `?include_dependencies=true`.
844
+ - Org owners/admins can revoke or restore individual cross-app grants from
845
+ Settings > Apps; the broker checks those grants on every call and stream.
846
+
847
+ Provider methods can use inline JSON Schema objects or schema file paths in
848
+ `input_schema`, `input`, `output_schema`, and `output`. Event topics can use
849
+ `schema` or `payload_schema`. The CLI scaffolds schema files by default because
850
+ they are easier to review and reuse for generated clients.
851
+
813
852
  ## Global Flags
814
853
 
815
854
  - `--json` emits machine-readable output for `package`, `publish`, `status`, `logs`, `test`, and `version`.
@@ -693,7 +693,18 @@ token = await ctx.connections.access_token("google_calendar")
693
693
  ## 11. App-To-App Services
694
694
 
695
695
  Apps can expose governed broker services and events, then consume them from
696
- other installed apps without knowing another app's URL.
696
+ other installed apps without knowing another app's URL. The broker is the only
697
+ supported integration path for cross-app business data. Do not read another
698
+ app's database tables and do not call another app's private backend route
699
+ directly.
700
+
701
+ Palette enforces the app-to-app contract in four places:
702
+
703
+ - Manifest review: providers declare `provides`, consumers declare `consumes`.
704
+ - Install: required provider apps are resolved before the consumer is activated.
705
+ - Org grants: owners/admins can allow or revoke each consumed service/event.
706
+ - Runtime: every service call, event emit, and event stream is checked against
707
+ same-org install state, `consumes`, grants, and JSON Schemas.
697
708
 
698
709
  Provider apps declare a namespace and callable methods with `provides`:
699
710
 
@@ -705,25 +716,40 @@ Provider apps declare a namespace and callable methods with `provides`:
705
716
  {
706
717
  "id": "hr.directory",
707
718
  "methods": [
708
- { "name": "approvalChain.get", "input_schema": "schemas/approval-chain.input.json" }
719
+ {
720
+ "name": "approvalChain.get",
721
+ "input_schema": "schemas/approval-chain.input.json",
722
+ "output_schema": "schemas/approval-chain.output.json"
723
+ }
709
724
  ]
710
725
  }
711
726
  ],
712
- "events": [{ "topic": "hierarchy.updated" }]
727
+ "events": [
728
+ {
729
+ "topic": "hierarchy.updated",
730
+ "schema": "schemas/hierarchy-updated.json"
731
+ }
732
+ ]
713
733
  }
714
734
  }
715
735
  ```
716
736
 
717
- Expose the handler with `@service`:
737
+ Expose the handler with `@service`. The platform registers handlers when the
738
+ plugin loads and injects a full `PluginContext` at dispatch time:
718
739
 
719
740
  ```python
720
741
  from palette_sdk import PluginContext, service
721
742
 
722
- @service("approvalChain.get")
743
+ @service("approvalChain.get", scope="members:read")
723
744
  async def approval_chain(ctx: PluginContext, payload: dict) -> dict:
724
745
  return {"approvers": [{"user_id": ctx.user_id, "step": 1}]}
725
746
  ```
726
747
 
748
+ If a method also declares `route_method` and `route_path`, Palette can fall back
749
+ to signed internal HTTP transport when no in-process handler is registered. New
750
+ provider apps should prefer `@service(...)` because it avoids URL coupling and
751
+ keeps dispatch inside the broker lifecycle.
752
+
727
753
  Consumers declare qualified targets with `consumes`:
728
754
 
729
755
  ```json
@@ -746,8 +772,11 @@ Consumers declare qualified targets with `consumes`:
746
772
  The CLI can update the manifest and generate typed clients for those targets:
747
773
 
748
774
  ```bash
775
+ pltt services sync
749
776
  pltt services add hr/v1#approvalChain.get --reason "Route leave approvals through HR"
750
777
  pltt services add hr/v1#hierarchy.updated --event --optional
778
+ pltt services mock hr/v1#approvalChain.get
779
+ pltt services test --offline
751
780
  pltt services pull --env staging
752
781
  ```
753
782
 
@@ -764,12 +793,42 @@ from palette_sdk import services
764
793
 
765
794
  chain = await services(ctx).call("hr/v1#approvalChain.get", {"user_id": ctx.user_id})
766
795
  await ctx.events.emit("leave/v1#leave.requested", {"approval_chain": chain})
796
+ await ctx.events.emit_durable("leave/v1#leave.requested", {"approval_chain": chain})
767
797
  ```
768
798
 
769
799
  At install time, Palette checks required `consumes` targets and can show the
770
800
  provider apps that must also be installed. The org-level grant is saved on the
771
801
  install and the broker checks it on every call, emit, or event stream.
772
802
 
803
+ Useful platform endpoints when building or debugging integrations:
804
+
805
+ ```text
806
+ GET /api/v1/app-installs/{app_id}/dependency-plan
807
+ POST /api/v1/app-installs/{app_id}?include_dependencies=true
808
+ GET /api/v1/app-installs/{app_id}/cross-app-grants
809
+ PATCH /api/v1/app-installs/{app_id}/cross-app-grants
810
+ GET /api/v1/os-broker/catalog
811
+ GET /api/v1/os-broker/schemas?targets=hr/v1#approvalChain.get
812
+ GET /api/v1/os-broker/audit?app_id=leave-management
813
+ ```
814
+
815
+ Local simulator workflows can use `.palette/app-services.local.json` mocks.
816
+ The mock command writes the file for you:
817
+
818
+ ```bash
819
+ pltt services mock hr/v1#approvalChain.get
820
+ ```
821
+
822
+ Example mock:
823
+
824
+ ```json
825
+ {
826
+ "hr/v1#approvalChain.get": {
827
+ "approvers": [{ "user_id": "manager-1", "step": 1 }]
828
+ }
829
+ }
830
+ ```
831
+
773
832
  App storage is separate from Data Rooms. Use `ctx.storage` and `palette.storage` for app-owned files that go directly to the OS-configured storage backend, currently GCS in hosted environments. Use `ctx.data_rooms` or `palette.dataRooms` only when the file should be visible and governed as a Data Room document.
774
833
 
775
834
  Palette scopes storage the same way. Files written through `ctx.storage` or the
@@ -8,7 +8,7 @@ const { watchFrontend } = require("../bundler")
8
8
  const { parseFlags, resolveEnvironment } = require("../environments")
9
9
  const { resolveDevPorts } = require("../ports")
10
10
  const { startSimulator } = require("../dev-simulator")
11
- const { loadLocalEnv } = require("../secrets")
11
+ const { loadLocalEnvDetails } = require("../secrets")
12
12
  const buildCommand = require("./build")
13
13
  const publish = require("./publish")
14
14
  const logs = require("./logs")
@@ -98,11 +98,39 @@ function lintMigrationsForDev(cwd, manifest) {
98
98
  process.exit(1)
99
99
  }
100
100
 
101
+ function formatEnvValue(value) {
102
+ if (value === undefined || value === null) return ""
103
+ const str = String(value)
104
+ if (!str) return ""
105
+ if (/[\s#"'\\]/.test(str)) return JSON.stringify(str)
106
+ return str
107
+ }
108
+
109
+ function writePlatformEnvFile(cwd, localEnv) {
110
+ const dir = path.join(cwd, ".palette")
111
+ fs.mkdirSync(dir, { recursive: true })
112
+ const envPath = path.join(dir, "platform.env")
113
+ const values = {}
114
+ for (const [key, value] of Object.entries(localEnv?.values || {})) {
115
+ values[key] = process.env[key] !== undefined ? process.env[key] : value
116
+ }
117
+ const lines = [
118
+ "# Generated by pltt dev --platform from local env files.",
119
+ "# Do not commit this file.",
120
+ "",
121
+ ]
122
+ for (const [key, value] of Object.entries(values).sort(([a], [b]) => a.localeCompare(b))) {
123
+ lines.push(`${key}=${formatEnvValue(value)}`)
124
+ }
125
+ fs.writeFileSync(envPath, `${lines.join("\n")}\n`)
126
+ return envPath
127
+ }
128
+
101
129
  async function run(args, { cwd }) {
102
130
  const { flags, rest } = parseFlags(args)
103
131
  const cloud = rest.includes("--cloud") || rest.includes("--sandbox")
104
132
  const platform = rest.includes("--platform")
105
- loadLocalEnv(cwd)
133
+ const localEnv = loadLocalEnvDetails(cwd, { environment: flags.env })
106
134
  if (cloud) {
107
135
  const json = args.includes("--json")
108
136
  const publishArgs = ["--publish-type", "preview"]
@@ -199,6 +227,9 @@ async function run(args, { cwd }) {
199
227
  }
200
228
  console.log(`[pltt] frontend: http://localhost:${frontendPort}/apps/${pluginId}`)
201
229
  console.log(`[pltt] backend: http://localhost:${backendPort}/api/v1/plugins/${pluginId}`)
230
+ if (localEnv.files.length) {
231
+ console.log(`[pltt] env files: ${localEnv.files.join(", ")}`)
232
+ }
202
233
 
203
234
  // Pre-pull so we can give a useful error if the image isn't reachable
204
235
  // (common cause: maintainer hasn't pushed it yet, or `docker login ghcr.io`
@@ -210,11 +241,13 @@ async function run(args, { cwd }) {
210
241
  process.exit(1)
211
242
  }
212
243
 
244
+ const platformEnvFile = writePlatformEnvFile(cwd, localEnv)
213
245
  const env = {
214
246
  ...process.env,
215
247
  PALETTE_DEV_IMAGE: DEFAULT_IMAGE,
216
248
  PALETTE_ACTIVE_PLUGIN: pluginId,
217
249
  PALETTE_PLUGIN_DIR: cwd,
250
+ PALETTE_PLUGIN_ENV_FILE: platformEnvFile,
218
251
  PALETTE_FRONTEND_PORT: frontendPort,
219
252
  PALETTE_BACKEND_PORT: backendPort,
220
253
  }
@@ -228,6 +228,14 @@ function scopesOf(spec) {
228
228
  return ["dev"]
229
229
  }
230
230
 
231
+ function withPluginScope(spec) {
232
+ const scopes = Array.from(new Set([...scopesOf(spec), "plugin"]))
233
+ return {
234
+ ...spec,
235
+ scope: scopes.length === 1 ? scopes[0] : scopes,
236
+ }
237
+ }
238
+
231
239
  function cloneManifest(manifest) {
232
240
  return JSON.parse(JSON.stringify(manifest))
233
241
  }
@@ -235,12 +243,6 @@ function cloneManifest(manifest) {
235
243
  function collectPluginSecrets(cwd, manifest, env, flags, log, localEnv) {
236
244
  const declared = declaredSecrets(manifest)
237
245
  const pluginSecrets = Object.entries(declared).filter(([, spec]) => spec.scope.includes("plugin"))
238
- const devRequired = Object.entries(declared).filter(([, spec]) => spec.scope.includes("dev") && spec.required)
239
- if (devRequired.length) {
240
- log(
241
- `[pltt] dev-only secrets are not uploaded: ${devRequired.map(([name]) => name).join(", ")}`,
242
- )
243
- }
244
246
 
245
247
  let fileValues = {}
246
248
  if (flags.secretsFile) {
@@ -281,7 +283,14 @@ function collectPluginSecrets(cwd, manifest, env, flags, log, localEnv) {
281
283
  }
282
284
  const explicitSpec = effectiveManifest.secrets[name]
283
285
  if (explicitSpec) {
284
- if (scopesOf(explicitSpec).includes("plugin")) values[name] = fileValues[name] ?? process.env[name] ?? localValues[name]
286
+ const value = fileValues[name] ?? process.env[name] ?? localValues[name]
287
+ if (scopesOf(explicitSpec).includes("plugin")) {
288
+ values[name] = value
289
+ } else if (canAutoUploadEnvKey(name)) {
290
+ effectiveManifest.secrets[name] = withPluginScope(explicitSpec)
291
+ values[name] = value
292
+ autoUploaded.push(name)
293
+ }
285
294
  continue
286
295
  }
287
296
  if (isReservedAutoEnvKey(name)) {
@@ -576,6 +576,21 @@ function normalizePaletteLanguage(language, fallback = "en") {
576
576
  return value ? value.split("-")[0] : fallback
577
577
  }
578
578
 
579
+ function normalizePaletteColorMode(mode, fallback = "light") {
580
+ return mode === "dark" ? "dark" : fallback
581
+ }
582
+
583
+ function detectPaletteColorMode() {
584
+ return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light"
585
+ }
586
+
587
+ function applyPaletteColorMode(mode) {
588
+ const normalized = normalizePaletteColorMode(mode)
589
+ document.documentElement.classList.toggle("dark", normalized === "dark")
590
+ document.documentElement.style.colorScheme = normalized
591
+ window.dispatchEvent(new CustomEvent("palette:theme-change", { detail: { colorMode: normalized } }))
592
+ }
593
+
579
594
  function Toasts() {
580
595
  const [items, setItems] = React.useState([])
581
596
  React.useEffect(() => {
@@ -594,6 +609,7 @@ function Toasts() {
594
609
 
595
610
  function Shell() {
596
611
  const [language, updateLanguage] = React.useState(() => normalizePaletteLanguage(navigator.language))
612
+ const [colorMode, updateColorMode] = React.useState(() => detectPaletteColorMode())
597
613
  const platform = React.useMemo(() => ({
598
614
  ...basePlatform,
599
615
  language,
@@ -605,18 +621,34 @@ function Shell() {
605
621
  document.documentElement.lang = normalized
606
622
  window.dispatchEvent(new CustomEvent("palette:language-change", { detail: { language: normalized } }))
607
623
  },
608
- }), [language])
624
+ colorMode,
625
+ setColorMode: (nextMode) => {
626
+ updateColorMode(normalizePaletteColorMode(nextMode))
627
+ },
628
+ }), [language, colorMode])
609
629
 
610
630
  React.useEffect(() => {
611
631
  document.documentElement.lang = language
612
632
  }, [language])
613
633
 
634
+ React.useEffect(() => {
635
+ applyPaletteColorMode(colorMode)
636
+ }, [colorMode])
637
+
614
638
  React.useEffect(() => {
615
639
  const onLanguageChange = () => updateLanguage(normalizePaletteLanguage(navigator.language))
616
640
  window.addEventListener("languagechange", onLanguageChange)
617
641
  return () => window.removeEventListener("languagechange", onLanguageChange)
618
642
  }, [])
619
643
 
644
+ React.useEffect(() => {
645
+ const media = window.matchMedia?.("(prefers-color-scheme: dark)")
646
+ if (!media) return
647
+ const onColorModeChange = () => updateColorMode(detectPaletteColorMode())
648
+ media.addEventListener?.("change", onColorModeChange)
649
+ return () => media.removeEventListener?.("change", onColorModeChange)
650
+ }, [])
651
+
620
652
  return React.createElement(PluginProvider, { value: platform },
621
653
  React.createElement("main", { className: "palette-local-shell" },
622
654
  React.createElement(Plugin, { platform }),
package/lib/manifest.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require("fs")
4
4
  const path = require("path")
5
- const { SECRET_SCOPES } = require("./secrets")
5
+ const { ENV_KEY_PATTERN, SECRET_SCOPES } = require("./secrets")
6
6
 
7
7
  const MANIFEST_FILE = "palette-plugin.json"
8
8
  const SUPPORTED_MANIFEST_VERSIONS = ["1"]
@@ -127,8 +127,8 @@ function validateSecrets(value, errors) {
127
127
  const allowed = new Set(["scope", "required", "label", "help", "validate"])
128
128
  for (const [name, spec] of Object.entries(value)) {
129
129
  const label = `secrets.${name}`
130
- if (!/^[A-Z_][A-Z0-9_]*$/.test(name)) {
131
- errors.push(`${label} must be an uppercase environment-style name`)
130
+ if (!ENV_KEY_PATTERN.test(name)) {
131
+ errors.push(`${label} must be a valid environment variable name`)
132
132
  }
133
133
  if (!isObject(spec)) {
134
134
  errors.push(`${label} must be an object`)
package/lib/secrets.js CHANGED
@@ -6,6 +6,7 @@ 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 ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/
9
10
  const RESERVED_AUTO_ENV_KEYS = new Set([
10
11
  "CI",
11
12
  "HOME",
@@ -162,7 +163,7 @@ function declaredSecrets(manifest) {
162
163
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {}
163
164
  const out = {}
164
165
  for (const [name, meta] of Object.entries(raw)) {
165
- if (!/^[A-Z_][A-Z0-9_]*$/.test(name)) continue
166
+ if (!ENV_KEY_PATTERN.test(name)) continue
166
167
  const item = meta && typeof meta === "object" && !Array.isArray(meta) ? meta : {}
167
168
  out[name] = {
168
169
  ...item,
@@ -210,7 +211,7 @@ function isReservedAutoEnvKey(key) {
210
211
  }
211
212
 
212
213
  function canAutoUploadEnvKey(key) {
213
- return /^[A-Z_][A-Z0-9_]*$/.test(key) && !isPublicEnvKey(key) && !isReservedAutoEnvKey(key)
214
+ return ENV_KEY_PATTERN.test(key) && !isPublicEnvKey(key) && !isReservedAutoEnvKey(key)
214
215
  }
215
216
 
216
217
  function redactValue(value) {
@@ -222,6 +223,7 @@ function redactValue(value) {
222
223
 
223
224
  module.exports = {
224
225
  EXAMPLE_ENV_PATH,
226
+ ENV_KEY_PATTERN,
225
227
  LOCAL_ENV_PATH,
226
228
  SECRET_SCOPES,
227
229
  declaredSecrets,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@palettelab/cli",
3
- "version": "0.3.52",
3
+ "version": "0.3.54",
4
4
  "description": "Developer CLI for building Palette platform plugins — no platform source access required.",
5
5
  "bin": {
6
6
  "pltt": "bin/pltt.js"
@@ -9,6 +9,8 @@
9
9
  services:
10
10
  platform:
11
11
  image: ${PALETTE_DEV_IMAGE:-ghcr.io/palette-lab/platform-dev:latest}
12
+ env_file:
13
+ - ${PALETTE_PLUGIN_ENV_FILE:-/dev/null}
12
14
  ports:
13
15
  - "${PALETTE_FRONTEND_PORT:-7321}:3000"
14
16
  - "${PALETTE_BACKEND_PORT:-8732}:8000"
@@ -25,8 +27,8 @@ services:
25
27
  STORAGE_BACKEND: "local"
26
28
  LOCAL_STORAGE_DIR: "/srv/storage"
27
29
  # Disable optional features that need real credentials
28
- RAG_ENABLED: "false"
29
- OPENAI_API_KEY: ""
30
+ RAG_ENABLED: "${RAG_ENABLED:-false}"
31
+ OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
30
32
  volumes:
31
33
  - "${PALETTE_PLUGIN_DIR}:/plugins/${PALETTE_ACTIVE_PLUGIN}"
32
34
  depends_on:
@@ -4,7 +4,7 @@
4
4
  "private": true,
5
5
  "description": "A Palette platform plugin",
6
6
  "dependencies": {
7
- "@palettelab/sdk": "^0.1.22"
7
+ "@palettelab/sdk": "^0.1.24"
8
8
  },
9
9
  "devDependencies": {
10
10
  "typescript": "^5.0.0",
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@palettelab/sdk": "^0.1.22",
5
+ "@palettelab/sdk": "^0.1.24",
6
6
  "react": "^19.0.0",
7
7
  "react-dom": "^19.0.0"
8
8
  }
@@ -3,7 +3,7 @@
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
5
  "dependencies": {
6
- "@palettelab/sdk": "^0.1.22",
6
+ "@palettelab/sdk": "^0.1.24",
7
7
  "react": "^19.0.0"
8
8
  }
9
9
  }
@@ -2,5 +2,5 @@
2
2
  "name": "my-db-plugin",
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
- "dependencies": { "@palettelab/sdk": "^0.1.22", "react": "^19.0.0" }
5
+ "dependencies": { "@palettelab/sdk": "^0.1.24", "react": "^19.0.0" }
6
6
  }
@@ -2,5 +2,5 @@
2
2
  "name": "my-external-svc",
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
- "dependencies": { "@palettelab/sdk": "^0.1.22", "react": "^19.0.0" }
5
+ "dependencies": { "@palettelab/sdk": "^0.1.24", "react": "^19.0.0" }
6
6
  }
@@ -3,7 +3,7 @@
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
5
  "dependencies": {
6
- "@palettelab/sdk": "^0.1.22",
6
+ "@palettelab/sdk": "^0.1.24",
7
7
  "react": "^19.0.0"
8
8
  }
9
9
  }
@@ -3,7 +3,7 @@
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
5
  "dependencies": {
6
- "@palettelab/sdk": "^0.1.22",
6
+ "@palettelab/sdk": "^0.1.24",
7
7
  "react": "^19.0.0"
8
8
  },
9
9
  "devDependencies": {
@@ -3,7 +3,7 @@
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
5
  "dependencies": {
6
- "@palettelab/sdk": "^0.1.22",
6
+ "@palettelab/sdk": "^0.1.24",
7
7
  "react": "^19.0.0"
8
8
  },
9
9
  "devDependencies": {
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@palettelab/sdk": "^0.1.22",
5
+ "@palettelab/sdk": "^0.1.24",
6
6
  "react": "^19.0.0"
7
7
  }
8
8
  }