@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.
- package/README.md +21 -3
- package/backend-sdk/palette_sdk/__init__.py +1 -1
- package/backend-sdk/palette_sdk/data_rooms.py +17 -0
- package/backend-sdk/palette_sdk/db/alembic_env.py +4 -2
- package/backend-sdk/palette_sdk/plugin_context.py +6 -0
- package/backend-sdk/pyproject.toml +1 -1
- package/docs/python-backend-sdk.md +641 -0
- package/lib/commands/build.js +4 -0
- package/lib/dev-simulator.js +30 -1
- package/package.json +2 -1
- package/template-fallback/frontend/src/index.tsx +35 -23
- package/template-fallback/frontend/src/translations.ts +36 -0
- package/template-fallback/package.json +1 -1
- package/template-fallback/palette-plugin.json +1 -1
- package/template-fallback/templates/dashboard/frontend/src/index.tsx +5 -3
- package/template-fallback/templates/dashboard/frontend/src/translations.ts +12 -0
- package/template-fallback/templates/dashboard/package.json +1 -1
- package/template-fallback/templates/dashboard/palette-plugin.json +1 -1
- package/template-fallback/templates/database/frontend/src/index.tsx +13 -12
- package/template-fallback/templates/database/frontend/src/translations.ts +30 -0
- package/template-fallback/templates/database/package.json +1 -1
- package/template-fallback/templates/database/palette-plugin.json +1 -1
- package/template-fallback/templates/external-service/frontend/src/index.tsx +5 -3
- package/template-fallback/templates/external-service/frontend/src/translations.ts +12 -0
- package/template-fallback/templates/external-service/package.json +1 -1
- package/template-fallback/templates/external-service/palette-plugin.json +1 -1
- package/template-fallback/templates/frontend-only/frontend/src/index.tsx +6 -3
- package/template-fallback/templates/frontend-only/frontend/src/translations.ts +12 -0
- package/template-fallback/templates/frontend-only/package.json +1 -1
- 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("...")`
|
|
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
|
|
508
|
+
pltt logs my-plugin --env staging
|
|
491
509
|
pltt logs --tail 100
|
|
492
510
|
pltt logs --follow
|
|
493
511
|
pltt logs --json
|
|
@@ -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
|
|
15
|
-
|
|
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
|
|