@happyvertical/smrt-cli 0.53.3 → 0.53.5

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 (76) hide show
  1. package/AGENTS.md +3 -0
  2. package/README.md +64 -0
  3. package/agents/app-commands.md +118 -0
  4. package/bin/smrt-web.mjs +19 -0
  5. package/bin/smrt.js +18 -12
  6. package/dist/{ApiKey-BZmbOTnL-DchyhIPA.js → ApiKey-BZmbOTnL-BbZupUZ3.js} +2 -2
  7. package/dist/ApiKeyCollection-Bfb_nw81-DYAZwx0O.js +87 -0
  8. package/dist/AuditLogCollection-B1YonCAP-Bkj2Ezon.js +193 -0
  9. package/dist/{NostrIdentity-n70E0vck-CLGPdJ1W.js → NostrIdentity-n70E0vck-Di3_eNCq.js} +4 -4
  10. package/dist/NostrIdentityCollection-Ca74dkaL-DvjOg9Xy.js +127 -0
  11. package/dist/{OidcIdentity-Cb1bxreM-DPBTZddF.js → OidcIdentity-Cb1bxreM-LXmC7uAf.js} +2 -2
  12. package/dist/OidcIdentityCollection-TV8VdK69-Cv1ZTeuk.js +86 -0
  13. package/dist/ProfileAssetCollection-B9hacUJa-CPuaWLk4.js +101 -0
  14. package/dist/{ProfileCollection-DuJT7Mum-BUYQQ9xZ.js → ProfileCollection-DuJT7Mum-nXbHrPpX.js} +31 -31
  15. package/dist/ProfileLinkCollection-4uNURz32-CbKZYmYm.js +233 -0
  16. package/dist/ProfileMetadataCollection-DQhbfEfa-_1rQCLTR.js +108 -0
  17. package/dist/ProfileMetafieldCollection-CoBuM_5G-CPjW3l5r.js +145 -0
  18. package/dist/ProfileRelationshipCollection-DUVgaIqf-RCDqYz6O.js +161 -0
  19. package/dist/ProfileRelationshipTermCollection-DtA9OM23-BoGhQBSQ.js +107 -0
  20. package/dist/{ProfileRelationshipType-BRiRka6t-xzRfZRFG.js → ProfileRelationshipType-BRiRka6t-Dezjv5jP.js} +1 -1
  21. package/dist/ProfileRelationshipTypeCollection-BdXwd7Kf-HoTmV1q-.js +57 -0
  22. package/dist/app/cli.d.ts +51 -0
  23. package/dist/app/cli.d.ts.map +1 -0
  24. package/dist/app/errors.d.ts +55 -0
  25. package/dist/app/errors.d.ts.map +1 -0
  26. package/dist/app/identity.d.ts +24 -0
  27. package/dist/app/identity.d.ts.map +1 -0
  28. package/dist/app/index.d.ts +20 -0
  29. package/dist/app/index.d.ts.map +1 -0
  30. package/dist/app/launchers.d.ts +51 -0
  31. package/dist/app/launchers.d.ts.map +1 -0
  32. package/dist/app/operations.d.ts +30 -0
  33. package/dist/app/operations.d.ts.map +1 -0
  34. package/dist/app/portability-assets.d.ts +151 -0
  35. package/dist/app/portability-assets.d.ts.map +1 -0
  36. package/dist/app/portability.d.ts +117 -0
  37. package/dist/app/portability.d.ts.map +1 -0
  38. package/dist/app/process-record.d.ts +54 -0
  39. package/dist/app/process-record.d.ts.map +1 -0
  40. package/dist/app/runtime.d.ts +98 -0
  41. package/dist/app/runtime.d.ts.map +1 -0
  42. package/dist/app/tokens.d.ts +21 -0
  43. package/dist/app/tokens.d.ts.map +1 -0
  44. package/dist/app.d.ts +1 -0
  45. package/dist/app.js +4 -0
  46. package/dist/cli-OZsH5cdL.js +1602 -0
  47. package/dist/cli-generator.d.ts.map +1 -1
  48. package/dist/commands/init.d.ts.map +1 -1
  49. package/dist/commands/utilities.d.ts.map +1 -1
  50. package/dist/{commands-B8N38deG.js → commands-UXK2F_0j.js} +49 -336
  51. package/dist/database-environment-D_Gk1Brf.js +20 -0
  52. package/dist/database-environment.d.ts +43 -0
  53. package/dist/database-environment.d.ts.map +1 -0
  54. package/dist/dist-BVjJWjT-.js +20 -0
  55. package/dist/{dist-B62O-xH6.js → dist-C87Ib9Vx.js} +2 -2
  56. package/dist/{dist-CL9KGm1j.js → dist-DLw_E34G.js} +1654 -1748
  57. package/dist/{dist-BBQyIRsO.js → dist-GIaWaUho.js} +4 -4
  58. package/dist/{dist-C8BOgli4.js → dist-RSE5hCY4.js} +3 -3
  59. package/dist/index.js +19 -9
  60. package/dist/mcp-apps-packaging-CixRnxz_.js +286 -0
  61. package/dist/{oidcProvisioningPrimitives-D9OYu5-F-l8afZs4F.js → oidcProvisioningPrimitives-D9OYu5-F-T9KkkDCd.js} +5 -5
  62. package/dist/portability-CES1YC2m.js +859 -0
  63. package/dist/resolveIdentity-B-PK6RwU-C9ogf8YG.js +269 -0
  64. package/package.json +19 -14
  65. package/dist/ApiKeyCollection-Bfb_nw81-DR5XF_SZ.js +0 -2
  66. package/dist/AuditLogCollection-B1YonCAP-PbzALncL.js +0 -2
  67. package/dist/NostrIdentityCollection-Ca74dkaL-C2r-uDlC.js +0 -2
  68. package/dist/OidcIdentityCollection-TV8VdK69-2fZROnih.js +0 -2
  69. package/dist/ProfileAssetCollection-B9hacUJa-D33sifgF.js +0 -2
  70. package/dist/ProfileLinkCollection-4uNURz32-CHDTinax.js +0 -2
  71. package/dist/ProfileMetadataCollection-DQhbfEfa-BQTogfVX.js +0 -2
  72. package/dist/ProfileMetafieldCollection-CoBuM_5G-BXtoIP02.js +0 -2
  73. package/dist/ProfileRelationshipCollection-DUVgaIqf-CNMliUF3.js +0 -2
  74. package/dist/ProfileRelationshipTermCollection-DtA9OM23-DPOMtMr7.js +0 -2
  75. package/dist/ProfileRelationshipTypeCollection-BdXwd7Kf-Co3PdH01.js +0 -2
  76. package/dist/resolveIdentity-B-PK6RwU-B306Ch1I.js +0 -2
package/AGENTS.md CHANGED
@@ -42,8 +42,11 @@ smrt config:export # Export agent config for SSG
42
42
  smrt init # Init new project
43
43
  smrt gnode # Scaffold gnode site
44
44
  smrt dispatch:* # Dispatch management (list/process/retry/cleanup)
45
+ smrt app <op> # App install/setup/start/migrate/worker…: agents/app-commands.md
45
46
  ```
46
47
 
48
+ DB target: config `database.url` > `DATABASE_URL` > `:memory:` (README).
49
+
47
50
  File-backed SQL/TypeScript migration generation is not supported. SMRT schema
48
51
  migrations are manifest-driven through registered objects and project manifests.
49
52
 
package/README.md CHANGED
@@ -208,6 +208,13 @@ Run the project's package-manager install command after initialization. Existing
208
208
  versions are preserved; s-m-r-t workspace projects receive `workspace:*` ranges and
209
209
  consumer projects receive the CLI's published release line.
210
210
 
211
+ The generated `src/lib/server/smrt.ts` exports only `runtime` (`classOptions()`
212
+ and `getCollection()` over `DATABASE_URL`/`DATABASE_TYPE`), which generated API
213
+ routes call per request; it no longer exports the deprecated
214
+ `getCollection`/`getSmrtConfig` accessors. To adopt the full application
215
+ runtime, replace that object with `createSmrtSvelteKitRuntime()` from
216
+ `@happyvertical/smrt-app-runtime/sveltekit` and mount its `handle`/`init`.
217
+
211
218
  ### Dispatch
212
219
 
213
220
  | Command | Description |
@@ -242,6 +249,45 @@ consumer projects receive the CLI's published release line.
242
249
  | `smrt playground dev` | Run the shared workspace host or local app playground |
243
250
  | `smrt playground list` | Show discovered playground modules and preview entries |
244
251
 
252
+ ### Application operations (`smrt app`)
253
+
254
+ Operational commands for a generated s-m-r-t application, run from the
255
+ application root. They replace the template's copied `scripts/*.mjs`, so an
256
+ app's `package.json` scripts are one-liners:
257
+
258
+ ```json
259
+ {
260
+ "dev": "smrt app dev",
261
+ "build": "smrt app build",
262
+ "preview": "smrt app start",
263
+ "db:migrate": "vite build && smrt app migrate",
264
+ "app:setup": "smrt app setup",
265
+ "app:doctor": "smrt app doctor",
266
+ "worker": "smrt app worker task",
267
+ "worker:schedule": "smrt app worker schedule"
268
+ }
269
+ ```
270
+
271
+ | Command | Description |
272
+ |---------|------------|
273
+ | `smrt app install` | Local: setup, start, and open owner onboarding under one operation lock |
274
+ | `smrt app setup` | Build, run `smrt db:migrate` explicitly, and prepare the private owner-onboarding handoff |
275
+ | `smrt app recover` | Local: rotate the single-use owner onboarding invitation |
276
+ | `smrt app start` / `stop` | Local: run the production build on loopback; readiness is proven by app id, process instance, and configuration fingerprint |
277
+ | `smrt app doctor` | Secret-free findings (`invalid-runtime-profile`, `unsafe-local-bind`, `runtime-path-unavailable`, `migration-required`, …); exits 1 on any error (default operation) |
278
+ | `smrt app open` | Open the app, or the pending onboarding launch file |
279
+ | `smrt app backup [destination]` | Local: copy the validated data root to a new private directory outside the checkout |
280
+ | `smrt app export [path]` / `import <path>` | Logical, asset-aware bundle; import requires an empty target (deployed: `SMRT_MAINTENANCE_MODE=true`) |
281
+ | `smrt app migrate` | Establish local storage custody, then `smrt db:migrate`, under the operation lock |
282
+ | `smrt app worker [task\|schedule]` | Deployed: imports `.smrt/runtime/register.js`, then runs the jobs runner until SIGTERM. The kind defaults to `task`; any other value is a usage error before anything starts |
283
+ | `smrt app dev\|build\|vite [args]` | Run the app's installed Vite with `.env` loaded (shell wins); `build` validates `./mcp-apps` first |
284
+
285
+ Success output is JSON on stdout; a failure is one JSON envelope on stderr with
286
+ exit code 1 and never contains secret values. The same primitives (operation
287
+ lock, writer lease, state root, artifact-path custody, portability) are
288
+ importable without side effects from `@happyvertical/smrt-cli/app`. Contract
289
+ details: [agents/app-commands.md](agents/app-commands.md).
290
+
245
291
  ### Auto-Generated Object Commands
246
292
 
247
293
  For each registered s-m-r-t object, the CLI generates:
@@ -325,6 +371,23 @@ export default {
325
371
  };
326
372
  ```
327
373
 
374
+ ### Database precedence
375
+
376
+ Every `db:*` command resolves its database once, at startup:
377
+
378
+ 1. `packages.cli.database.url` from any config layer — an explicit project
379
+ setting always wins, so a stray shell `DATABASE_URL` never retargets it.
380
+ 2. `DATABASE_URL`, with the engine from the config's `database.type`, else
381
+ `DATABASE_TYPE` (`sqlite` | `postgres`), else the URL scheme
382
+ (`postgres://` / `postgresql://` → postgres, anything else sqlite). An
383
+ unsupported `DATABASE_TYPE` disables this step with a warning.
384
+ 3. The `:memory:` default, which schema commands refuse.
385
+
386
+ `smrt app setup` and `smrt app migrate` hand `smrt db:migrate` the profile's
387
+ database through step 2, so an application does not forward
388
+ `DATABASE_URL` in its own config. The rule is smrt-config's
389
+ `resolveCliDatabaseConfig()`, which `smrt-dev-mcp` uses too.
390
+
328
391
  ### Entry Point Discovery
329
392
 
330
393
  The CLI loads s-m-r-t objects from your project entry point:
@@ -345,4 +408,5 @@ If compiled classes cannot be loaded, the CLI falls back to manifest-only mode (
345
408
 
346
409
  - `@happyvertical/smrt-core` -- ORM, manifest, code generation
347
410
  - `@happyvertical/smrt-config` -- configuration loading
411
+ - `@happyvertical/smrt-app-runtime` -- local storage custody and runtime composition for `smrt app`
348
412
  - `@happyvertical/smrt-scanner` -- AST scanning for metadata extraction
@@ -0,0 +1,118 @@
1
+ # `smrt app` — application operator commands (#3371)
2
+
3
+ `smrt app <operation>` is the CLI home of the operations a generated SMRT app
4
+ used to ship as copied `scripts/*.mjs`. It is a port, not a redesign: state
5
+ files, JSON output, exit codes, environment variables, and messages match the
6
+ template's scripts so an app's `package.json` scripts become one-liners.
7
+
8
+ | Template script | Command |
9
+ |---|---|
10
+ | `scripts/smrt-app.mjs <op>` | `smrt app install\|setup\|recover\|start\|stop\|doctor\|open\|backup\|export\|import` |
11
+ | `scripts/smrt-prepare-migration.mjs` | `smrt app migrate` |
12
+ | `scripts/smrt-worker.mjs [task\|schedule]` | `smrt app worker [task\|schedule]` |
13
+ | `node --env-file-if-exists=.env scripts/smrt-vite.mjs <args>` | `smrt app vite <args>` (`smrt app dev …` = `vite dev …` plus `--host 127.0.0.1` / `--port $PORT` unless given: `devServerArguments`) |
14
+ | `smrt-mcp-apps.mjs validate-if-present && …smrt-vite.mjs build` | `smrt app build [vite args]` |
15
+ | `scripts/smrt-web.mjs` | `bin/smrt-web.mjs` (spawned by `start`) |
16
+ | — (new, #3413) | `smrt app token [create] --scopes … [--expires 30d] [--label …]`, `token list`, `token revoke <id>` |
17
+ | helper modules | `@happyvertical/smrt-cli/app` (side-effect-free subpath) |
18
+
19
+ ## Source
20
+
21
+ `src/app/`: `cli.ts` (dispatch, help, error envelope), `operations.ts`,
22
+ `launchers.ts` (migrate/worker/vite), `identity.ts` (artifact-path custody;
23
+ re-exports identity and state root), `process-record.ts`, `portability.ts`,
24
+ `portability-assets.ts`, `runtime.ts` (context, runners, profile-aware child
25
+ environment). Application ID, configuration fingerprint, state root,
26
+ operation lock, writer lease, stale reclaim, and provider readiness are
27
+ `@happyvertical/smrt-app-runtime`'s (the web process takes the same lease
28
+ without depending on the CLI); `./app` re-exports them unchanged. `main()` dispatches `app` before manifest or
29
+ config loading; `bin/smrt.js` imports `dist/` in-process so SIGTERM reaches
30
+ `smrt app worker`.
31
+
32
+ ## Contracts that must not drift
33
+
34
+ - **Profile**: `resolveConfiguredRuntime` and app-runtime's SvelteKit entry
35
+ both call smrt-config's `resolveEffectiveApplicationRuntime(loaded)` (#3446),
36
+ so operator and web process agree: no `runtime` block (or no config) is
37
+ `local`; any present value other than `undefined` (including
38
+ `null`/`false`/`0`/`''`) is validated and fails closed when it is not a
39
+ valid block.
40
+ - **State root** `<XDG_STATE_HOME|~/.local/state|~/Library/Application Support|%LOCALAPPDATA%>/.<appId>-<sha256(dataRoot)[:12]>-state`,
41
+ mode 0700, every ancestor user/root-owned and not group/world-writable,
42
+ app-bound empty 0600 marker `.smrt-state-<appId>`. Shared with the running
43
+ web server (`writer.lease`), so record formats are byte-compatible.
44
+ - **Locks**: `operation.lock` (one operator op; dead owner reclaimed;
45
+ unparsable fails closed; never unlinks a replaced lock) and `writer.lease`
46
+ (one writer; a live op blocks a writer unless it presents that op's
47
+ `instance`, which `start` passes as `SMRT_OPERATION_INSTANCE`). Stale
48
+ records are removed only under an exclusive SQLite transaction on
49
+ `<state>/.smrt-lock-reclaim.sqlite` (kernel lock, released on death), after
50
+ re-reading that the file still holds the exact dead record
51
+ (app-runtime `stale-reclaim.ts`); a contender waits up to 2 s, then fails. Acquisition
52
+ is still a plain `O_EXCL` create, so formats are unchanged.
53
+ - **Storage custody** belongs to `@happyvertical/smrt-app-runtime`: `setup`
54
+ goes through `initializeLocalApplicationRuntime` with an explicit
55
+ `prepareDatabase` that runs `smrt db:migrate`; `migrate` calls
56
+ `prepareLocalDatabaseStorage`; `doctor`/`backup`/`export`/`import` call
57
+ `validateLocalDatabaseStorage`, which never creates or repairs storage.
58
+ - **No runtime schema**: migrations run only through `smrt db:migrate`.
59
+ - **Output**: success JSON on stdout; failure is one stderr line
60
+ `{schemaVersion:1,status:'error',code:'operation-failed',message,recovery,secretValuesIncluded:false}`
61
+ plus `runtimeCode` when a `LocalRuntimeError` carries one
62
+ (`migration_failed` + `MIGRATION_FAILED_MESSAGE`). Messages pass through
63
+ `redactSecrets()` (secret-named env values, URL userinfo, `token=`, Bearer;
64
+ every span is found on the raw text and the union masked at once, so a
65
+ literal value never splits a structural match; existing `[redacted]`
66
+ markers are opaque, so a second pass neither corrupts nor grows text).
67
+ `start` sends the detached web process's stdout/stderr to a fresh 0600
68
+ `<state>/app.log` per start (removed, then created `wx`; a pipe would EPIPE
69
+ once the CLI exits); a start that never proves readiness adds `output` (the
70
+ last ≤ 8 KiB, redacted against the child env before the cut with
71
+ `{ strict: true }`: no 8-character floor for env values or Bearer tokens) and `logFile`
72
+ (`ApplicationStartError`).
73
+ The bootstrap token is written only to mode-0600 `onboarding.json` /
74
+ `onboarding-launch.html`; `open` passes the launch file URL, never the token. `setup`/`recover` print the one-time
75
+ `/setup?token=` URL only to an interactive terminal (`io.operatorTerminal`,
76
+ stderr when both stdio are TTYs), never to stdout/stderr logs or pipes. The
77
+ web runtime deletes the files after a successful owner claim.
78
+ - **`token`** (`tokens.ts`, local only): parses every argument before touching
79
+ state, opens `openLocalMcpTokenStore` (validated custody, uncached
80
+ connection, no writer lease, so it runs beside `app:start`), and serializes
81
+ create/revoke under `operation.lock`. Only the `issued` document carries
82
+ the token (`secretValuesIncluded: true`); list/revoke/error output never
83
+ does.
84
+ - **Artifacts**: backup/export/import paths go through
85
+ `assertExternalArtifactPath` (real path of nearest existing ancestor, never
86
+ in or over the checkout). Backup refuses an existing destination; export
87
+ publishes via `link()` (never replaces); import requires a 0600 regular
88
+ bundle for the same app, an exactly matching manifest schema, and an empty
89
+ target; deployed import requires `SMRT_MAINTENANCE_MODE=true`.
90
+ - **Process identity**: `app.pid` is trusted only when the live command
91
+ line's last two tokens are a path with basename `smrt-web.mjs` and exactly
92
+ `--smrt-instance=<record instance>` (an app started by the template's former
93
+ `scripts/smrt-web.mjs` still matches); `stop` re-reads it immediately before SIGTERM
94
+ (`checkOwnedProcess`: only `gone`/`mismatched` drop `app.pid`; an
95
+ `unverifiable` query keeps it, sends nothing, and fails naming the pid). A `start`
96
+ whose launcher never proves readiness is sent SIGTERM, then SIGKILL; only a
97
+ confirmed exit removes `app.pid`, otherwise the record stays and the error
98
+ names the pid.
99
+ - **Application resolution**: Vite and bare readiness-module specifiers
100
+ resolve from the app's own `node_modules` chain (never `NODE_PATH`).
101
+ Readiness specifiers go through Node's own ESM resolver re-parented to the
102
+ app root (a temporary `module.registerHooks` resolve hook, because the
103
+ `import.meta.resolve` parent argument is still flagged), so `exports`
104
+ precedence, condition order, `null` targets, and invalid targets follow
105
+ Node exactly; the result must stay inside the installed package. Relative
106
+ readiness specifiers resolve against the app root.
107
+ - **Import recovery**: after the database commit the asset journal gains an
108
+ additive `committedAt`. A leftover journal with matching table counts is
109
+ accepted as complete only with that marker for the same bundle digest, or
110
+ when every table's rows equal the bundle's; otherwise recovery refuses
111
+ (`asset-recovery-target-mismatch`) and keeps the journal.
112
+ - `scripts/smrt-portability.mjs`, when an app provides it, still overrides
113
+ the built-in export/import adapter (the documented extension point).
114
+
115
+ Tests: `src/app/__tests__/` (primitives, operations in-process, real CLI
116
+ process, and the template's portability-asset suite ported verbatim); lock,
117
+ lease, state-custody, and readiness cases (including review findings R1, R3,
118
+ T2) live in app-runtime's `src/operator-primitives.test.ts`.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ // Web launcher spawned by `smrt app start` (cwd = application root).
3
+ //
4
+ // The file name and the `--smrt-instance=<32 hex>` argument are the process
5
+ // identity `smrt app stop` verifies before signalling a recorded pid, so a
6
+ // recycled pid is never terminated. Ported from the template's
7
+ // `scripts/smrt-web.mjs`.
8
+
9
+ import { resolve } from 'node:path';
10
+ import { pathToFileURL } from 'node:url';
11
+
12
+ const instance = process.argv
13
+ .find((value) => value.startsWith('--smrt-instance='))
14
+ ?.slice('--smrt-instance='.length);
15
+ if (!instance || !/^[a-f0-9]{32}$/.test(instance)) {
16
+ throw new Error('A valid application process identity is required.');
17
+ }
18
+
19
+ await import(pathToFileURL(resolve(process.cwd(), 'build', 'index.js')).href);
package/bin/smrt.js CHANGED
@@ -3,24 +3,30 @@
3
3
  import { spawnSync } from 'node:child_process';
4
4
  import { existsSync } from 'node:fs';
5
5
  import { dirname, resolve } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
6
+ import { fileURLToPath, pathToFileURL } from 'node:url';
7
7
 
8
8
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
9
9
  const distEntry = resolve(packageRoot, 'dist/index.js');
10
10
  const sourceEntry = resolve(packageRoot, 'src/index.ts');
11
11
 
12
- const command = existsSync(distEntry)
13
- ? [distEntry, ...process.argv.slice(2)]
14
- : ['--import', 'tsx', sourceEntry, ...process.argv.slice(2)];
12
+ if (existsSync(distEntry)) {
13
+ // Run the built CLI in this process so signals reach long-running commands
14
+ // (a container's SIGTERM to `smrt app worker`), and exit codes are its own.
15
+ await import(pathToFileURL(distEntry).href);
16
+ } else {
17
+ const result = spawnSync(
18
+ process.execPath,
19
+ ['--import', 'tsx', sourceEntry, ...process.argv.slice(2)],
20
+ { stdio: 'inherit' },
21
+ );
15
22
 
16
- const result = spawnSync(process.execPath, command, { stdio: 'inherit' });
23
+ if (result.error) {
24
+ throw result.error;
25
+ }
17
26
 
18
- if (result.error) {
19
- throw result.error;
20
- }
27
+ if (result.signal) {
28
+ process.kill(process.pid, result.signal);
29
+ }
21
30
 
22
- if (result.signal) {
23
- process.kill(process.pid, result.signal);
31
+ process.exit(result.status ?? 1);
24
32
  }
25
-
26
- process.exit(result.status ?? 1);
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-CLGPdJ1W.js";
2
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
3
3
  import { SmrtObject, field, foreignKey, smrt } from "@happyvertical/smrt-core";
4
4
  import { createHash, randomBytes } from "node:crypto";
5
5
  //#region ../profiles/dist/chunks/ApiKey-BZmbOTnL.js
@@ -127,7 +127,7 @@ var ApiKey = class extends SmrtObject {
127
127
  */
128
128
  static async verify(key, options = {}) {
129
129
  const keyHash = ApiKey.hashKey(key);
130
- const { ApiKeyCollection } = await import("./ApiKeyCollection-Bfb_nw81-DR5XF_SZ.js").then((n) => n.n);
130
+ const { ApiKeyCollection } = await import("./ApiKeyCollection-Bfb_nw81-DYAZwx0O.js").then((n) => n.n).then((n) => n.n);
131
131
  const apiKey = await (await ApiKeyCollection.create(options)).findOne({ where: { keyHash } });
132
132
  if (!apiKey) return null;
133
133
  if (!apiKey.isValid()) return null;
@@ -0,0 +1,87 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
3
+ import { t as ApiKey } from "./ApiKey-BZmbOTnL-BbZupUZ3.js";
4
+ import { SmrtCollection } from "@happyvertical/smrt-core";
5
+ //#region ../profiles/dist/chunks/ApiKeyCollection-Bfb_nw81.js
6
+ var ApiKeyCollection_Bfb_nw81_exports = /* @__PURE__ */ __exportAll({
7
+ n: () => ApiKeyCollection_exports,
8
+ t: () => ApiKeyCollection
9
+ });
10
+ var ApiKeyCollection_exports = /* @__PURE__ */ __exportAll$1({ ApiKeyCollection: () => ApiKeyCollection });
11
+ var ApiKeyCollection = class extends SmrtCollection {
12
+ static _itemClass = ApiKey;
13
+ /**
14
+ * Find all keys for a profile
15
+ */
16
+ async findByProfile(profileId) {
17
+ return await this.list({ where: { profileId } });
18
+ }
19
+ /**
20
+ * Find active (non-revoked, non-expired) keys for a profile
21
+ */
22
+ async findActiveByProfile(profileId) {
23
+ return (await this.findByProfile(profileId)).filter((key) => key.isValid());
24
+ }
25
+ /**
26
+ * Find key by hash
27
+ */
28
+ async findByHash(keyHash) {
29
+ return await this.findOne({ where: { keyHash } });
30
+ }
31
+ /**
32
+ * Verify an API key string and return the record if valid
33
+ */
34
+ async verify(key) {
35
+ const keyHash = ApiKey.hashKey(key);
36
+ const apiKey = await this.findByHash(keyHash);
37
+ if (!apiKey) return null;
38
+ if (!apiKey.isValid()) return null;
39
+ await apiKey.recordUsage();
40
+ if (!apiKey.isValid()) return null;
41
+ return apiKey;
42
+ }
43
+ /**
44
+ * Generate a new key for a profile
45
+ */
46
+ async generateForProfile(profile, options) {
47
+ return await ApiKey.generate(profile, {
48
+ ...options,
49
+ db: this.options.db
50
+ });
51
+ }
52
+ /**
53
+ * Revoke all keys for a profile
54
+ */
55
+ async revokeAllForProfile(profileId) {
56
+ const keys = await this.findActiveByProfile(profileId);
57
+ for (const key of keys) await key.revoke();
58
+ return keys.length;
59
+ }
60
+ /**
61
+ * Revoke a specific key by prefix
62
+ */
63
+ async revokeByPrefix(keyPrefix) {
64
+ const key = await this.findOne({ where: { keyPrefix } });
65
+ if (key?.isValid()) {
66
+ await key.revoke();
67
+ return true;
68
+ }
69
+ return false;
70
+ }
71
+ /**
72
+ * Clean up expired keys (soft delete by setting revokedAt)
73
+ */
74
+ async cleanupExpired() {
75
+ const now = /* @__PURE__ */ new Date();
76
+ const allKeys = await this.list({});
77
+ let cleaned = 0;
78
+ for (const key of allKeys) if (key.expiresAt && key.expiresAt < now && !key.revokedAt) {
79
+ key.revokedAt = now;
80
+ await key.save();
81
+ cleaned++;
82
+ }
83
+ return cleaned;
84
+ }
85
+ };
86
+ //#endregion
87
+ export { ApiKeyCollection_Bfb_nw81_exports as n, ApiKeyCollection as t };
@@ -0,0 +1,193 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { i as tenantId, r as TenantScoped } from "./dist-C87Ib9Vx.js";
3
+ import { n as __exportAll$1 } from "./NostrIdentity-n70E0vck-Di3_eNCq.js";
4
+ import { SmrtCollection, SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
5
+ //#region ../profiles/dist/chunks/AuditLogCollection-B1YonCAP.js
6
+ var AuditLogCollection_B1YonCAP_exports = /* @__PURE__ */ __exportAll({
7
+ n: () => AuditLogCollection_exports,
8
+ r: () => AuditLog,
9
+ t: () => AuditLogCollection
10
+ });
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __decorateClass = (decorators, target, key, kind) => {
14
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
15
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
16
+ if (kind && result) __defProp(target, key, result);
17
+ return result;
18
+ };
19
+ var AuditLog = class extends SmrtObject {
20
+ tenantId = null;
21
+ profileId;
22
+ /**
23
+ * Action performed (e.g., 'issue.update', 'feedback.incorporate')
24
+ */
25
+ action = "";
26
+ /**
27
+ * Type of resource affected (e.g., 'Issue', 'Repository')
28
+ */
29
+ resourceType = "";
30
+ /**
31
+ * ID of the affected resource
32
+ */
33
+ resourceId = "";
34
+ /**
35
+ * Source of the action
36
+ */
37
+ source = "web";
38
+ /**
39
+ * Additional context (CI run ID, request ID, etc.)
40
+ */
41
+ metadata = {};
42
+ onBehalfOfId;
43
+ constructor(options = {}) {
44
+ super(options);
45
+ if (options.profileId) this.profileId = options.profileId;
46
+ if (options.action) this.action = options.action;
47
+ if (options.resourceType) this.resourceType = options.resourceType;
48
+ if (options.resourceId) this.resourceId = options.resourceId;
49
+ if (options.source) this.source = options.source;
50
+ if (options.metadata) this.metadata = options.metadata;
51
+ if (options.onBehalfOfId !== void 0) this.onBehalfOfId = options.onBehalfOfId;
52
+ }
53
+ /**
54
+ * Get the profile who performed the action
55
+ */
56
+ async getProfile() {
57
+ return await this.getRelated("profileId");
58
+ }
59
+ /**
60
+ * Get the profile on whose behalf the action was performed (if any)
61
+ */
62
+ async getOnBehalfOf() {
63
+ if (!this.onBehalfOfId) return null;
64
+ return await this.getRelated("onBehalfOfId");
65
+ }
66
+ /**
67
+ * Get the effective actor (onBehalfOf if set, otherwise profile)
68
+ */
69
+ async getEffectiveActor() {
70
+ const onBehalfOf = await this.getOnBehalfOf();
71
+ if (onBehalfOf) return onBehalfOf;
72
+ return await this.getProfile();
73
+ }
74
+ /**
75
+ * Create an audit log entry
76
+ */
77
+ static async record(options) {
78
+ const log = new AuditLog({
79
+ ...options,
80
+ profileId: options.profile.id,
81
+ onBehalfOfId: options.onBehalfOf?.id
82
+ });
83
+ await log.initialize();
84
+ await log.save();
85
+ return log;
86
+ }
87
+ };
88
+ __decorateClass([tenantId({ nullable: true })], AuditLog.prototype, "tenantId", 2);
89
+ __decorateClass([foreignKey("Profile", { required: true })], AuditLog.prototype, "profileId", 2);
90
+ __decorateClass([foreignKey("Profile", { nullable: true })], AuditLog.prototype, "onBehalfOfId", 2);
91
+ AuditLog = __decorateClass([TenantScoped({
92
+ mode: "optional",
93
+ allowSuperAdminBypass: true
94
+ }), smrt({
95
+ tableName: "audit_logs",
96
+ api: { include: ["list", "get"] },
97
+ mcp: { include: ["list", "get"] },
98
+ cli: { include: ["list"] }
99
+ })], AuditLog);
100
+ var AuditLogCollection_exports = /* @__PURE__ */ __exportAll$1({ AuditLogCollection: () => AuditLogCollection });
101
+ var AuditLogCollection = class extends SmrtCollection {
102
+ static _itemClass = AuditLog;
103
+ /**
104
+ * Find logs for a profile (actions they performed)
105
+ */
106
+ async findByProfile(profileId) {
107
+ return await this.list({
108
+ where: { profileId },
109
+ orderBy: "createdAt DESC"
110
+ });
111
+ }
112
+ /**
113
+ * Find logs for a resource
114
+ */
115
+ async findByResource(resourceType, resourceId) {
116
+ return await this.list({
117
+ where: {
118
+ resourceType,
119
+ resourceId
120
+ },
121
+ orderBy: "createdAt DESC"
122
+ });
123
+ }
124
+ /**
125
+ * Find logs by action type
126
+ */
127
+ async findByAction(action) {
128
+ return await this.list({
129
+ where: { action },
130
+ orderBy: "createdAt DESC"
131
+ });
132
+ }
133
+ /**
134
+ * Find logs by source
135
+ */
136
+ async findBySource(source) {
137
+ return await this.list({
138
+ where: { source },
139
+ orderBy: "createdAt DESC"
140
+ });
141
+ }
142
+ /**
143
+ * Find logs where actions were performed on behalf of someone
144
+ */
145
+ async findOnBehalfOf(profileId) {
146
+ return await this.list({
147
+ where: { onBehalfOfId: profileId },
148
+ orderBy: "createdAt DESC"
149
+ });
150
+ }
151
+ /**
152
+ * Record a new audit log entry
153
+ */
154
+ async record(options) {
155
+ const log = new AuditLog({
156
+ ...this.options,
157
+ profileId: options.profile.id,
158
+ action: options.action,
159
+ resourceType: options.resourceType,
160
+ resourceId: options.resourceId,
161
+ source: options.source || "web",
162
+ metadata: options.metadata || {},
163
+ onBehalfOfId: options.onBehalfOf?.id
164
+ });
165
+ await log.initialize();
166
+ await log.save();
167
+ return log;
168
+ }
169
+ /**
170
+ * Get recent activity for a profile
171
+ */
172
+ async getRecentActivity(profileId, limit = 50) {
173
+ return await this.list({
174
+ where: { profileId },
175
+ orderBy: "createdAt DESC",
176
+ limit
177
+ });
178
+ }
179
+ /**
180
+ * Get activity timeline for a resource
181
+ */
182
+ async getResourceTimeline(resourceType, resourceId) {
183
+ return await this.list({
184
+ where: {
185
+ resourceType,
186
+ resourceId
187
+ },
188
+ orderBy: "createdAt ASC"
189
+ });
190
+ }
191
+ };
192
+ //#endregion
193
+ export { AuditLogCollection as n, AuditLogCollection_B1YonCAP_exports as r, AuditLog as t };
@@ -4097,21 +4097,21 @@ var NostrIdentity = class extends SmrtObject {
4097
4097
  * Find identity by public key
4098
4098
  */
4099
4099
  static async findByPubkey(pubkey, options = {}) {
4100
- const { NostrIdentityCollection } = await import("./NostrIdentityCollection-Ca74dkaL-C2r-uDlC.js").then((n) => n.n);
4100
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-Ca74dkaL-DvjOg9Xy.js").then((n) => n.n).then((n) => n.n);
4101
4101
  return await (await NostrIdentityCollection.create(options)).findOne({ where: { pubkey } });
4102
4102
  }
4103
4103
  /**
4104
4104
  * Find identity by email
4105
4105
  */
4106
4106
  static async findByEmail(email, options = {}) {
4107
- const { NostrIdentityCollection } = await import("./NostrIdentityCollection-Ca74dkaL-C2r-uDlC.js").then((n) => n.n);
4107
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-Ca74dkaL-DvjOg9Xy.js").then((n) => n.n).then((n) => n.n);
4108
4108
  return await (await NostrIdentityCollection.create(options)).findOne({ where: { email: email.toLowerCase() } });
4109
4109
  }
4110
4110
  /**
4111
4111
  * Find identity by NIP-05 username
4112
4112
  */
4113
4113
  static async findByNip05(username, options = {}) {
4114
- const { NostrIdentityCollection } = await import("./NostrIdentityCollection-Ca74dkaL-C2r-uDlC.js").then((n) => n.n);
4114
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-Ca74dkaL-DvjOg9Xy.js").then((n) => n.n).then((n) => n.n);
4115
4115
  return await (await NostrIdentityCollection.create(options)).findOne({ where: { nip05Username: username.toLowerCase() } });
4116
4116
  }
4117
4117
  /**
@@ -4186,4 +4186,4 @@ NostrIdentity = __decorateClass([smrt({
4186
4186
  cli: { include: ["list", "get"] }
4187
4187
  })], NostrIdentity);
4188
4188
  //#endregion
4189
- export { verifyAuthEvent as a, generateNostrKeypair as i, __exportAll as n, encryptPrivkey as r, NostrIdentity as t };
4189
+ export { deriveEncryptionKey as a, privkeyToNsec as c, verifyNostrSignature as d, decryptPrivkey as i, pubkeyToNpub as l, __exportAll as n, encryptPrivkey as o, computeEventId as r, generateNostrKeypair as s, NostrIdentity as t, verifyAuthEvent as u };