@karmaniverous/jeeves-server-core 0.1.1 → 0.1.3
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/dist/src/crypto.d.ts +0 -1
- package/dist/src/crypto.js +9 -6
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +2 -6
- package/dist/src/schema.d.ts +168 -0
- package/dist/src/schema.js +252 -0
- package/dist/src/types.d.ts +0 -1
- package/package.json +25 -5
- package/dist/src/crypto.d.ts.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js +0 -6
package/dist/src/crypto.d.ts
CHANGED
package/dist/src/crypto.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Cryptographic utilities for HMAC key computation and verification.
|
|
3
5
|
*
|
|
4
6
|
* @packageDocumentation
|
|
5
7
|
*/
|
|
6
|
-
import crypto from 'node:crypto';
|
|
7
8
|
/**
|
|
8
9
|
* Compute path-specific key: HMAC-SHA256(apiKey, normalizedPath)
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
function computePathKey(apiKey, urlPath) {
|
|
11
12
|
const normalized = urlPath.toLowerCase().replace(/^\/+|\/+$/g, '');
|
|
12
13
|
const hash = crypto
|
|
13
14
|
.createHmac('sha256', apiKey)
|
|
@@ -19,7 +20,7 @@ export function computePathKey(apiKey, urlPath) {
|
|
|
19
20
|
* Compute insider key: HMAC-SHA256(apiKey, "insider")
|
|
20
21
|
* Works for any path, grants full navigation
|
|
21
22
|
*/
|
|
22
|
-
|
|
23
|
+
function computeInsiderKey(apiKey) {
|
|
23
24
|
const hash = crypto
|
|
24
25
|
.createHmac('sha256', apiKey)
|
|
25
26
|
.update('insider')
|
|
@@ -29,7 +30,7 @@ export function computeInsiderKey(apiKey) {
|
|
|
29
30
|
/**
|
|
30
31
|
* Compute outsider key with expiry: HMAC-SHA256(apiKey, path + "|" + expiry)
|
|
31
32
|
*/
|
|
32
|
-
|
|
33
|
+
function computeOutsiderKeyWithExpiry(apiKey, urlPath, expiry) {
|
|
33
34
|
const normalized = urlPath.toLowerCase().replace(/^\/+|\/+$/g, '');
|
|
34
35
|
const data = `${normalized}|${String(expiry)}`;
|
|
35
36
|
const hash = crypto.createHmac('sha256', apiKey).update(data).digest('hex');
|
|
@@ -39,7 +40,7 @@ export function computeOutsiderKeyWithExpiry(apiKey, urlPath, expiry) {
|
|
|
39
40
|
* Compute deep share key: HMAC-SHA256(seed, path + "|" + d + "|" + dirs + "|" + stack + "|" + exp)
|
|
40
41
|
* Used for share links with depth \> 0 or dirs enabled.
|
|
41
42
|
*/
|
|
42
|
-
|
|
43
|
+
function computeDeepShareKey(seed, urlPath, params) {
|
|
43
44
|
const normalized = urlPath.toLowerCase().replace(/^\/+|\/+$/g, '');
|
|
44
45
|
const data = `${normalized}|${String(params.depth)}|${params.dirs ? '1' : '0'}|${params.stack}|${params.exp ?? ''}`;
|
|
45
46
|
const hash = crypto.createHmac('sha256', seed).update(data).digest('hex');
|
|
@@ -48,7 +49,7 @@ export function computeDeepShareKey(seed, urlPath, params) {
|
|
|
48
49
|
/**
|
|
49
50
|
* Timing-safe string comparison
|
|
50
51
|
*/
|
|
51
|
-
|
|
52
|
+
function timingSafeEqual(a, b) {
|
|
52
53
|
try {
|
|
53
54
|
return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
54
55
|
}
|
|
@@ -56,3 +57,5 @@ export function timingSafeEqual(a, b) {
|
|
|
56
57
|
return false;
|
|
57
58
|
}
|
|
58
59
|
}
|
|
60
|
+
|
|
61
|
+
export { computeDeepShareKey, computeInsiderKey, computeOutsiderKeyWithExpiry, computePathKey, timingSafeEqual };
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
export { computeDeepShareKey, computeInsiderKey, computeOutsiderKeyWithExpiry, computePathKey, type DeepShareParams, timingSafeEqual, } from './crypto.js';
|
|
7
|
+
export { type AuthMode, authModeSchema, authSchema, DEPRECATED_CONFIG_PROPS, eventConfigSchema, googleAuthSchema, insiderEntrySchema, isScopeName, type JeevesConfig, jeevesConfigSchema, keyEntrySchema, oauthProviderSchema, oauthSchema, scopesObjectSchema, scopesSchema, } from './schema.js';
|
|
7
8
|
export type { AuthStatusResponse, DriveEntry, ExportCacheClearResponse, ExportLink, FileMutationAction, LinkInfoResponse, OAuthStartRequest, OAuthStartResponse, RotateKeyResponse, ShareForRequest, ShareForResponse, ShareRequest, ShareResponse, } from './types.js';
|
|
8
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
export { computeDeepShareKey, computeInsiderKey, computeOutsiderKeyWithExpiry, computePathKey, timingSafeEqual, } from './crypto.js';
|
|
1
|
+
export { computeDeepShareKey, computeInsiderKey, computeOutsiderKeyWithExpiry, computePathKey, timingSafeEqual } from './crypto.js';
|
|
2
|
+
export { DEPRECATED_CONFIG_PROPS, authModeSchema, authSchema, eventConfigSchema, googleAuthSchema, insiderEntrySchema, isScopeName, jeevesConfigSchema, keyEntrySchema, oauthProviderSchema, oauthSchema, scopesObjectSchema, scopesSchema } from './schema.js';
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** Config properties deprecated in v3.6.0 (now resolved via core services). */
|
|
3
|
+
export declare const DEPRECATED_CONFIG_PROPS: readonly ["host", "runnerUrl", "watcherUrl", "metaUrl"];
|
|
4
|
+
/** Supported authentication methods */
|
|
5
|
+
export declare const authModeSchema: z.ZodEnum<{
|
|
6
|
+
google: "google";
|
|
7
|
+
keys: "keys";
|
|
8
|
+
}>;
|
|
9
|
+
/** Google OAuth configuration */
|
|
10
|
+
export declare const googleAuthSchema: z.ZodObject<{
|
|
11
|
+
clientId: z.ZodString;
|
|
12
|
+
clientSecret: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
/** Auth configuration */
|
|
15
|
+
export declare const authSchema: z.ZodObject<{
|
|
16
|
+
modes: z.ZodArray<z.ZodEnum<{
|
|
17
|
+
google: "google";
|
|
18
|
+
keys: "keys";
|
|
19
|
+
}>>;
|
|
20
|
+
google: z.ZodOptional<z.ZodObject<{
|
|
21
|
+
clientId: z.ZodString;
|
|
22
|
+
clientSecret: z.ZodString;
|
|
23
|
+
}, z.core.$strip>>;
|
|
24
|
+
sessionSecret: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
/** Event webhook configuration */
|
|
27
|
+
export declare const eventConfigSchema: z.ZodObject<{
|
|
28
|
+
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
29
|
+
cmd: z.ZodString;
|
|
30
|
+
map: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
31
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
/**
|
|
34
|
+
* Scopes configuration — controls which paths a user/key can access.
|
|
35
|
+
*
|
|
36
|
+
* Three forms:
|
|
37
|
+
* - `string` — single allow pattern (e.g. '/d/*')
|
|
38
|
+
* - `string[]` — array of allow patterns (shorthand for \{ allow: [...] \})
|
|
39
|
+
* - `\{ allow?: string[], deny?: string[] \}` — explicit allow/deny rules
|
|
40
|
+
*
|
|
41
|
+
* Semantics:
|
|
42
|
+
* - Path must match at least one allow rule AND NOT match any deny rule
|
|
43
|
+
* - Omitting `allow` = implicit ['/*'] (allow everything)
|
|
44
|
+
* - Omitting `deny` = no exclusions
|
|
45
|
+
* - Omitting scopes entirely = unrestricted access
|
|
46
|
+
*/
|
|
47
|
+
export declare const scopesObjectSchema: z.ZodObject<{
|
|
48
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
49
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
export declare const scopesSchema: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
52
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
53
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
54
|
+
}, z.core.$strip>]>;
|
|
55
|
+
/** Insider entry (identity + scopes + optional persisted seed) */
|
|
56
|
+
export declare const insiderEntrySchema: z.ZodObject<{
|
|
57
|
+
scopes: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
58
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
60
|
+
}, z.core.$strip>]>>;
|
|
61
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
62
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
63
|
+
seed: z.ZodOptional<z.ZodString>;
|
|
64
|
+
keyCreatedAt: z.ZodOptional<z.ZodString>;
|
|
65
|
+
}, z.core.$strip>;
|
|
66
|
+
/** Key entry — plain string (seed, no scopes) or object with key + optional scopes */
|
|
67
|
+
export declare const keyEntrySchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
68
|
+
key: z.ZodString;
|
|
69
|
+
scopes: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
70
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
71
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
72
|
+
}, z.core.$strip>]>>;
|
|
73
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
74
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
75
|
+
}, z.core.$strip>]>;
|
|
76
|
+
/** Check if a string is a named scope identifier (not a path glob). */
|
|
77
|
+
export declare function isScopeName(v: string): boolean;
|
|
78
|
+
/** OAuth provider configuration */
|
|
79
|
+
export declare const oauthProviderSchema: z.ZodObject<{
|
|
80
|
+
authUrl: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
81
|
+
tokenUrl: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
82
|
+
pkce: z.ZodDefault<z.ZodBoolean>;
|
|
83
|
+
defaultScopes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
84
|
+
}, z.core.$strip>;
|
|
85
|
+
/** OAuth configuration */
|
|
86
|
+
export declare const oauthSchema: z.ZodObject<{
|
|
87
|
+
credentialDir: z.ZodString;
|
|
88
|
+
providers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
89
|
+
authUrl: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
90
|
+
tokenUrl: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
91
|
+
pkce: z.ZodDefault<z.ZodBoolean>;
|
|
92
|
+
defaultScopes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
93
|
+
}, z.core.$strip>>>;
|
|
94
|
+
}, z.core.$strip>;
|
|
95
|
+
/** Top-level Jeeves Server configuration */
|
|
96
|
+
export declare const jeevesConfigSchema: z.ZodObject<{
|
|
97
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
98
|
+
chromePath: z.ZodString;
|
|
99
|
+
auth: z.ZodObject<{
|
|
100
|
+
modes: z.ZodArray<z.ZodEnum<{
|
|
101
|
+
google: "google";
|
|
102
|
+
keys: "keys";
|
|
103
|
+
}>>;
|
|
104
|
+
google: z.ZodOptional<z.ZodObject<{
|
|
105
|
+
clientId: z.ZodString;
|
|
106
|
+
clientSecret: z.ZodString;
|
|
107
|
+
}, z.core.$strip>>;
|
|
108
|
+
sessionSecret: z.ZodOptional<z.ZodString>;
|
|
109
|
+
}, z.core.$strip>;
|
|
110
|
+
scopes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
111
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
112
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
113
|
+
}, z.core.$strip>>>;
|
|
114
|
+
insiders: z.ZodDefault<z.ZodRecord<z.ZodEmail, z.ZodObject<{
|
|
115
|
+
scopes: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
116
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
117
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
118
|
+
}, z.core.$strip>]>>;
|
|
119
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
120
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
121
|
+
seed: z.ZodOptional<z.ZodString>;
|
|
122
|
+
keyCreatedAt: z.ZodOptional<z.ZodString>;
|
|
123
|
+
}, z.core.$strip>>>;
|
|
124
|
+
keys: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
125
|
+
key: z.ZodString;
|
|
126
|
+
scopes: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
127
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
128
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
129
|
+
}, z.core.$strip>]>>;
|
|
130
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
131
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
132
|
+
}, z.core.$strip>]>>>;
|
|
133
|
+
events: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
134
|
+
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
135
|
+
cmd: z.ZodString;
|
|
136
|
+
map: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
137
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
138
|
+
}, z.core.$strip>>>;
|
|
139
|
+
eventTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
140
|
+
eventLogPurgeMs: z.ZodDefault<z.ZodNumber>;
|
|
141
|
+
maxZipSizeMb: z.ZodDefault<z.ZodNumber>;
|
|
142
|
+
roots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
143
|
+
mermaidCliPath: z.ZodOptional<z.ZodString>;
|
|
144
|
+
plantuml: z.ZodOptional<z.ZodObject<{
|
|
145
|
+
jarPath: z.ZodOptional<z.ZodString>;
|
|
146
|
+
javaPath: z.ZodOptional<z.ZodString>;
|
|
147
|
+
servers: z.ZodOptional<z.ZodArray<z.ZodURL>>;
|
|
148
|
+
}, z.core.$strip>>;
|
|
149
|
+
diagramCachePath: z.ZodOptional<z.ZodString>;
|
|
150
|
+
outsiderPolicy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
151
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
152
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
153
|
+
}, z.core.$strip>]>>;
|
|
154
|
+
oauth: z.ZodOptional<z.ZodObject<{
|
|
155
|
+
credentialDir: z.ZodString;
|
|
156
|
+
providers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
157
|
+
authUrl: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
158
|
+
tokenUrl: z.ZodPipe<z.ZodString, z.ZodURL>;
|
|
159
|
+
pkce: z.ZodDefault<z.ZodBoolean>;
|
|
160
|
+
defaultScopes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
161
|
+
}, z.core.$strip>>>;
|
|
162
|
+
}, z.core.$strip>>;
|
|
163
|
+
go: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
164
|
+
}, z.core.$strip>;
|
|
165
|
+
/** Inferred config type */
|
|
166
|
+
export type JeevesConfig = z.infer<typeof jeevesConfigSchema>;
|
|
167
|
+
/** Auth mode type */
|
|
168
|
+
export type AuthMode = z.infer<typeof authModeSchema>;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { SERVER_PORT } from '@karmaniverous/jeeves';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/** Config properties deprecated in v3.6.0 (now resolved via core services). */
|
|
5
|
+
const DEPRECATED_CONFIG_PROPS = [
|
|
6
|
+
'host',
|
|
7
|
+
'runnerUrl',
|
|
8
|
+
'watcherUrl',
|
|
9
|
+
'metaUrl',
|
|
10
|
+
];
|
|
11
|
+
/** Supported authentication methods */
|
|
12
|
+
const authModeSchema = z.enum(['google', 'keys']);
|
|
13
|
+
/** Google OAuth configuration */
|
|
14
|
+
const googleAuthSchema = z.object({
|
|
15
|
+
clientId: z.string().min(1),
|
|
16
|
+
clientSecret: z.string().min(1),
|
|
17
|
+
});
|
|
18
|
+
/** Auth configuration */
|
|
19
|
+
const authSchema = z.object({
|
|
20
|
+
/** Active authentication methods. Order determines priority. */
|
|
21
|
+
modes: z
|
|
22
|
+
.array(authModeSchema)
|
|
23
|
+
.min(1, { message: 'At least one auth mode is required' }),
|
|
24
|
+
/** Google OAuth config. Required if modes includes "google". */
|
|
25
|
+
google: googleAuthSchema.optional(),
|
|
26
|
+
/** Session cookie signing secret. Required if modes includes "google". */
|
|
27
|
+
sessionSecret: z.string().min(1).optional(),
|
|
28
|
+
});
|
|
29
|
+
/** Event webhook configuration */
|
|
30
|
+
const eventConfigSchema = z.object({
|
|
31
|
+
schema: z.record(z.string(), z.unknown()),
|
|
32
|
+
cmd: z.string(),
|
|
33
|
+
map: z.record(z.string(), z.unknown()).optional(),
|
|
34
|
+
timeoutMs: z.number().positive().optional(),
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Scopes configuration — controls which paths a user/key can access.
|
|
38
|
+
*
|
|
39
|
+
* Three forms:
|
|
40
|
+
* - `string` — single allow pattern (e.g. '/d/*')
|
|
41
|
+
* - `string[]` — array of allow patterns (shorthand for \{ allow: [...] \})
|
|
42
|
+
* - `\{ allow?: string[], deny?: string[] \}` — explicit allow/deny rules
|
|
43
|
+
*
|
|
44
|
+
* Semantics:
|
|
45
|
+
* - Path must match at least one allow rule AND NOT match any deny rule
|
|
46
|
+
* - Omitting `allow` = implicit ['/*'] (allow everything)
|
|
47
|
+
* - Omitting `deny` = no exclusions
|
|
48
|
+
* - Omitting scopes entirely = unrestricted access
|
|
49
|
+
*/
|
|
50
|
+
const scopesObjectSchema = z.object({
|
|
51
|
+
allow: z.array(z.string()).optional(),
|
|
52
|
+
deny: z.array(z.string()).optional(),
|
|
53
|
+
});
|
|
54
|
+
const scopesSchema = z.union([
|
|
55
|
+
z.string(),
|
|
56
|
+
z.array(z.string()),
|
|
57
|
+
scopesObjectSchema,
|
|
58
|
+
]);
|
|
59
|
+
/** Insider entry (identity + scopes + optional persisted seed) */
|
|
60
|
+
const insiderEntrySchema = z.object({
|
|
61
|
+
scopes: scopesSchema.optional(),
|
|
62
|
+
/** Extra allow patterns merged on top of named scope references */
|
|
63
|
+
allow: z.array(z.string()).optional(),
|
|
64
|
+
/** Extra deny patterns merged on top of named scope references */
|
|
65
|
+
deny: z.array(z.string()).optional(),
|
|
66
|
+
/** Auto-generated insider seed, persisted in config.json */
|
|
67
|
+
seed: z.string().min(1).optional(),
|
|
68
|
+
/** ISO timestamp of when the seed was created */
|
|
69
|
+
keyCreatedAt: z.string().optional(),
|
|
70
|
+
});
|
|
71
|
+
/** Key entry — plain string (seed, no scopes) or object with key + optional scopes */
|
|
72
|
+
const keyEntrySchema = z.union([
|
|
73
|
+
z.string().min(1),
|
|
74
|
+
z.object({
|
|
75
|
+
key: z.string().min(1),
|
|
76
|
+
scopes: scopesSchema.optional(),
|
|
77
|
+
/** Extra allow patterns merged on top of named scope references */
|
|
78
|
+
allow: z.array(z.string()).optional(),
|
|
79
|
+
/** Extra deny patterns merged on top of named scope references */
|
|
80
|
+
deny: z.array(z.string()).optional(),
|
|
81
|
+
}),
|
|
82
|
+
]);
|
|
83
|
+
/** Check if a string is a named scope identifier (not a path glob). */
|
|
84
|
+
function isScopeName(v) {
|
|
85
|
+
return /^[A-Za-z0-9_-]+$/.test(v);
|
|
86
|
+
}
|
|
87
|
+
/** Helper: collect named scope references from a scopes field value.
|
|
88
|
+
*
|
|
89
|
+
* Backward-compat note: `scopes` has historically accepted path globs like "/**" or ["/a","/b"].
|
|
90
|
+
* We only treat values as *named scope references* if they look like identifiers
|
|
91
|
+
* (e.g. "restricted", "no-vc") rather than path globs.
|
|
92
|
+
*/
|
|
93
|
+
function getScopeRefs(scopes) {
|
|
94
|
+
if (typeof scopes === 'string')
|
|
95
|
+
return isScopeName(scopes) ? [scopes] : [];
|
|
96
|
+
if (Array.isArray(scopes) && scopes.length > 0) {
|
|
97
|
+
const strs = scopes.filter((v) => typeof v === 'string');
|
|
98
|
+
if (strs.length !== scopes.length)
|
|
99
|
+
return [];
|
|
100
|
+
return strs.filter((v) => isScopeName(v));
|
|
101
|
+
}
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
/** OAuth provider configuration */
|
|
105
|
+
const oauthProviderSchema = z.object({
|
|
106
|
+
authUrl: z.string().pipe(z.url()),
|
|
107
|
+
tokenUrl: z.string().pipe(z.url()),
|
|
108
|
+
pkce: z.boolean().default(false),
|
|
109
|
+
defaultScopes: z.array(z.string()).default([]),
|
|
110
|
+
});
|
|
111
|
+
/** OAuth configuration */
|
|
112
|
+
const oauthSchema = z.object({
|
|
113
|
+
credentialDir: z.string(),
|
|
114
|
+
providers: z.record(z.string(), oauthProviderSchema).default({}),
|
|
115
|
+
});
|
|
116
|
+
/** Top-level Jeeves Server configuration */
|
|
117
|
+
const jeevesConfigSchema = z
|
|
118
|
+
.object({
|
|
119
|
+
port: z.number().int().positive().default(SERVER_PORT),
|
|
120
|
+
chromePath: z.string().min(1),
|
|
121
|
+
auth: authSchema,
|
|
122
|
+
/** Named scope definitions, referenced by insiders/keys/outsiderPolicy */
|
|
123
|
+
scopes: z.record(z.string(), scopesObjectSchema).default({}),
|
|
124
|
+
insiders: z.record(z.email(), insiderEntrySchema).default({}),
|
|
125
|
+
keys: z.record(z.string(), keyEntrySchema).default({}),
|
|
126
|
+
events: z.record(z.string(), eventConfigSchema).default({}),
|
|
127
|
+
eventTimeoutMs: z.number().positive().default(30_000),
|
|
128
|
+
eventLogPurgeMs: z.number().positive().default(2_592_000_000),
|
|
129
|
+
/** Maximum directory size in MB for ZIP export. Directories exceeding this are refused. */
|
|
130
|
+
maxZipSizeMb: z.number().positive().default(100),
|
|
131
|
+
/**
|
|
132
|
+
* Filesystem roots for the file browser (Linux only).
|
|
133
|
+
* Map of id → filesystem path. On Windows this is ignored (drives are auto-discovered).
|
|
134
|
+
* Example: \{ home: '/home/user', projects: '/opt/projects' \}
|
|
135
|
+
* Default: \{ root: '/' \}
|
|
136
|
+
*/
|
|
137
|
+
roots: z.record(z.string(), z.string()).optional(),
|
|
138
|
+
/** @deprecated Mermaid is now bundled. This field is ignored but kept for backward compatibility. */
|
|
139
|
+
mermaidCliPath: z.string().optional(),
|
|
140
|
+
/**
|
|
141
|
+
* PlantUML rendering configuration.
|
|
142
|
+
* - jarPath: local PlantUML jar (requires Java). Tried first — supports !include.
|
|
143
|
+
* - servers: fallback PlantUML server URLs, tried in order.
|
|
144
|
+
* The public community server (https://www.plantuml.com/plantuml) is always
|
|
145
|
+
* appended as the last resort unless explicitly listed.
|
|
146
|
+
* If omitted entirely, only the community server is used.
|
|
147
|
+
*/
|
|
148
|
+
plantuml: z
|
|
149
|
+
.object({
|
|
150
|
+
jarPath: z.string().optional(),
|
|
151
|
+
javaPath: z.string().optional(),
|
|
152
|
+
servers: z.array(z.url()).optional(),
|
|
153
|
+
})
|
|
154
|
+
.optional(),
|
|
155
|
+
/**
|
|
156
|
+
* Directory for cached rendered diagrams (content-addressed by source hash).
|
|
157
|
+
* Defaults to `.diagram-cache` in the server working directory.
|
|
158
|
+
*/
|
|
159
|
+
diagramCachePath: z.string().optional(),
|
|
160
|
+
/**
|
|
161
|
+
* Global outsider policy — constrains which paths are eligible for outsider sharing.
|
|
162
|
+
* Uses the same allow/deny model as insider scopes.
|
|
163
|
+
* If omitted, all paths are shareable with outsiders.
|
|
164
|
+
*/
|
|
165
|
+
outsiderPolicy: scopesSchema.optional(),
|
|
166
|
+
/** OAuth2 credential management configuration */
|
|
167
|
+
oauth: oauthSchema.optional(),
|
|
168
|
+
/**
|
|
169
|
+
* Shortlink redirects. Keys are slug identifiers (`[a-zA-Z0-9_-]+`),
|
|
170
|
+
* values are redirect targets — either relative paths (e.g. `/browse/...`)
|
|
171
|
+
* or absolute URLs (e.g. `https://example.com`).
|
|
172
|
+
*
|
|
173
|
+
* Accessible at `GET /go/:slug` without authentication.
|
|
174
|
+
*/
|
|
175
|
+
go: z
|
|
176
|
+
.record(z.string().regex(/^[a-zA-Z0-9_-]+$/), z
|
|
177
|
+
.string()
|
|
178
|
+
.min(1)
|
|
179
|
+
.refine((v) => v.startsWith('/') || /^[a-z]+:\/\//i.test(v), {
|
|
180
|
+
message: 'Target must be a relative path starting with / or an absolute URL',
|
|
181
|
+
}))
|
|
182
|
+
.default({}),
|
|
183
|
+
})
|
|
184
|
+
.superRefine((config, ctx) => {
|
|
185
|
+
// Google auth mode requires google config + sessionSecret
|
|
186
|
+
if (config.auth.modes.includes('google')) {
|
|
187
|
+
if (!config.auth.google) {
|
|
188
|
+
ctx.addIssue({
|
|
189
|
+
code: 'custom',
|
|
190
|
+
message: 'auth.google is required when auth.modes includes "google"',
|
|
191
|
+
path: ['auth', 'google'],
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (!config.auth.sessionSecret) {
|
|
195
|
+
ctx.addIssue({
|
|
196
|
+
code: 'custom',
|
|
197
|
+
message: 'auth.sessionSecret is required when auth.modes includes "google"',
|
|
198
|
+
path: ['auth', 'sessionSecret'],
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Keys auth mode requires at least one key
|
|
203
|
+
if (config.auth.modes.includes('keys') &&
|
|
204
|
+
Object.keys(config.keys).length === 0) {
|
|
205
|
+
ctx.addIssue({
|
|
206
|
+
code: 'custom',
|
|
207
|
+
message: 'At least one key is required when auth.modes includes "keys"',
|
|
208
|
+
path: ['keys'],
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// _internal and _plugin keys must not have scopes
|
|
212
|
+
for (const reserved of ['_internal', '_plugin']) {
|
|
213
|
+
const key = config.keys[reserved];
|
|
214
|
+
if (key && typeof key === 'object' && 'scopes' in key && key.scopes) {
|
|
215
|
+
ctx.addIssue({
|
|
216
|
+
code: 'custom',
|
|
217
|
+
message: `${reserved} key must not have scopes (it is always unscoped)`,
|
|
218
|
+
path: ['keys', reserved],
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Validate all scope name references resolve to the top-level scopes map
|
|
223
|
+
const scopeNames = new Set(Object.keys(config.scopes));
|
|
224
|
+
const validateRefs = (refs, refPath) => {
|
|
225
|
+
for (const ref of refs) {
|
|
226
|
+
if (!scopeNames.has(ref)) {
|
|
227
|
+
ctx.addIssue({
|
|
228
|
+
code: 'custom',
|
|
229
|
+
message: `Scope "${ref}" is not defined in the top-level scopes map`,
|
|
230
|
+
path: refPath,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
for (const [email, entry] of Object.entries(config.insiders)) {
|
|
236
|
+
const refs = getScopeRefs(entry.scopes);
|
|
237
|
+
if (refs.length > 0)
|
|
238
|
+
validateRefs(refs, ['insiders', email, 'scopes']);
|
|
239
|
+
}
|
|
240
|
+
for (const [name, entry] of Object.entries(config.keys)) {
|
|
241
|
+
if (typeof entry === 'object') {
|
|
242
|
+
const refs = getScopeRefs(entry.scopes);
|
|
243
|
+
if (refs.length > 0)
|
|
244
|
+
validateRefs(refs, ['keys', name, 'scopes']);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const outsiderRefs = getScopeRefs(config.outsiderPolicy);
|
|
248
|
+
if (outsiderRefs.length > 0)
|
|
249
|
+
validateRefs(outsiderRefs, ['outsiderPolicy']);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
export { DEPRECATED_CONFIG_PROPS, authModeSchema, authSchema, eventConfigSchema, googleAuthSchema, insiderEntrySchema, isScopeName, jeevesConfigSchema, keyEntrySchema, oauthProviderSchema, oauthSchema, scopesObjectSchema, scopesSchema };
|
package/dist/src/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karmaniverous/jeeves-server-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"author": "Jeeves",
|
|
5
|
+
"description": "Shared cryptographic utilities and API types for jeeves-server packages.",
|
|
6
|
+
"license": "MIT",
|
|
4
7
|
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/karmaniverous/jeeves-server.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
5
13
|
"files": [
|
|
6
14
|
"dist"
|
|
7
15
|
],
|
|
@@ -10,14 +18,15 @@
|
|
|
10
18
|
},
|
|
11
19
|
"scripts": {
|
|
12
20
|
"prebuild": "npx rimraf dist",
|
|
13
|
-
"build": "
|
|
21
|
+
"build": "cross-env NO_COLOR=1 rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
|
|
14
22
|
"lint": "eslint .",
|
|
15
23
|
"lint:fix": "eslint --fix .",
|
|
16
24
|
"test": "vitest run --passWithNoTests",
|
|
17
25
|
"typecheck": "tsc --noEmit",
|
|
18
26
|
"changelog": "git-cliff -o CHANGELOG.md",
|
|
19
27
|
"release": "dotenvx run -f .env.local -- release-it",
|
|
20
|
-
"release:pre": "dotenvx run -f .env.local -- release-it --no-git.requireBranch --github.prerelease --preRelease"
|
|
28
|
+
"release:pre": "dotenvx run -f .env.local -- release-it --no-git.requireBranch --github.prerelease --preRelease",
|
|
29
|
+
"postbuild": "npx rimraf dist/rollup.config.d.ts dist/rollup.config.d.ts.map"
|
|
21
30
|
},
|
|
22
31
|
"exports": {
|
|
23
32
|
".": {
|
|
@@ -28,10 +37,21 @@
|
|
|
28
37
|
}
|
|
29
38
|
},
|
|
30
39
|
"types": "dist/src/index.d.ts",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@karmaniverous/jeeves": "^0.5.11",
|
|
42
|
+
"zod": "^4.4.3"
|
|
43
|
+
},
|
|
31
44
|
"devDependencies": {
|
|
32
|
-
"
|
|
45
|
+
"@dotenvx/dotenvx": "^1.69.1",
|
|
46
|
+
"@rollup/plugin-commonjs": "^29.0.2",
|
|
47
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
48
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
49
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
50
|
+
"cross-env": "^10.1.0",
|
|
33
51
|
"release-it": "^19.2.4",
|
|
34
|
-
"
|
|
52
|
+
"rollup": "^4.60.4",
|
|
53
|
+
"tslib": "^2.8.1",
|
|
54
|
+
"vitest": "^4.1.7"
|
|
35
55
|
},
|
|
36
56
|
"engines": {
|
|
37
57
|
"node": ">=22"
|
package/dist/src/crypto.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM,CAKR;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,GACtB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAM7D"}
|
package/dist/src/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,4BAA4B,EAC5B,cAAc,EACd,KAAK,eAAe,EACpB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,kBAAkB,EAClB,UAAU,EACV,wBAAwB,EACxB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/src/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oCAAoC;AACpC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,yCAAyC;AACzC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,SAAS,GAAG,gBAAgB,GAAG,SAAS,GAAG,eAAe,CAAC;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,sCAAsC;AACtC,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,+CAA+C;AAC/C,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,kCAAkC;AAClC,MAAM,MAAM,kBAAkB,GAC1B,YAAY,GACZ,cAAc,GACd,cAAc,GACd,WAAW,GACX,iBAAiB,CAAC;AAEtB,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qCAAqC;AACrC,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf"}
|