terp-cli 0.5.0__tar.gz → 0.5.2__tar.gz
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.
- terp_cli-0.5.2/PKG-INFO +16 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/pyproject.toml +8 -8
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/__init__.py +45 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/verify.py +19 -4
- terp_cli-0.5.0/PKG-INFO +0 -16
- {terp_cli-0.5.0 → terp_cli-0.5.2}/.gitignore +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/_appref.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/access.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/apidocs.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/capabilities.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/dev.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/docker.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/grants.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/jobs.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/openapi.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/profiles.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/py.typed +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/scaffold.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/schema.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/seed.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/service_accounts.py +0 -0
- {terp_cli-0.5.0 → terp_cli-0.5.2}/src/terp/cli/users.py +0 -0
terp_cli-0.5.2/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: terp-cli
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Requires-Dist: terp-arch==0.5.2
|
|
8
|
+
Requires-Dist: terp-core==0.5.2
|
|
9
|
+
Requires-Dist: terp-migrations==0.5.2
|
|
10
|
+
Provides-Extra: jobs
|
|
11
|
+
Requires-Dist: terp-cap-outbox==0.5.2; extra == 'jobs'
|
|
12
|
+
Requires-Dist: terp-cap-scheduler-apscheduler==0.5.2; extra == 'jobs'
|
|
13
|
+
Provides-Extra: scheduler
|
|
14
|
+
Requires-Dist: terp-cap-scheduler-apscheduler==0.5.2; extra == 'scheduler'
|
|
15
|
+
Provides-Extra: worker
|
|
16
|
+
Requires-Dist: terp-cap-outbox==0.5.2; extra == 'worker'
|
|
@@ -4,29 +4,29 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "terp-cli"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs."
|
|
9
9
|
requires-python = ">=3.13"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"terp-core==0.5.
|
|
12
|
+
"terp-core==0.5.2",
|
|
13
13
|
# `terp migrate` delegates to terp-migrations (lazily imported), keeping Alembic
|
|
14
14
|
# off the path for `terp inspect` / `terp guide` while making migrations work.
|
|
15
|
-
"terp-migrations==0.5.
|
|
15
|
+
"terp-migrations==0.5.2",
|
|
16
16
|
# `terp guide rules` projects the live rule registry; terp-arch is imported lazily
|
|
17
17
|
# (only when that topic is rendered), so it stays off the common `terp guide` path.
|
|
18
|
-
"terp-arch==0.5.
|
|
18
|
+
"terp-arch==0.5.2",
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
# Job-process adapters stay optional: the base CLI must not install the table-owning outbox
|
|
22
22
|
# or a scheduler engine into apps that only use inspect/check/migrate. Deployments can select
|
|
23
23
|
# one process role, while `jobs` is the convenient complete jobs-process bundle.
|
|
24
24
|
[project.optional-dependencies]
|
|
25
|
-
worker = ["terp-cap-outbox==0.5.
|
|
26
|
-
scheduler = ["terp-cap-scheduler-apscheduler==0.5.
|
|
25
|
+
worker = ["terp-cap-outbox==0.5.2"]
|
|
26
|
+
scheduler = ["terp-cap-scheduler-apscheduler==0.5.2"]
|
|
27
27
|
jobs = [
|
|
28
|
-
"terp-cap-outbox==0.5.
|
|
29
|
-
"terp-cap-scheduler-apscheduler==0.5.
|
|
28
|
+
"terp-cap-outbox==0.5.2",
|
|
29
|
+
"terp-cap-scheduler-apscheduler==0.5.2",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.scripts]
|
|
@@ -337,15 +337,60 @@ Password strength (PasswordPolicy, Tier-B)
|
|
|
337
337
|
"events": """\
|
|
338
338
|
Domain events (eventbus capability)
|
|
339
339
|
|
|
340
|
+
- The capability is a separate distribution — nothing below exists until you add it:
|
|
341
|
+
uv add terp-cap-eventbus
|
|
342
|
+
Then import from terp.capabilities.eventbus (EventEmittingService, LifecycleEventMap,
|
|
343
|
+
subscribe, dispatch_in_process).
|
|
340
344
|
- Declare typed events in your control plane (never bare strings):
|
|
341
345
|
NOTE_CREATED = EventDefinition("note.created", payload_schema=NoteCreatedPayload)
|
|
342
346
|
event_catalog = EventCatalog([NOTE_CREATED])
|
|
347
|
+
- Declare what the module produces in its manifest. The emits list is the module's
|
|
348
|
+
published contract, so the gate refuses an event you emit without naming it here:
|
|
349
|
+
module = ModuleSpec(name="notes", emits=[NOTE_CREATED, NOTE_ARCHIVED])
|
|
343
350
|
- Emit declaratively from a service (atomic with the write):
|
|
344
351
|
class NoteService(EventEmittingService[Note, NoteCreate, NoteUpdate]):
|
|
345
352
|
model = Note
|
|
346
353
|
event_map = LifecycleEventMap(created=NOTE_CREATED)
|
|
354
|
+
- The map covers "every write of this shape emits this event". When the decision is
|
|
355
|
+
CONDITIONAL — a state transition, one event for some updates and none for others —
|
|
356
|
+
extend the same hook rather than emitting from a router or a task:
|
|
357
|
+
class NoteService(EventEmittingService[Note, NoteCreate, NoteUpdate]):
|
|
358
|
+
model = Note
|
|
359
|
+
event_map = LifecycleEventMap(created=NOTE_CREATED)
|
|
360
|
+
|
|
361
|
+
def _after_write(self, session, entity, action) -> None:
|
|
362
|
+
super()._after_write(session, entity, action)
|
|
363
|
+
if action is AuditAction.UPDATED and entity.status == "archived":
|
|
364
|
+
emit(session, event=NOTE_ARCHIVED, payload=NoteArchivedPayload(id=entity.id))
|
|
365
|
+
_after_write runs inside the write's transaction, so a conditional emit is still
|
|
366
|
+
atomic with the row it describes — which is the whole reason the map lives there.
|
|
347
367
|
- Subscribe with @subscribe(NOTE_CREATED). Reference catalog constants only (the gate
|
|
348
368
|
enforces no-drift). Wire create_app(..., event_dispatcher=dispatch_in_process).
|
|
369
|
+
- Switching the bus on in a service-level test: the `terp_events` fixture, never a
|
|
370
|
+
direct configure_events (see `terp guide testing`).
|
|
371
|
+
""",
|
|
372
|
+
"testing": """\
|
|
373
|
+
Testing a Terp app (process-global runtime isolation)
|
|
374
|
+
|
|
375
|
+
- create_app installs SIX process globals per app: the audit policy+sink, the event
|
|
376
|
+
catalog+dispatcher, the job catalog+queue, the schedule catalog, the password policy,
|
|
377
|
+
and the decrypt call site. In a test process they outlive the app that installed them.
|
|
378
|
+
- You get isolation for free. terp-core ships a pytest plugin (terp.core.testing,
|
|
379
|
+
registered under pytest11) whose autouse fixture SNAPSHOTS every runtime before a
|
|
380
|
+
test and RESTORES it after. No conftest.py line, no opt-in. Without it a suite goes
|
|
381
|
+
order-dependent: green together, red alone - the sharpest failure mode there is,
|
|
382
|
+
because the green is the wrong answer.
|
|
383
|
+
- Isolation does not INSTALL a runtime your test needs - that is your decision:
|
|
384
|
+
* whole runtime -> compose the app in a fixture (see apps/example/tests/conftest.py,
|
|
385
|
+
where db_engine + app_db are function-scoped and build() runs per test)
|
|
386
|
+
* only the event bus -> the `terp_events` fixture, which installs a catalog
|
|
387
|
+
(and dispatcher) for the duration of one test:
|
|
388
|
+
terp_events(event_catalog, dispatcher=dispatch_in_process)
|
|
389
|
+
* asserting on what was written to a FAKE/bare session -> `terp_default_runtime`,
|
|
390
|
+
which states the baseline instead of assuming it. Without it a composed app's
|
|
391
|
+
durable audit sink quietly adds audit rows to the session under assertion.
|
|
392
|
+
- If a test passes in the suite and fails alone (or vice versa), it is reading a
|
|
393
|
+
runtime it never installed. Install it explicitly; never re-order the suite.
|
|
349
394
|
""",
|
|
350
395
|
"jobs": """\
|
|
351
396
|
Background jobs (terp.core.enqueue + JobCatalog)
|
|
@@ -270,6 +270,19 @@ def _node_platform() -> tuple[str, str]:
|
|
|
270
270
|
return system, arch
|
|
271
271
|
|
|
272
272
|
|
|
273
|
+
def _node_libc(system: str) -> str | None:
|
|
274
|
+
"""This machine's libc flavour as npm names it, or None where npm ignores it.
|
|
275
|
+
|
|
276
|
+
npm gates Linux binaries on ``libc`` as well as ``os``/``cpu``: a x64 Linux
|
|
277
|
+
bundler ships a ``-gnu`` *and* a ``-musl`` binding and installs exactly one.
|
|
278
|
+
Reading only ``os``/``cpu`` therefore reports the flavour this machine is not,
|
|
279
|
+
turning a perfectly healthy tree red.
|
|
280
|
+
"""
|
|
281
|
+
if system != "linux":
|
|
282
|
+
return None
|
|
283
|
+
return "musl" if any(pathlib.Path("/lib").glob("ld-musl-*.so.1")) else "glibc"
|
|
284
|
+
|
|
285
|
+
|
|
273
286
|
def _node_modules_problem(root: pathlib.Path) -> str | None:
|
|
274
287
|
"""Explain an unusable ``frontend/node_modules``, or None if it looks fine.
|
|
275
288
|
|
|
@@ -302,17 +315,19 @@ def _node_modules_problem(root: pathlib.Path) -> str | None:
|
|
|
302
315
|
return None
|
|
303
316
|
|
|
304
317
|
system, arch = _node_platform()
|
|
318
|
+
libc = _node_libc(system)
|
|
305
319
|
missing = [
|
|
306
320
|
name
|
|
307
321
|
for name, entry in packages.items()
|
|
308
322
|
if name.startswith("node_modules/")
|
|
309
323
|
and isinstance(entry, dict)
|
|
310
|
-
# Only packages this platform is *supposed* to have: an entry with no
|
|
311
|
-
# constraint is platform-neutral, and one constrained elsewhere
|
|
312
|
-
# design rather than by a bad install.
|
|
324
|
+
# Only packages this platform is *supposed* to have: an entry with no
|
|
325
|
+
# os/cpu/libc constraint is platform-neutral, and one constrained elsewhere
|
|
326
|
+
# is absent by design rather than by a bad install.
|
|
313
327
|
and system in (entry.get("os") or [system])
|
|
314
328
|
and arch in (entry.get("cpu") or [arch])
|
|
315
|
-
and (entry.get("
|
|
329
|
+
and (libc is None or libc in (entry.get("libc") or [libc]))
|
|
330
|
+
and (entry.get("os") or entry.get("cpu") or entry.get("libc"))
|
|
316
331
|
and not (frontend / name).exists()
|
|
317
332
|
]
|
|
318
333
|
if not missing:
|
terp_cli-0.5.0/PKG-INFO
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: terp-cli
|
|
3
|
-
Version: 0.5.0
|
|
4
|
-
Summary: Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs.
|
|
5
|
-
License-Expression: Apache-2.0
|
|
6
|
-
Requires-Python: >=3.13
|
|
7
|
-
Requires-Dist: terp-arch==0.5.0
|
|
8
|
-
Requires-Dist: terp-core==0.5.0
|
|
9
|
-
Requires-Dist: terp-migrations==0.5.0
|
|
10
|
-
Provides-Extra: jobs
|
|
11
|
-
Requires-Dist: terp-cap-outbox==0.5.0; extra == 'jobs'
|
|
12
|
-
Requires-Dist: terp-cap-scheduler-apscheduler==0.5.0; extra == 'jobs'
|
|
13
|
-
Provides-Extra: scheduler
|
|
14
|
-
Requires-Dist: terp-cap-scheduler-apscheduler==0.5.0; extra == 'scheduler'
|
|
15
|
-
Provides-Extra: worker
|
|
16
|
-
Requires-Dist: terp-cap-outbox==0.5.0; extra == 'worker'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|