@synoikia/core 0.6.0 → 0.8.0

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 (103) hide show
  1. package/dist/app.d.ts +8 -0
  2. package/dist/app.js +16 -4
  3. package/dist/app.js.map +1 -1
  4. package/dist/approvals/grants.d.ts +34 -0
  5. package/dist/approvals/grants.js +79 -0
  6. package/dist/approvals/grants.js.map +1 -0
  7. package/dist/approvals/service.d.ts +24 -1
  8. package/dist/approvals/service.js +24 -2
  9. package/dist/approvals/service.js.map +1 -1
  10. package/dist/auth/mcp-auth.d.ts +8 -0
  11. package/dist/auth/mcp-auth.js +82 -33
  12. package/dist/auth/mcp-auth.js.map +1 -1
  13. package/dist/auth/mcp-tokens.d.ts +9 -3
  14. package/dist/auth/mcp-tokens.js +24 -4
  15. package/dist/auth/mcp-tokens.js.map +1 -1
  16. package/dist/auth/oauth.d.ts +5 -1
  17. package/dist/auth/oauth.js +15 -1
  18. package/dist/auth/oauth.js.map +1 -1
  19. package/dist/auth/oidc.js +3 -2
  20. package/dist/auth/oidc.js.map +1 -1
  21. package/dist/auth/roles.d.ts +43 -0
  22. package/dist/auth/roles.js +164 -0
  23. package/dist/auth/roles.js.map +1 -0
  24. package/dist/auth/sessions.d.ts +7 -0
  25. package/dist/auth/sessions.js +18 -2
  26. package/dist/auth/sessions.js.map +1 -1
  27. package/dist/auth/users.d.ts +26 -1
  28. package/dist/auth/users.js +109 -12
  29. package/dist/auth/users.js.map +1 -1
  30. package/dist/catalog/groups.js +2 -1
  31. package/dist/catalog/groups.js.map +1 -1
  32. package/dist/catalog/role-levels.d.ts +67 -0
  33. package/dist/catalog/role-levels.js +297 -0
  34. package/dist/catalog/role-levels.js.map +1 -0
  35. package/dist/catalog/rules.d.ts +33 -9
  36. package/dist/catalog/rules.js +51 -25
  37. package/dist/catalog/rules.js.map +1 -1
  38. package/dist/db/schema.d.ts +566 -0
  39. package/dist/db/schema.js +64 -0
  40. package/dist/db/schema.js.map +1 -1
  41. package/dist/errors.d.ts +5 -2
  42. package/dist/errors.js +5 -0
  43. package/dist/errors.js.map +1 -1
  44. package/dist/gate/access.d.ts +23 -2
  45. package/dist/gate/access.js +31 -3
  46. package/dist/gate/access.js.map +1 -1
  47. package/dist/gate/pipeline.d.ts +25 -5
  48. package/dist/gate/pipeline.js +67 -6
  49. package/dist/gate/pipeline.js.map +1 -1
  50. package/dist/gate/preapproval.d.ts +2 -0
  51. package/dist/gate/preapproval.js +16 -3
  52. package/dist/gate/preapproval.js.map +1 -1
  53. package/dist/http/admin/auth.d.ts +4 -0
  54. package/dist/http/admin/auth.js +53 -4
  55. package/dist/http/admin/auth.js.map +1 -1
  56. package/dist/http/admin/instances.js +19 -1
  57. package/dist/http/admin/instances.js.map +1 -1
  58. package/dist/http/admin/me.d.ts +6 -0
  59. package/dist/http/admin/me.js +189 -0
  60. package/dist/http/admin/me.js.map +1 -0
  61. package/dist/http/admin/roles.d.ts +5 -0
  62. package/dist/http/admin/roles.js +45 -0
  63. package/dist/http/admin/roles.js.map +1 -0
  64. package/dist/http/admin/system.js +11 -6
  65. package/dist/http/admin/system.js.map +1 -1
  66. package/dist/http/admin-app.js +13 -2
  67. package/dist/http/admin-app.js.map +1 -1
  68. package/dist/http/common.d.ts +1 -1
  69. package/dist/http/mcp/approval-card.d.ts +6 -0
  70. package/dist/http/mcp/approval-card.js +160 -0
  71. package/dist/http/mcp/approval-card.js.map +1 -0
  72. package/dist/http/mcp/approval-routes.d.ts +2 -2
  73. package/dist/http/mcp/approval-routes.js +40 -4
  74. package/dist/http/mcp/approval-routes.js.map +1 -1
  75. package/dist/http/mcp/endpoint.js +104 -10
  76. package/dist/http/mcp/endpoint.js.map +1 -1
  77. package/dist/http/mcp/oauth-routes.js +22 -8
  78. package/dist/http/mcp/oauth-routes.js.map +1 -1
  79. package/dist/http/mcp/pages.d.ts +8 -1
  80. package/dist/http/mcp/pages.js +21 -2
  81. package/dist/http/mcp/pages.js.map +1 -1
  82. package/dist/instances/manager.d.ts +4 -0
  83. package/dist/instances/settings.d.ts +2 -0
  84. package/dist/instances/settings.js +2 -0
  85. package/dist/instances/settings.js.map +1 -1
  86. package/dist/runtime/executions.d.ts +68 -0
  87. package/dist/runtime/executions.js +158 -0
  88. package/dist/runtime/executions.js.map +1 -0
  89. package/dist/runtime/index.d.ts +4 -1
  90. package/dist/runtime/index.js +131 -25
  91. package/dist/runtime/index.js.map +1 -1
  92. package/dist/settings.d.ts +4 -0
  93. package/dist/settings.js +12 -0
  94. package/dist/settings.js.map +1 -1
  95. package/dist/testing/index.d.ts +11 -1
  96. package/dist/testing/index.js +23 -7
  97. package/dist/testing/index.js.map +1 -1
  98. package/drizzle/0013_user_roles.sql +63 -0
  99. package/drizzle/0014_user_registration_source.sql +1 -0
  100. package/drizzle/meta/0013_snapshot.json +2833 -0
  101. package/drizzle/meta/0014_snapshot.json +2840 -0
  102. package/drizzle/meta/_journal.json +14 -0
  103. package/package.json +3 -3
@@ -1,3 +1,150 @@
1
+ export declare const roles: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
+ name: "roles";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
+ name: "id";
7
+ tableName: "roles";
8
+ dataType: "string";
9
+ columnType: "SQLiteText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {
22
+ length: number | undefined;
23
+ }>;
24
+ name: import("drizzle-orm/sqlite-core").SQLiteColumn<{
25
+ name: "name";
26
+ tableName: "roles";
27
+ dataType: "string";
28
+ columnType: "SQLiteText";
29
+ data: string;
30
+ driverParam: string;
31
+ notNull: true;
32
+ hasDefault: false;
33
+ isPrimaryKey: false;
34
+ isAutoincrement: false;
35
+ hasRuntimeDefault: false;
36
+ enumValues: [string, ...string[]];
37
+ baseColumn: never;
38
+ identity: undefined;
39
+ generated: undefined;
40
+ }, {}, {
41
+ length: number | undefined;
42
+ }>;
43
+ builtIn: import("drizzle-orm/sqlite-core").SQLiteColumn<{
44
+ name: string;
45
+ tableName: "roles";
46
+ dataType: "boolean";
47
+ columnType: "SQLiteBoolean";
48
+ data: boolean;
49
+ driverParam: number;
50
+ notNull: true;
51
+ hasDefault: true;
52
+ isPrimaryKey: false;
53
+ isAutoincrement: false;
54
+ hasRuntimeDefault: false;
55
+ enumValues: undefined;
56
+ baseColumn: never;
57
+ identity: undefined;
58
+ generated: undefined;
59
+ }, {}, {}>;
60
+ canSetOwnLevels: import("drizzle-orm/sqlite-core").SQLiteColumn<{
61
+ name: string;
62
+ tableName: "roles";
63
+ dataType: "boolean";
64
+ columnType: "SQLiteBoolean";
65
+ data: boolean;
66
+ driverParam: number;
67
+ notNull: true;
68
+ hasDefault: true;
69
+ isPrimaryKey: false;
70
+ isAutoincrement: false;
71
+ hasRuntimeDefault: false;
72
+ enumValues: undefined;
73
+ baseColumn: never;
74
+ identity: undefined;
75
+ generated: undefined;
76
+ }, {}, {}>;
77
+ canManageOwnRules: import("drizzle-orm/sqlite-core").SQLiteColumn<{
78
+ name: string;
79
+ tableName: "roles";
80
+ dataType: "boolean";
81
+ columnType: "SQLiteBoolean";
82
+ data: boolean;
83
+ driverParam: number;
84
+ notNull: true;
85
+ hasDefault: true;
86
+ isPrimaryKey: false;
87
+ isAutoincrement: false;
88
+ hasRuntimeDefault: false;
89
+ enumValues: undefined;
90
+ baseColumn: never;
91
+ identity: undefined;
92
+ generated: undefined;
93
+ }, {}, {}>;
94
+ canSeeStatus: import("drizzle-orm/sqlite-core").SQLiteColumn<{
95
+ name: string;
96
+ tableName: "roles";
97
+ dataType: "boolean";
98
+ columnType: "SQLiteBoolean";
99
+ data: boolean;
100
+ driverParam: number;
101
+ notNull: true;
102
+ hasDefault: true;
103
+ isPrimaryKey: false;
104
+ isAutoincrement: false;
105
+ hasRuntimeDefault: false;
106
+ enumValues: undefined;
107
+ baseColumn: never;
108
+ identity: undefined;
109
+ generated: undefined;
110
+ }, {}, {}>;
111
+ createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
112
+ name: string;
113
+ tableName: "roles";
114
+ dataType: "date";
115
+ columnType: "SQLiteTimestamp";
116
+ data: Date;
117
+ driverParam: number;
118
+ notNull: true;
119
+ hasDefault: true;
120
+ isPrimaryKey: false;
121
+ isAutoincrement: false;
122
+ hasRuntimeDefault: false;
123
+ enumValues: undefined;
124
+ baseColumn: never;
125
+ identity: undefined;
126
+ generated: undefined;
127
+ }, {}, {}>;
128
+ updatedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
129
+ name: string;
130
+ tableName: "roles";
131
+ dataType: "date";
132
+ columnType: "SQLiteTimestamp";
133
+ data: Date;
134
+ driverParam: number;
135
+ notNull: false;
136
+ hasDefault: false;
137
+ isPrimaryKey: false;
138
+ isAutoincrement: false;
139
+ hasRuntimeDefault: false;
140
+ enumValues: undefined;
141
+ baseColumn: never;
142
+ identity: undefined;
143
+ generated: undefined;
144
+ }, {}, {}>;
145
+ };
146
+ dialect: "sqlite";
147
+ }>;
1
148
  export declare const users: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
149
  name: "users";
3
150
  schema: undefined;
@@ -218,6 +365,44 @@ export declare const users: import("drizzle-orm/sqlite-core").SQLiteTableWithCol
218
365
  identity: undefined;
219
366
  generated: undefined;
220
367
  }, {}, {}>;
368
+ roleId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
369
+ name: "role_id";
370
+ tableName: "users";
371
+ dataType: "string";
372
+ columnType: "SQLiteText";
373
+ data: string;
374
+ driverParam: string;
375
+ notNull: true;
376
+ hasDefault: true;
377
+ isPrimaryKey: false;
378
+ isAutoincrement: false;
379
+ hasRuntimeDefault: false;
380
+ enumValues: [string, ...string[]];
381
+ baseColumn: never;
382
+ identity: undefined;
383
+ generated: undefined;
384
+ }, {}, {
385
+ length: number | undefined;
386
+ }>;
387
+ registeredVia: import("drizzle-orm/sqlite-core").SQLiteColumn<{
388
+ name: "registered_via";
389
+ tableName: "users";
390
+ dataType: "string";
391
+ columnType: "SQLiteText";
392
+ data: "oidc" | "external" | "signup";
393
+ driverParam: string;
394
+ notNull: false;
395
+ hasDefault: false;
396
+ isPrimaryKey: false;
397
+ isAutoincrement: false;
398
+ hasRuntimeDefault: false;
399
+ enumValues: ["oidc", "external", "signup"];
400
+ baseColumn: never;
401
+ identity: undefined;
402
+ generated: undefined;
403
+ }, {}, {
404
+ length: number | undefined;
405
+ }>;
221
406
  };
222
407
  dialect: "sqlite";
223
408
  }>;
@@ -2584,6 +2769,25 @@ export declare const preApprovalRules: import("drizzle-orm/sqlite-core").SQLiteT
2584
2769
  }, {}, {
2585
2770
  length: number | undefined;
2586
2771
  }>;
2772
+ ownerUserId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2773
+ name: "owner_user_id";
2774
+ tableName: "pre_approval_rules";
2775
+ dataType: "string";
2776
+ columnType: "SQLiteText";
2777
+ data: string;
2778
+ driverParam: string;
2779
+ notNull: false;
2780
+ hasDefault: false;
2781
+ isPrimaryKey: false;
2782
+ isAutoincrement: false;
2783
+ hasRuntimeDefault: false;
2784
+ enumValues: [string, ...string[]];
2785
+ baseColumn: never;
2786
+ identity: undefined;
2787
+ generated: undefined;
2788
+ }, {}, {
2789
+ length: number | undefined;
2790
+ }>;
2587
2791
  createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2588
2792
  name: string;
2589
2793
  tableName: "pre_approval_rules";
@@ -3068,6 +3272,25 @@ export declare const pendingApprovals: import("drizzle-orm/sqlite-core").SQLiteT
3068
3272
  identity: undefined;
3069
3273
  generated: undefined;
3070
3274
  }, {}, {}>;
3275
+ ownerUserId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3276
+ name: "owner_user_id";
3277
+ tableName: "pending_approvals";
3278
+ dataType: "string";
3279
+ columnType: "SQLiteText";
3280
+ data: string;
3281
+ driverParam: string;
3282
+ notNull: false;
3283
+ hasDefault: false;
3284
+ isPrimaryKey: false;
3285
+ isAutoincrement: false;
3286
+ hasRuntimeDefault: false;
3287
+ enumValues: [string, ...string[]];
3288
+ baseColumn: never;
3289
+ identity: undefined;
3290
+ generated: undefined;
3291
+ }, {}, {
3292
+ length: number | undefined;
3293
+ }>;
3071
3294
  };
3072
3295
  dialect: "sqlite";
3073
3296
  }>;
@@ -3371,6 +3594,349 @@ export declare const auditLog: import("drizzle-orm/sqlite-core").SQLiteTableWith
3371
3594
  };
3372
3595
  dialect: "sqlite";
3373
3596
  }>;
3597
+ /** The endpoints a role has; outside them its users reach nothing. */
3598
+ export declare const roleInstances: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
3599
+ name: "role_instances";
3600
+ schema: undefined;
3601
+ columns: {
3602
+ roleId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3603
+ name: "role_id";
3604
+ tableName: "role_instances";
3605
+ dataType: "string";
3606
+ columnType: "SQLiteText";
3607
+ data: string;
3608
+ driverParam: string;
3609
+ notNull: true;
3610
+ hasDefault: false;
3611
+ isPrimaryKey: false;
3612
+ isAutoincrement: false;
3613
+ hasRuntimeDefault: false;
3614
+ enumValues: [string, ...string[]];
3615
+ baseColumn: never;
3616
+ identity: undefined;
3617
+ generated: undefined;
3618
+ }, {}, {
3619
+ length: number | undefined;
3620
+ }>;
3621
+ instanceId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3622
+ name: "instance_id";
3623
+ tableName: "role_instances";
3624
+ dataType: "string";
3625
+ columnType: "SQLiteText";
3626
+ data: string;
3627
+ driverParam: string;
3628
+ notNull: true;
3629
+ hasDefault: false;
3630
+ isPrimaryKey: false;
3631
+ isAutoincrement: false;
3632
+ hasRuntimeDefault: false;
3633
+ enumValues: [string, ...string[]];
3634
+ baseColumn: never;
3635
+ identity: undefined;
3636
+ generated: undefined;
3637
+ }, {}, {
3638
+ length: number | undefined;
3639
+ }>;
3640
+ };
3641
+ dialect: "sqlite";
3642
+ }>;
3643
+ /** A role's maximum level for one group or one operation (exactly one is set); no row means None. */
3644
+ export declare const roleLevels: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
3645
+ name: "role_levels";
3646
+ schema: undefined;
3647
+ columns: {
3648
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3649
+ name: "id";
3650
+ tableName: "role_levels";
3651
+ dataType: "string";
3652
+ columnType: "SQLiteText";
3653
+ data: string;
3654
+ driverParam: string;
3655
+ notNull: true;
3656
+ hasDefault: false;
3657
+ isPrimaryKey: true;
3658
+ isAutoincrement: false;
3659
+ hasRuntimeDefault: false;
3660
+ enumValues: [string, ...string[]];
3661
+ baseColumn: never;
3662
+ identity: undefined;
3663
+ generated: undefined;
3664
+ }, {}, {
3665
+ length: number | undefined;
3666
+ }>;
3667
+ roleId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3668
+ name: "role_id";
3669
+ tableName: "role_levels";
3670
+ dataType: "string";
3671
+ columnType: "SQLiteText";
3672
+ data: string;
3673
+ driverParam: string;
3674
+ notNull: true;
3675
+ hasDefault: false;
3676
+ isPrimaryKey: false;
3677
+ isAutoincrement: false;
3678
+ hasRuntimeDefault: false;
3679
+ enumValues: [string, ...string[]];
3680
+ baseColumn: never;
3681
+ identity: undefined;
3682
+ generated: undefined;
3683
+ }, {}, {
3684
+ length: number | undefined;
3685
+ }>;
3686
+ instanceId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3687
+ name: "instance_id";
3688
+ tableName: "role_levels";
3689
+ dataType: "string";
3690
+ columnType: "SQLiteText";
3691
+ data: string;
3692
+ driverParam: string;
3693
+ notNull: true;
3694
+ hasDefault: false;
3695
+ isPrimaryKey: false;
3696
+ isAutoincrement: false;
3697
+ hasRuntimeDefault: false;
3698
+ enumValues: [string, ...string[]];
3699
+ baseColumn: never;
3700
+ identity: undefined;
3701
+ generated: undefined;
3702
+ }, {}, {
3703
+ length: number | undefined;
3704
+ }>;
3705
+ groupId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3706
+ name: "group_id";
3707
+ tableName: "role_levels";
3708
+ dataType: "string";
3709
+ columnType: "SQLiteText";
3710
+ data: string;
3711
+ driverParam: string;
3712
+ notNull: false;
3713
+ hasDefault: false;
3714
+ isPrimaryKey: false;
3715
+ isAutoincrement: false;
3716
+ hasRuntimeDefault: false;
3717
+ enumValues: [string, ...string[]];
3718
+ baseColumn: never;
3719
+ identity: undefined;
3720
+ generated: undefined;
3721
+ }, {}, {
3722
+ length: number | undefined;
3723
+ }>;
3724
+ operationId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3725
+ name: "operation_id";
3726
+ tableName: "role_levels";
3727
+ dataType: "string";
3728
+ columnType: "SQLiteText";
3729
+ data: string;
3730
+ driverParam: string;
3731
+ notNull: false;
3732
+ hasDefault: false;
3733
+ isPrimaryKey: false;
3734
+ isAutoincrement: false;
3735
+ hasRuntimeDefault: false;
3736
+ enumValues: [string, ...string[]];
3737
+ baseColumn: never;
3738
+ identity: undefined;
3739
+ generated: undefined;
3740
+ }, {}, {
3741
+ length: number | undefined;
3742
+ }>;
3743
+ level: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3744
+ name: "level";
3745
+ tableName: "role_levels";
3746
+ dataType: "string";
3747
+ columnType: "SQLiteText";
3748
+ data: "none" | "read" | "ask" | "write";
3749
+ driverParam: string;
3750
+ notNull: true;
3751
+ hasDefault: false;
3752
+ isPrimaryKey: false;
3753
+ isAutoincrement: false;
3754
+ hasRuntimeDefault: false;
3755
+ enumValues: ["none", "read", "ask", "write"];
3756
+ baseColumn: never;
3757
+ identity: undefined;
3758
+ generated: undefined;
3759
+ }, {}, {
3760
+ length: number | undefined;
3761
+ }>;
3762
+ changedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3763
+ name: string;
3764
+ tableName: "role_levels";
3765
+ dataType: "date";
3766
+ columnType: "SQLiteTimestamp";
3767
+ data: Date;
3768
+ driverParam: number;
3769
+ notNull: false;
3770
+ hasDefault: false;
3771
+ isPrimaryKey: false;
3772
+ isAutoincrement: false;
3773
+ hasRuntimeDefault: false;
3774
+ enumValues: undefined;
3775
+ baseColumn: never;
3776
+ identity: undefined;
3777
+ generated: undefined;
3778
+ }, {}, {}>;
3779
+ changedBy: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3780
+ name: "changed_by";
3781
+ tableName: "role_levels";
3782
+ dataType: "string";
3783
+ columnType: "SQLiteText";
3784
+ data: string;
3785
+ driverParam: string;
3786
+ notNull: false;
3787
+ hasDefault: false;
3788
+ isPrimaryKey: false;
3789
+ isAutoincrement: false;
3790
+ hasRuntimeDefault: false;
3791
+ enumValues: [string, ...string[]];
3792
+ baseColumn: never;
3793
+ identity: undefined;
3794
+ generated: undefined;
3795
+ }, {}, {
3796
+ length: number | undefined;
3797
+ }>;
3798
+ };
3799
+ dialect: "sqlite";
3800
+ }>;
3801
+ /** A user's own level for one group or one operation, capped by their role's maximum. */
3802
+ export declare const userLevels: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
3803
+ name: "user_levels";
3804
+ schema: undefined;
3805
+ columns: {
3806
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3807
+ name: "id";
3808
+ tableName: "user_levels";
3809
+ dataType: "string";
3810
+ columnType: "SQLiteText";
3811
+ data: string;
3812
+ driverParam: string;
3813
+ notNull: true;
3814
+ hasDefault: false;
3815
+ isPrimaryKey: true;
3816
+ isAutoincrement: false;
3817
+ hasRuntimeDefault: false;
3818
+ enumValues: [string, ...string[]];
3819
+ baseColumn: never;
3820
+ identity: undefined;
3821
+ generated: undefined;
3822
+ }, {}, {
3823
+ length: number | undefined;
3824
+ }>;
3825
+ userId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3826
+ name: "user_id";
3827
+ tableName: "user_levels";
3828
+ dataType: "string";
3829
+ columnType: "SQLiteText";
3830
+ data: string;
3831
+ driverParam: string;
3832
+ notNull: true;
3833
+ hasDefault: false;
3834
+ isPrimaryKey: false;
3835
+ isAutoincrement: false;
3836
+ hasRuntimeDefault: false;
3837
+ enumValues: [string, ...string[]];
3838
+ baseColumn: never;
3839
+ identity: undefined;
3840
+ generated: undefined;
3841
+ }, {}, {
3842
+ length: number | undefined;
3843
+ }>;
3844
+ instanceId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3845
+ name: "instance_id";
3846
+ tableName: "user_levels";
3847
+ dataType: "string";
3848
+ columnType: "SQLiteText";
3849
+ data: string;
3850
+ driverParam: string;
3851
+ notNull: true;
3852
+ hasDefault: false;
3853
+ isPrimaryKey: false;
3854
+ isAutoincrement: false;
3855
+ hasRuntimeDefault: false;
3856
+ enumValues: [string, ...string[]];
3857
+ baseColumn: never;
3858
+ identity: undefined;
3859
+ generated: undefined;
3860
+ }, {}, {
3861
+ length: number | undefined;
3862
+ }>;
3863
+ groupId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3864
+ name: "group_id";
3865
+ tableName: "user_levels";
3866
+ dataType: "string";
3867
+ columnType: "SQLiteText";
3868
+ data: string;
3869
+ driverParam: string;
3870
+ notNull: false;
3871
+ hasDefault: false;
3872
+ isPrimaryKey: false;
3873
+ isAutoincrement: false;
3874
+ hasRuntimeDefault: false;
3875
+ enumValues: [string, ...string[]];
3876
+ baseColumn: never;
3877
+ identity: undefined;
3878
+ generated: undefined;
3879
+ }, {}, {
3880
+ length: number | undefined;
3881
+ }>;
3882
+ operationId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3883
+ name: "operation_id";
3884
+ tableName: "user_levels";
3885
+ dataType: "string";
3886
+ columnType: "SQLiteText";
3887
+ data: string;
3888
+ driverParam: string;
3889
+ notNull: false;
3890
+ hasDefault: false;
3891
+ isPrimaryKey: false;
3892
+ isAutoincrement: false;
3893
+ hasRuntimeDefault: false;
3894
+ enumValues: [string, ...string[]];
3895
+ baseColumn: never;
3896
+ identity: undefined;
3897
+ generated: undefined;
3898
+ }, {}, {
3899
+ length: number | undefined;
3900
+ }>;
3901
+ level: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3902
+ name: "level";
3903
+ tableName: "user_levels";
3904
+ dataType: "string";
3905
+ columnType: "SQLiteText";
3906
+ data: "none" | "read" | "ask" | "write";
3907
+ driverParam: string;
3908
+ notNull: true;
3909
+ hasDefault: false;
3910
+ isPrimaryKey: false;
3911
+ isAutoincrement: false;
3912
+ hasRuntimeDefault: false;
3913
+ enumValues: ["none", "read", "ask", "write"];
3914
+ baseColumn: never;
3915
+ identity: undefined;
3916
+ generated: undefined;
3917
+ }, {}, {
3918
+ length: number | undefined;
3919
+ }>;
3920
+ changedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3921
+ name: string;
3922
+ tableName: "user_levels";
3923
+ dataType: "date";
3924
+ columnType: "SQLiteTimestamp";
3925
+ data: Date;
3926
+ driverParam: number;
3927
+ notNull: false;
3928
+ hasDefault: false;
3929
+ isPrimaryKey: false;
3930
+ isAutoincrement: false;
3931
+ hasRuntimeDefault: false;
3932
+ enumValues: undefined;
3933
+ baseColumn: never;
3934
+ identity: undefined;
3935
+ generated: undefined;
3936
+ }, {}, {}>;
3937
+ };
3938
+ dialect: "sqlite";
3939
+ }>;
3374
3940
  export declare const mcpTokens: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
3375
3941
  name: "mcp_tokens";
3376
3942
  schema: undefined;
package/dist/db/schema.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { sql } from 'drizzle-orm';
2
2
  import { blob, index, integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
3
+ import { ADMIN_ROLE_ID } from '../gate/access.js';
3
4
  /** The SQLite schema (design §7.1): JSON as TEXT, timestamps as epoch ms, secrets as AES-256-GCM blobs. */
4
5
  const id = () => text('id').primaryKey();
5
6
  const ts = (name) => integer(name, { mode: 'timestamp_ms' });
@@ -9,6 +10,16 @@ const createdAt = () => ts('created_at')
9
10
  const flag = (name) => integer(name, { mode: 'boolean' });
10
11
  const json = (name) => text(name, { mode: 'json' });
11
12
  // ── identity & admin auth ────────────────────────────────────────────────────────────────────────
13
+ export const roles = sqliteTable('roles', {
14
+ id: id(),
15
+ name: text('name').notNull().unique(),
16
+ builtIn: flag('built_in').notNull().default(false),
17
+ canSetOwnLevels: flag('can_set_own_levels').notNull().default(false),
18
+ canManageOwnRules: flag('can_manage_own_rules').notNull().default(false),
19
+ canSeeStatus: flag('can_see_status').notNull().default(false),
20
+ createdAt: createdAt(),
21
+ updatedAt: ts('updated_at'),
22
+ });
12
23
  export const users = sqliteTable('users', {
13
24
  id: id(),
14
25
  username: text('username').notNull().unique(),
@@ -23,6 +34,10 @@ export const users = sqliteTable('users', {
23
34
  createdAt: createdAt(),
24
35
  lastLoginAt: ts('last_login_at'),
25
36
  disabled: flag('disabled').notNull().default(false),
37
+ /** References `roles`; SQLite can't add that constraint to an existing table, so RoleService keeps it. */
38
+ roleId: text('role_id').notNull().default(ADMIN_ROLE_ID),
39
+ /** How the user came to exist without an admin (design §6.5); null: an admin or setup made it. */
40
+ registeredVia: text('registered_via', { enum: ['oidc', 'external', 'signup'] }),
26
41
  });
27
42
  export const sessions = sqliteTable('sessions', {
28
43
  idHash: text('id_hash').primaryKey(),
@@ -217,6 +232,8 @@ export const preApprovalRules = sqliteTable('pre_approval_rules', {
217
232
  reason: text('reason').notNull(),
218
233
  enabled: flag('enabled').notNull().default(true),
219
234
  createdBy: text('created_by').references(() => users.id),
235
+ /** Null: an admin rule for every caller. Set: applies only to this user's calls (design §6.4). */
236
+ ownerUserId: text('owner_user_id').references(() => users.id),
220
237
  createdAt: createdAt(),
221
238
  updatedAt: ts('updated_at'),
222
239
  lastTriggeredAt: ts('last_triggered_at'),
@@ -258,6 +275,8 @@ export const pendingApprovals = sqliteTable('pending_approvals', {
258
275
  /** `url`: decided by a signed-in human on the approval page; `elicitation`: by the MCP client (opt-in). */
259
276
  decidedVia: text('decided_via', { enum: ['elicitation', 'url'] }),
260
277
  decidedAt: ts('decided_at'),
278
+ /** The user who owns the calling credential: the only one who may decide on the page (design §5.3). */
279
+ ownerUserId: text('owner_user_id').references(() => users.id),
261
280
  });
262
281
  /** Append-only: no API updates or deletes rows (design §7.3). */
263
282
  export const auditLog = sqliteTable('audit_log', {
@@ -278,6 +297,51 @@ export const auditLog = sqliteTable('audit_log', {
278
297
  durationMs: integer('duration_ms'),
279
298
  detail: json('detail'),
280
299
  }, (t) => [index('audit_at_idx').on(t.at), index('audit_instance_at_idx').on(t.instanceId, t.at)]);
300
+ // ── roles (design §6.4) ──────────────────────────────────────────────────────────────────────────
301
+ /** The endpoints a role has; outside them its users reach nothing. */
302
+ export const roleInstances = sqliteTable('role_instances', {
303
+ roleId: text('role_id')
304
+ .notNull()
305
+ .references(() => roles.id, { onDelete: 'cascade' }),
306
+ instanceId: text('instance_id')
307
+ .notNull()
308
+ .references(() => pluginInstances.id, { onDelete: 'cascade' }),
309
+ }, (t) => [uniqueIndex('role_instances_idx').on(t.roleId, t.instanceId)]);
310
+ /** A role's maximum level for one group or one operation (exactly one is set); no row means None. */
311
+ export const roleLevels = sqliteTable('role_levels', {
312
+ id: id(),
313
+ roleId: text('role_id')
314
+ .notNull()
315
+ .references(() => roles.id, { onDelete: 'cascade' }),
316
+ instanceId: text('instance_id')
317
+ .notNull()
318
+ .references(() => pluginInstances.id, { onDelete: 'cascade' }),
319
+ groupId: text('group_id').references(() => operationGroups.id, { onDelete: 'cascade' }),
320
+ operationId: text('operation_id').references(() => operations.id, { onDelete: 'cascade' }),
321
+ level: text('level', { enum: ['none', 'read', 'ask', 'write'] }).notNull(),
322
+ changedAt: ts('changed_at'),
323
+ changedBy: text('changed_by').references(() => users.id, { onDelete: 'set null' }),
324
+ }, (t) => [
325
+ uniqueIndex('role_levels_group_idx').on(t.roleId, t.groupId),
326
+ uniqueIndex('role_levels_op_idx').on(t.roleId, t.operationId),
327
+ ]);
328
+ /** A user's own level for one group or one operation, capped by their role's maximum. */
329
+ export const userLevels = sqliteTable('user_levels', {
330
+ id: id(),
331
+ userId: text('user_id')
332
+ .notNull()
333
+ .references(() => users.id, { onDelete: 'cascade' }),
334
+ instanceId: text('instance_id')
335
+ .notNull()
336
+ .references(() => pluginInstances.id, { onDelete: 'cascade' }),
337
+ groupId: text('group_id').references(() => operationGroups.id, { onDelete: 'cascade' }),
338
+ operationId: text('operation_id').references(() => operations.id, { onDelete: 'cascade' }),
339
+ level: text('level', { enum: ['none', 'read', 'ask', 'write'] }).notNull(),
340
+ changedAt: ts('changed_at'),
341
+ }, (t) => [
342
+ uniqueIndex('user_levels_group_idx').on(t.userId, t.groupId),
343
+ uniqueIndex('user_levels_op_idx').on(t.userId, t.operationId),
344
+ ]);
281
345
  // ── MCP client auth ──────────────────────────────────────────────────────────────────────────────
282
346
  export const mcpTokens = sqliteTable('mcp_tokens', {
283
347
  id: id(),