@palettelab/cli 0.3.25 → 0.3.27

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.
Files changed (30) hide show
  1. package/README.md +21 -3
  2. package/backend-sdk/palette_sdk/__init__.py +1 -1
  3. package/backend-sdk/palette_sdk/data_rooms.py +17 -0
  4. package/backend-sdk/palette_sdk/db/alembic_env.py +4 -2
  5. package/backend-sdk/palette_sdk/plugin_context.py +6 -0
  6. package/backend-sdk/pyproject.toml +1 -1
  7. package/docs/python-backend-sdk.md +641 -0
  8. package/lib/commands/build.js +4 -0
  9. package/lib/dev-simulator.js +30 -1
  10. package/package.json +2 -1
  11. package/template-fallback/frontend/src/index.tsx +35 -23
  12. package/template-fallback/frontend/src/translations.ts +36 -0
  13. package/template-fallback/package.json +1 -1
  14. package/template-fallback/palette-plugin.json +1 -1
  15. package/template-fallback/templates/dashboard/frontend/src/index.tsx +5 -3
  16. package/template-fallback/templates/dashboard/frontend/src/translations.ts +12 -0
  17. package/template-fallback/templates/dashboard/package.json +1 -1
  18. package/template-fallback/templates/dashboard/palette-plugin.json +1 -1
  19. package/template-fallback/templates/database/frontend/src/index.tsx +13 -12
  20. package/template-fallback/templates/database/frontend/src/translations.ts +30 -0
  21. package/template-fallback/templates/database/package.json +1 -1
  22. package/template-fallback/templates/database/palette-plugin.json +1 -1
  23. package/template-fallback/templates/external-service/frontend/src/index.tsx +5 -3
  24. package/template-fallback/templates/external-service/frontend/src/translations.ts +12 -0
  25. package/template-fallback/templates/external-service/package.json +1 -1
  26. package/template-fallback/templates/external-service/palette-plugin.json +1 -1
  27. package/template-fallback/templates/frontend-only/frontend/src/index.tsx +6 -3
  28. package/template-fallback/templates/frontend-only/frontend/src/translations.ts +12 -0
  29. package/template-fallback/templates/frontend-only/package.json +1 -1
  30. package/template-fallback/templates/frontend-only/palette-plugin.json +1 -1
package/README.md CHANGED
@@ -195,6 +195,10 @@ internal platform parity checks with `pltt dev --platform`.
195
195
 
196
196
  ## App-Owned Data, Migrations, And Python Backends
197
197
 
198
+ For a detailed Python backend SDK guide with route examples, app-owned data,
199
+ migrations, Data Rooms, config, secrets, lifecycle hooks, and hosted sandbox
200
+ testing, see [Python Backend SDK Developer Guide](./docs/python-backend-sdk.md).
201
+
198
202
  Palette apps can own their own Python backend, database tables, migrations, and
199
203
  organization-scoped data. The generated database template uses this structure:
200
204
 
@@ -267,7 +271,7 @@ Backend SDK features for app-owned data:
267
271
  - `PluginContext` exposes `user_id`, `organization_id`, `plugin_id`, `permissions`, `config`, `storage`, and `ctx.db`.
268
272
  - `ctx.repo(Model)` gives org-safe CRUD helpers for app tables.
269
273
  - `ctx.data_rooms` gives backend access to Palette Data Rooms without importing platform internals.
270
- - `ctx.has_permission("...")` checks declared permissions.
274
+ - `ctx.has_permission("...")`, `ctx.has_any_permission([...])`, and `ctx.has_all_permissions([...])` check declared permissions.
271
275
  - `ctx.config_value("key")` and `ctx.require_config("key")` read app install/config values.
272
276
  - `ctx.secret("KEY")` reads app secrets from config or environment variables.
273
277
  - `LifecycleHooks` lets apps define install/update/enable/disable/uninstall hooks.
@@ -290,6 +294,14 @@ async def sync_invoices(ctx: PluginContext = Depends(get_plugin_context)):
290
294
  folder_id=folder["id"] if folder else None,
291
295
  )
292
296
  content = await ctx.data_rooms.read_file_bytes(file["id"]) if file else None
297
+ if folder:
298
+ await ctx.data_rooms.upload_file(
299
+ room["id"],
300
+ "summary.txt",
301
+ b"Generated by my app",
302
+ folder_id=folder["id"],
303
+ content_type="text/plain",
304
+ )
293
305
  return {"room": room, "folder": folder, "bytes": len(content or b"")}
294
306
  ```
295
307
 
@@ -358,13 +370,19 @@ By default this starts:
358
370
 
359
371
  - A small local app shell on the first available port starting at http://localhost:3000
360
372
  - A local FastAPI backend runner on the first available port starting at http://localhost:8000
361
- - A mock Palette platform context for `usePlatform()`, toasts, org/user data, and authenticated API calls
373
+ - A mock Palette platform context for `usePlatform()`, OS language, toasts, org/user data, and authenticated API calls
362
374
  - A plugin-local SQLite database under `.palette/dev/` when `capabilities.database` or `database` is enabled
363
375
 
364
376
  Your frontend entry is bundled and watched. Your backend entry is loaded under
365
377
  `/api/v1/plugins/<your-id>/*` with a dev `PluginContext`, so normal SDK calls
366
378
  work without Docker or platform source.
367
379
 
380
+ The simulator also provides the same language fields that Palette OS provides:
381
+ `language`, `fallbackLanguage`, `supportedLanguages`, and `setLanguage()`.
382
+ Generated frontend templates include app-owned `frontend/src/translations.ts`
383
+ files wired through `usePluginTranslations()`, so apps can switch when the OS
384
+ language changes without storing copy in the platform.
385
+
368
386
  For Python apps with database tables, `ctx.db` is an async SQLAlchemy session in
369
387
  local dev. The simulator imports `backend/api/models.py` when present and creates
370
388
  tables from `palette_sdk.db.PluginBase.metadata`. Production installs still use
@@ -487,7 +505,7 @@ If no publish ID is provided, the CLI uses `.palette/last-publish.json` when ava
487
505
  Fetch or stream telemetry events for a plugin.
488
506
 
489
507
  ```bash
490
- pltt logs hello-sdk --env staging
508
+ pltt logs my-plugin --env staging
491
509
  pltt logs --tail 100
492
510
  pltt logs --follow
493
511
  pltt logs --json
@@ -51,4 +51,4 @@ __all__ = [
51
51
  "route_permission_issues",
52
52
  ]
53
53
 
54
- __version__ = "0.1.5"
54
+ __version__ = "0.1.6"
@@ -108,3 +108,20 @@ class DataRoomsClient:
108
108
 
109
109
  async def read_file_bytes(self, file_id: int) -> bytes:
110
110
  return await self._require_service().read_file_bytes(file_id)
111
+
112
+ async def upload_file(
113
+ self,
114
+ room_id: int,
115
+ filename: str,
116
+ content: bytes,
117
+ *,
118
+ folder_id: int | None = None,
119
+ content_type: str | None = None,
120
+ ) -> dict[str, Any]:
121
+ return await self._require_service().upload_file(
122
+ room_id,
123
+ filename,
124
+ content,
125
+ folder_id=folder_id,
126
+ content_type=content_type,
127
+ )
@@ -11,8 +11,10 @@ invokes Alembic:
11
11
  - ``PALETTE_PLUGIN_ID`` — plugin id; determines the target schema
12
12
  (``app_<sanitized_id>``) and version table schema.
13
13
  - ``PALETTE_DB_URL`` — SQLAlchemy async URL for the plugin's DB.
14
- - ``PALETTE_PLUGIN_ROLE`` (optional) — role the migration should run as;
15
- defaults to the connecting user (so DDL works).
14
+ - ``PALETTE_PLUGIN_ROLE`` (optional) — schema-limited role the migration
15
+ should run as; the hosted platform sets this to
16
+ ``plugin_migrator_<sanitized_id>``. Local/manual
17
+ runs default to the connecting user.
16
18
 
17
19
  Plugin devs running ``alembic upgrade head`` locally without those vars get
18
20
  a helpful error rather than a scribbled-on main DB.
@@ -45,6 +45,12 @@ class PluginContext:
45
45
  def has_permission(self, permission: str) -> bool:
46
46
  return permission in self.permissions
47
47
 
48
+ def has_any_permission(self, permissions: list[str] | tuple[str, ...] | set[str]) -> bool:
49
+ return any(permission in self.permissions for permission in permissions)
50
+
51
+ def has_all_permissions(self, permissions: list[str] | tuple[str, ...] | set[str]) -> bool:
52
+ return all(permission in self.permissions for permission in permissions)
53
+
48
54
  def config_value(self, key: str, default: Any = None) -> Any:
49
55
  return self.config.get(key, default)
50
56
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "palette-sdk"
3
- version = "0.1.5"
3
+ version = "0.1.6"
4
4
  description = "Palette Platform SDK for building backend plugins"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"