aicodeman 1.4.0 → 1.5.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.
- package/README.md +36 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +168 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/multiuser.d.ts +38 -0
- package/dist/config/multiuser.d.ts.map +1 -0
- package/dist/config/multiuser.js +58 -0
- package/dist/config/multiuser.js.map +1 -0
- package/dist/cron/cron-service.d.ts +1 -1
- package/dist/cron/cron-service.d.ts.map +1 -1
- package/dist/cron/cron-service.js +31 -4
- package/dist/cron/cron-service.js.map +1 -1
- package/dist/docker-hosts.d.ts +85 -5
- package/dist/docker-hosts.d.ts.map +1 -1
- package/dist/docker-hosts.js +260 -22
- package/dist/docker-hosts.js.map +1 -1
- package/dist/mux-interface.d.ts +6 -0
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/plan-orchestrator.d.ts +8 -1
- package/dist/plan-orchestrator.d.ts.map +1 -1
- package/dist/plan-orchestrator.js +20 -1
- package/dist/plan-orchestrator.js.map +1 -1
- package/dist/push-store.d.ts +17 -5
- package/dist/push-store.d.ts.map +1 -1
- package/dist/push-store.js +3 -1
- package/dist/push-store.js.map +1 -1
- package/dist/session-cli-builder.d.ts +1 -1
- package/dist/session-cli-builder.d.ts.map +1 -1
- package/dist/session-cli-builder.js +7 -2
- package/dist/session-cli-builder.js.map +1 -1
- package/dist/session.d.ts +9 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +19 -2
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +24 -2
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +60 -12
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tunnel-manager.d.ts +17 -0
- package/dist/tunnel-manager.d.ts.map +1 -1
- package/dist/tunnel-manager.js +48 -8
- package/dist/tunnel-manager.js.map +1 -1
- package/dist/types/api.d.ts +10 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js +20 -0
- package/dist/types/api.js.map +1 -1
- package/dist/types/cron.d.ts +2 -0
- package/dist/types/cron.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/session.d.ts +8 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/types/user.d.ts +59 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +12 -0
- package/dist/types/user.js.map +1 -0
- package/dist/user-store.d.ts +139 -0
- package/dist/user-store.d.ts.map +1 -0
- package/dist/user-store.js +433 -0
- package/dist/user-store.js.map +1 -0
- package/dist/web/admin-audit.d.ts +17 -0
- package/dist/web/admin-audit.d.ts.map +1 -0
- package/dist/web/admin-audit.js +19 -0
- package/dist/web/admin-audit.js.map +1 -0
- package/dist/web/middleware/auth.d.ts +8 -0
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +250 -51
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/ports/auth-port.d.ts +13 -0
- package/dist/web/ports/auth-port.d.ts.map +1 -1
- package/dist/web/ports/config-port.d.ts +4 -1
- package/dist/web/ports/config-port.d.ts.map +1 -1
- package/dist/web/ports/infra-port.d.ts +4 -1
- package/dist/web/ports/infra-port.d.ts.map +1 -1
- package/dist/web/public/admin-ui.js +260 -0
- package/dist/web/public/admin-ui.js.br +0 -0
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/{app.07e61fcf.js → app.6c4b9893.js} +1 -1
- package/dist/web/public/app.6c4b9893.js.br +0 -0
- package/dist/web/public/app.6c4b9893.js.gz +0 -0
- package/dist/web/public/{constants.503109fa.js → constants.5ce97313.js} +7 -0
- package/dist/web/public/constants.5ce97313.js.br +0 -0
- package/dist/web/public/{constants.503109fa.js.gz → constants.5ce97313.js.gz} +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +24 -14
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
- package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
- package/dist/web/public/mobile.0644e3ac.css.gz +0 -0
- package/dist/web/public/notification-manager.ca9dc40d.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.ca03853f.js → panels-ui.3c9b0465.js} +53 -53
- package/dist/web/public/panels-ui.3c9b0465.js.br +0 -0
- package/dist/web/public/panels-ui.3c9b0465.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.4851c61a.js +58 -0
- package/dist/web/public/session-ui.4851c61a.js.br +0 -0
- package/dist/web/public/session-ui.4851c61a.js.gz +0 -0
- package/dist/web/public/settings-ui.29214cf1.js +67 -0
- package/dist/web/public/settings-ui.29214cf1.js.br +0 -0
- package/dist/web/public/settings-ui.29214cf1.js.gz +0 -0
- package/dist/web/public/styles.9e6cdb9d.css +1 -0
- package/dist/web/public/styles.9e6cdb9d.css.br +0 -0
- package/dist/web/public/styles.9e6cdb9d.css.gz +0 -0
- package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.efda6083.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/route-helpers.d.ts +78 -1
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +172 -3
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/admin-routes.d.ts +20 -0
- package/dist/web/routes/admin-routes.d.ts.map +1 -0
- package/dist/web/routes/admin-routes.js +193 -0
- package/dist/web/routes/admin-routes.js.map +1 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +202 -74
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/clipboard-routes.d.ts +2 -2
- package/dist/web/routes/clipboard-routes.d.ts.map +1 -1
- package/dist/web/routes/clipboard-routes.js +10 -0
- package/dist/web/routes/clipboard-routes.js.map +1 -1
- package/dist/web/routes/cron-routes.d.ts.map +1 -1
- package/dist/web/routes/cron-routes.js +57 -9
- package/dist/web/routes/cron-routes.js.map +1 -1
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +26 -20
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/index.d.ts +2 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +2 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/me-routes.d.ts +16 -0
- package/dist/web/routes/me-routes.d.ts.map +1 -0
- package/dist/web/routes/me-routes.js +73 -0
- package/dist/web/routes/me-routes.js.map +1 -0
- package/dist/web/routes/mux-routes.d.ts.map +1 -1
- package/dist/web/routes/mux-routes.js +23 -5
- package/dist/web/routes/mux-routes.js.map +1 -1
- package/dist/web/routes/orchestrator-routes.d.ts.map +1 -1
- package/dist/web/routes/orchestrator-routes.js +43 -11
- package/dist/web/routes/orchestrator-routes.js.map +1 -1
- package/dist/web/routes/plan-routes.d.ts.map +1 -1
- package/dist/web/routes/plan-routes.js +28 -9
- package/dist/web/routes/plan-routes.js.map +1 -1
- package/dist/web/routes/push-routes.d.ts.map +1 -1
- package/dist/web/routes/push-routes.js +4 -0
- package/dist/web/routes/push-routes.js.map +1 -1
- package/dist/web/routes/ralph-routes.d.ts.map +1 -1
- package/dist/web/routes/ralph-routes.js +21 -17
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/respawn-routes.d.ts.map +1 -1
- package/dist/web/routes/respawn-routes.js +23 -14
- package/dist/web/routes/respawn-routes.js.map +1 -1
- package/dist/web/routes/scheduled-routes.d.ts.map +1 -1
- package/dist/web/routes/scheduled-routes.js +22 -6
- package/dist/web/routes/scheduled-routes.js.map +1 -1
- package/dist/web/routes/search-routes.d.ts.map +1 -1
- package/dist/web/routes/search-routes.js +9 -3
- package/dist/web/routes/search-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +209 -65
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +108 -23
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/ws-routes.d.ts.map +1 -1
- package/dist/web/routes/ws-routes.js +11 -0
- package/dist/web/routes/ws-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +1 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +3 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +12 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +187 -11
- package/dist/web/server.js.map +1 -1
- package/dist/web/sse-events.d.ts +18 -0
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +19 -0
- package/dist/web/sse-events.js.map +1 -1
- package/dist/web/sse-stream-manager.d.ts +32 -2
- package/dist/web/sse-stream-manager.d.ts.map +1 -1
- package/dist/web/sse-stream-manager.js +51 -4
- package/dist/web/sse-stream-manager.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/app.07e61fcf.js.br +0 -0
- package/dist/web/public/app.07e61fcf.js.gz +0 -0
- package/dist/web/public/constants.503109fa.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.js.gz +0 -0
- package/dist/web/public/session-ui.9da1b71f.js +0 -58
- package/dist/web/public/session-ui.9da1b71f.js.br +0 -0
- package/dist/web/public/session-ui.9da1b71f.js.gz +0 -0
- package/dist/web/public/settings-ui.4acbe789.js +0 -67
- package/dist/web/public/settings-ui.4acbe789.js.br +0 -0
- package/dist/web/public/settings-ui.4acbe789.js.gz +0 -0
- package/dist/web/public/styles.546ddae4.css +0 -1
- package/dist/web/public/styles.546ddae4.css.br +0 -0
- package/dist/web/public/styles.546ddae4.css.gz +0 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Multi-user store: `~/.codeman/users.json` (via `dataPath`, 0600).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the storage-module pattern of `remote-hosts.ts` / `docker-hosts.ts`, but
|
|
5
|
+
* because it holds password hashes it writes atomically (tmp + rename) at mode
|
|
6
|
+
* 0600 and keeps only a SHORT in-process cache so the CLI (`codeman users …`) can
|
|
7
|
+
* edit the file while the server runs and have changes picked up within the TTL.
|
|
8
|
+
*
|
|
9
|
+
* Pure, IO-free helpers (`isValidUsername`, `hashPassword`, `verifyPasswordHash`,
|
|
10
|
+
* `needsRehash`, `resolveClaudeModeForUser`, the last-admin invariants) are split
|
|
11
|
+
* out so they are unit-testable without a server. Hashing is `scrypt` from
|
|
12
|
+
* `node:crypto` (no new deps), compared via `timingSafeEqual`; parameters are
|
|
13
|
+
* stored per record so cost can be raised later and old records rehashed on their
|
|
14
|
+
* next successful login.
|
|
15
|
+
*
|
|
16
|
+
* See `docs/multi-user-plan.md` sections 4.1, 5, 6.3.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
19
|
+
import fs from 'node:fs/promises';
|
|
20
|
+
import { isAbsolute, join, relative } from 'node:path';
|
|
21
|
+
import { randomBytes, scrypt as scryptCb, timingSafeEqual } from 'node:crypto';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
23
|
+
import { dataPath, getDataDir } from './config/instance.js';
|
|
24
|
+
import { getUserSpacesDir, isMultiUserMode, maxUsers } from './config/multiuser.js';
|
|
25
|
+
const scrypt = promisify(scryptCb);
|
|
26
|
+
const USERS_FILE = 'users.json';
|
|
27
|
+
const CACHE_TTL_MS = 1000;
|
|
28
|
+
const KEYLEN = 64;
|
|
29
|
+
const SALT_BYTES = 32;
|
|
30
|
+
/** Generous ceiling so raising N/r later does not trip scrypt's memory guard. */
|
|
31
|
+
const SCRYPT_MAXMEM = 256 * 1024 * 1024;
|
|
32
|
+
/** Current hashing parameters. Stored per record; raise these to increase cost. */
|
|
33
|
+
export const DEFAULT_SCRYPT_PARAMS = { N: 16384, r: 8, p: 1 };
|
|
34
|
+
/** Username: lowercase, first char alphanumeric, 2-32 chars total. Becomes a folder name. */
|
|
35
|
+
const USERNAME_RE = /^[a-z0-9][a-z0-9_-]{1,31}$/;
|
|
36
|
+
/** Typed error whose `.code` maps to an API errorCode at the route layer. */
|
|
37
|
+
export class UserStoreError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
constructor(message, code) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = 'UserStoreError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// ─────────────────────────────── pure helpers ───────────────────────────────
|
|
46
|
+
export function normalizeUsername(name) {
|
|
47
|
+
return String(name ?? '')
|
|
48
|
+
.trim()
|
|
49
|
+
.toLowerCase();
|
|
50
|
+
}
|
|
51
|
+
export function isValidUsername(name) {
|
|
52
|
+
return USERNAME_RE.test(normalizeUsername(name));
|
|
53
|
+
}
|
|
54
|
+
/** Hash a password with the given (or current) scrypt params + a fresh random salt. */
|
|
55
|
+
export async function hashPassword(password, params = DEFAULT_SCRYPT_PARAMS) {
|
|
56
|
+
const salt = randomBytes(SALT_BYTES);
|
|
57
|
+
const derived = await scrypt(password, salt, KEYLEN, { ...params, maxmem: SCRYPT_MAXMEM });
|
|
58
|
+
return {
|
|
59
|
+
algo: 'scrypt',
|
|
60
|
+
N: params.N,
|
|
61
|
+
r: params.r,
|
|
62
|
+
p: params.p,
|
|
63
|
+
salt: salt.toString('hex'),
|
|
64
|
+
hash: derived.toString('hex'),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Constant-time verify of a password against a stored hash record. Never throws. */
|
|
68
|
+
export async function verifyPasswordHash(password, record) {
|
|
69
|
+
if (!record || record.algo !== 'scrypt')
|
|
70
|
+
return false;
|
|
71
|
+
let salt;
|
|
72
|
+
let expected;
|
|
73
|
+
try {
|
|
74
|
+
salt = Buffer.from(record.salt, 'hex');
|
|
75
|
+
expected = Buffer.from(record.hash, 'hex');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (expected.length === 0)
|
|
81
|
+
return false;
|
|
82
|
+
let derived;
|
|
83
|
+
try {
|
|
84
|
+
derived = await scrypt(password, salt, expected.length, {
|
|
85
|
+
N: record.N,
|
|
86
|
+
r: record.r,
|
|
87
|
+
p: record.p,
|
|
88
|
+
maxmem: SCRYPT_MAXMEM,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (derived.length !== expected.length)
|
|
95
|
+
return false;
|
|
96
|
+
return timingSafeEqual(derived, expected);
|
|
97
|
+
}
|
|
98
|
+
/** True when a stored hash uses weaker params than current and should be rehashed. */
|
|
99
|
+
export function needsRehash(record, params = DEFAULT_SCRYPT_PARAMS) {
|
|
100
|
+
return record.algo !== 'scrypt' || record.N !== params.N || record.r !== params.r || record.p !== params.p;
|
|
101
|
+
}
|
|
102
|
+
/** URL-safe one-time password (16 chars) for admin create/reset flows. */
|
|
103
|
+
export function generateOneTimePassword() {
|
|
104
|
+
return randomBytes(12).toString('base64url');
|
|
105
|
+
}
|
|
106
|
+
export function toPublicUser(u) {
|
|
107
|
+
return {
|
|
108
|
+
username: u.username,
|
|
109
|
+
role: u.role,
|
|
110
|
+
disabled: !!u.disabled,
|
|
111
|
+
mustChangePassword: !!u.mustChangePassword,
|
|
112
|
+
canBypassPermissions: !!u.canBypassPermissions,
|
|
113
|
+
createdAt: u.createdAt,
|
|
114
|
+
lastLoginAt: u.lastLoginAt,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function countEnabledAdmins(users) {
|
|
118
|
+
return users.filter((u) => u.role === 'admin' && !u.disabled).length;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Section 6.3: resolve the effective Claude permission mode for a user. Admins and
|
|
122
|
+
* granted users get the global mode as-is; a non-granted regular user whose mode
|
|
123
|
+
* would be `dangerously-skip-permissions` is silently downgraded to `auto` (all
|
|
124
|
+
* other modes are already <= auto and pass through). Pure.
|
|
125
|
+
*/
|
|
126
|
+
export function resolveClaudeModeForUser(globalMode, grant) {
|
|
127
|
+
const mode = globalMode ?? 'dangerously-skip-permissions';
|
|
128
|
+
if (grant.role === 'admin' || grant.canBypassPermissions)
|
|
129
|
+
return mode;
|
|
130
|
+
return mode === 'dangerously-skip-permissions' ? 'auto' : mode;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Section 6.3: whether a user may run arbitrary commands as the host account
|
|
134
|
+
* (shell-mode sessions, cron `launchCommand`, other CLIs' bypass flags). Same
|
|
135
|
+
* one-bit grant as bypass. Admins always may.
|
|
136
|
+
*/
|
|
137
|
+
export function canRunPrivilegedCommands(grant) {
|
|
138
|
+
return grant.role === 'admin' || !!grant.canBypassPermissions;
|
|
139
|
+
}
|
|
140
|
+
// ─────────────────────────────── IO layer ───────────────────────────────
|
|
141
|
+
let cache = null;
|
|
142
|
+
/** Drop the in-process cache (called after every write; exported for tests). */
|
|
143
|
+
export function invalidateUsersCache() {
|
|
144
|
+
cache = null;
|
|
145
|
+
}
|
|
146
|
+
export async function readUsers(force = false) {
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
if (!force && cache && now - cache.ts < CACHE_TTL_MS)
|
|
149
|
+
return cache.users;
|
|
150
|
+
let raw;
|
|
151
|
+
try {
|
|
152
|
+
raw = await fs.readFile(dataPath(USERS_FILE), 'utf-8');
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
// ENOENT is the ONLY legitimately-empty store (first boot). Any other read
|
|
156
|
+
// error (EIO/EACCES/EMFILE/EBUSY) is a transient/permission failure, NOT an
|
|
157
|
+
// empty store — do NOT cache [] and do NOT let it look empty, or a following
|
|
158
|
+
// createUser/bootstrap would overwrite users.json and destroy every account.
|
|
159
|
+
if (err.code === 'ENOENT') {
|
|
160
|
+
cache = { users: [], ts: now };
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
// A present-but-corrupt file (invalid JSON) must also fail loud rather than
|
|
166
|
+
// read as empty, so mutators/bootstrap abort instead of clobbering it.
|
|
167
|
+
const parsed = JSON.parse(raw);
|
|
168
|
+
const users = Array.isArray(parsed.users) ? parsed.users : [];
|
|
169
|
+
cache = { users, ts: now };
|
|
170
|
+
return users;
|
|
171
|
+
}
|
|
172
|
+
async function writeUsers(users) {
|
|
173
|
+
const dir = getDataDir();
|
|
174
|
+
if (!existsSync(dir))
|
|
175
|
+
mkdirSync(dir, { recursive: true });
|
|
176
|
+
const finalPath = dataPath(USERS_FILE);
|
|
177
|
+
// Unique per-writer tmp name (pid + random) so the CLI (`codeman users …`) and
|
|
178
|
+
// the live server — designed to write this file concurrently across processes —
|
|
179
|
+
// never share a single `users.json.tmp` inode and tear each other's payload.
|
|
180
|
+
// Matches the state-store.ts / self-update.ts convention.
|
|
181
|
+
const tmpPath = `${finalPath}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
|
|
182
|
+
const payload = { version: 1, users };
|
|
183
|
+
try {
|
|
184
|
+
await fs.writeFile(tmpPath, JSON.stringify(payload, null, 2), { mode: 0o600 });
|
|
185
|
+
await fs.chmod(tmpPath, 0o600).catch(() => { });
|
|
186
|
+
await fs.rename(tmpPath, finalPath);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
await fs.unlink(tmpPath).catch(() => { });
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
cache = { users, ts: Date.now() };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Serialize every read-modify-write on users.json. Without this a fire-and-forget
|
|
196
|
+
* touchLastLogin (fired on each Basic auth) can interleave with a route's
|
|
197
|
+
* create/update and clobber records, since both do readUsers(true) → mutate →
|
|
198
|
+
* writeUsers against a single shared file + tmp path.
|
|
199
|
+
*/
|
|
200
|
+
let mutateChain = Promise.resolve();
|
|
201
|
+
function withUsersLock(fn) {
|
|
202
|
+
const run = mutateChain.then(fn, fn);
|
|
203
|
+
mutateChain = run.then(() => undefined, () => undefined);
|
|
204
|
+
return run;
|
|
205
|
+
}
|
|
206
|
+
export async function hasUsers() {
|
|
207
|
+
return (await readUsers()).length > 0;
|
|
208
|
+
}
|
|
209
|
+
// A precomputed dummy hash so an unknown/disabled user costs the same scrypt work
|
|
210
|
+
// as a real verify (defeats username-enumeration by timing). Created once, lazily.
|
|
211
|
+
let dummyHashPromise = null;
|
|
212
|
+
function getDummyHash() {
|
|
213
|
+
if (!dummyHashPromise)
|
|
214
|
+
dummyHashPromise = hashPassword('codeman-timing-equalization-placeholder');
|
|
215
|
+
return dummyHashPromise;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Verify a username/password against the store. Returns the record (plus whether it
|
|
219
|
+
* should be rehashed) on success, or null for wrong password / unknown / disabled
|
|
220
|
+
* user. Runs a dummy scrypt on the miss path so timing does not reveal which users
|
|
221
|
+
* exist. Never writes (the caller decides when to persist lastLogin / rehash).
|
|
222
|
+
*/
|
|
223
|
+
export async function verifyPassword(username, password) {
|
|
224
|
+
const user = await findUser(username);
|
|
225
|
+
if (!user || user.disabled) {
|
|
226
|
+
await verifyPasswordHash(password, await getDummyHash());
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const ok = await verifyPasswordHash(password, user.password);
|
|
230
|
+
if (!ok)
|
|
231
|
+
return null;
|
|
232
|
+
return { user, needsRehash: needsRehash(user.password) };
|
|
233
|
+
}
|
|
234
|
+
export async function findUser(username) {
|
|
235
|
+
const norm = normalizeUsername(username);
|
|
236
|
+
if (!norm)
|
|
237
|
+
return undefined;
|
|
238
|
+
const users = await readUsers();
|
|
239
|
+
return users.find((u) => u.username === norm);
|
|
240
|
+
}
|
|
241
|
+
export async function createUser(opts) {
|
|
242
|
+
const username = normalizeUsername(opts.username);
|
|
243
|
+
if (!isValidUsername(username)) {
|
|
244
|
+
throw new UserStoreError('Username must be lowercase, start alphanumeric, 2-32 chars ([a-z0-9_-])', 'INVALID_INPUT');
|
|
245
|
+
}
|
|
246
|
+
if (opts.role !== 'admin' && opts.role !== 'user') {
|
|
247
|
+
throw new UserStoreError('Role must be "admin" or "user"', 'INVALID_INPUT');
|
|
248
|
+
}
|
|
249
|
+
if (!opts.password || opts.password.length < 8) {
|
|
250
|
+
throw new UserStoreError('Password must be at least 8 characters', 'INVALID_INPUT');
|
|
251
|
+
}
|
|
252
|
+
return withUsersLock(async () => {
|
|
253
|
+
const users = await readUsers(true);
|
|
254
|
+
if (users.some((u) => u.username === username)) {
|
|
255
|
+
throw new UserStoreError(`User "${username}" already exists`, 'USER_EXISTS');
|
|
256
|
+
}
|
|
257
|
+
if (users.length >= maxUsers()) {
|
|
258
|
+
throw new UserStoreError(`Maximum number of users (${maxUsers()}) reached`, 'INVALID_INPUT');
|
|
259
|
+
}
|
|
260
|
+
const record = {
|
|
261
|
+
username,
|
|
262
|
+
role: opts.role,
|
|
263
|
+
password: await hashPassword(opts.password),
|
|
264
|
+
disabled: false,
|
|
265
|
+
mustChangePassword: !!opts.mustChangePassword,
|
|
266
|
+
canBypassPermissions: !!opts.canBypassPermissions,
|
|
267
|
+
createdAt: Date.now(),
|
|
268
|
+
};
|
|
269
|
+
users.push(record);
|
|
270
|
+
await writeUsers(users);
|
|
271
|
+
return record;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/** Set a user's password. `mustChangePassword` is left unchanged unless specified. */
|
|
275
|
+
export async function setPassword(username, password, opts = {}) {
|
|
276
|
+
if (!password || password.length < 8) {
|
|
277
|
+
throw new UserStoreError('Password must be at least 8 characters', 'INVALID_INPUT');
|
|
278
|
+
}
|
|
279
|
+
const norm = normalizeUsername(username);
|
|
280
|
+
return withUsersLock(async () => {
|
|
281
|
+
const users = await readUsers(true);
|
|
282
|
+
const record = users.find((u) => u.username === norm);
|
|
283
|
+
if (!record)
|
|
284
|
+
throw new UserStoreError(`User "${norm}" not found`, 'USER_NOT_FOUND');
|
|
285
|
+
record.password = await hashPassword(password);
|
|
286
|
+
if (opts.mustChangePassword !== undefined)
|
|
287
|
+
record.mustChangePassword = opts.mustChangePassword;
|
|
288
|
+
await writeUsers(users);
|
|
289
|
+
return record;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
export async function updateUser(username, patch) {
|
|
293
|
+
const norm = normalizeUsername(username);
|
|
294
|
+
return withUsersLock(async () => {
|
|
295
|
+
const users = await readUsers(true);
|
|
296
|
+
const record = users.find((u) => u.username === norm);
|
|
297
|
+
if (!record)
|
|
298
|
+
throw new UserStoreError(`User "${norm}" not found`, 'USER_NOT_FOUND');
|
|
299
|
+
// Guard the last-enabled-admin invariant against demote/disable.
|
|
300
|
+
const before = countEnabledAdmins(users);
|
|
301
|
+
const projected = {
|
|
302
|
+
...record,
|
|
303
|
+
role: patch.role ?? record.role,
|
|
304
|
+
disabled: patch.disabled ?? record.disabled,
|
|
305
|
+
};
|
|
306
|
+
const after = countEnabledAdmins(users.map((u) => (u.username === norm ? projected : u)));
|
|
307
|
+
if (before > 0 && after === 0) {
|
|
308
|
+
throw new UserStoreError('Cannot demote or disable the last enabled admin', 'LAST_ADMIN');
|
|
309
|
+
}
|
|
310
|
+
if (patch.role !== undefined)
|
|
311
|
+
record.role = patch.role;
|
|
312
|
+
if (patch.disabled !== undefined)
|
|
313
|
+
record.disabled = patch.disabled;
|
|
314
|
+
if (patch.canBypassPermissions !== undefined)
|
|
315
|
+
record.canBypassPermissions = patch.canBypassPermissions;
|
|
316
|
+
if (patch.mustChangePassword !== undefined)
|
|
317
|
+
record.mustChangePassword = patch.mustChangePassword;
|
|
318
|
+
await writeUsers(users);
|
|
319
|
+
return record;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Record a successful login timestamp. Best-effort + throttled: skips the write if
|
|
324
|
+
* the last login was within the last minute (Basic clients re-send credentials on
|
|
325
|
+
* every request, so this fires often — the throttle keeps disk churn bounded).
|
|
326
|
+
*/
|
|
327
|
+
export async function touchLastLogin(username) {
|
|
328
|
+
const norm = normalizeUsername(username);
|
|
329
|
+
try {
|
|
330
|
+
await withUsersLock(async () => {
|
|
331
|
+
const users = await readUsers(true);
|
|
332
|
+
const record = users.find((u) => u.username === norm);
|
|
333
|
+
if (!record)
|
|
334
|
+
return;
|
|
335
|
+
if (record.lastLoginAt && Date.now() - record.lastLoginAt < 60_000)
|
|
336
|
+
return;
|
|
337
|
+
record.lastLoginAt = Date.now();
|
|
338
|
+
await writeUsers(users);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
/* best-effort */
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
export async function deleteUser(username) {
|
|
346
|
+
const norm = normalizeUsername(username);
|
|
347
|
+
await withUsersLock(async () => {
|
|
348
|
+
const users = await readUsers(true);
|
|
349
|
+
const record = users.find((u) => u.username === norm);
|
|
350
|
+
if (!record)
|
|
351
|
+
throw new UserStoreError(`User "${norm}" not found`, 'USER_NOT_FOUND');
|
|
352
|
+
const before = countEnabledAdmins(users);
|
|
353
|
+
const remaining = users.filter((u) => u.username !== norm);
|
|
354
|
+
const after = countEnabledAdmins(remaining);
|
|
355
|
+
if (before > 0 && after === 0) {
|
|
356
|
+
throw new UserStoreError('Cannot delete the last enabled admin', 'LAST_ADMIN');
|
|
357
|
+
}
|
|
358
|
+
await writeUsers(remaining);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* First-boot bootstrap: in multi-user mode with no users yet, create the initial
|
|
363
|
+
* admin from `CODEMAN_USERNAME`/`CODEMAN_PASSWORD` if both are set. Returns a
|
|
364
|
+
* status the caller (server start / CLI) uses to decide whether to refuse boot.
|
|
365
|
+
*/
|
|
366
|
+
export async function bootstrapInitialAdmin() {
|
|
367
|
+
if (await hasUsers())
|
|
368
|
+
return { status: 'exists' };
|
|
369
|
+
const username = process.env.CODEMAN_USERNAME;
|
|
370
|
+
const password = process.env.CODEMAN_PASSWORD;
|
|
371
|
+
if (!username || !password)
|
|
372
|
+
return { status: 'missing-env' };
|
|
373
|
+
const created = await createUser({ username, role: 'admin', password });
|
|
374
|
+
return { status: 'created', username: created.username };
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Delete a user's on-disk space (`<USER_SPACES_DIR>/<username>`) with the section 8
|
|
378
|
+
* guard rails: the top-level dir must not be a symlink, and its realpath must
|
|
379
|
+
* resolve strictly inside USER_SPACES_DIR (so a symlinked or `..`-escaping target
|
|
380
|
+
* can never be used to rm an arbitrary tree). No-op if the space does not exist.
|
|
381
|
+
*/
|
|
382
|
+
export async function deleteUserSpace(username) {
|
|
383
|
+
const norm = normalizeUsername(username);
|
|
384
|
+
if (!isValidUsername(norm))
|
|
385
|
+
throw new UserStoreError('Invalid username', 'INVALID_INPUT');
|
|
386
|
+
const root = getUserSpacesDir();
|
|
387
|
+
const target = join(root, norm);
|
|
388
|
+
let lst;
|
|
389
|
+
try {
|
|
390
|
+
lst = await fs.lstat(target);
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
return; // nothing to delete
|
|
394
|
+
}
|
|
395
|
+
if (lst.isSymbolicLink()) {
|
|
396
|
+
throw new UserStoreError('Refusing to delete a symlinked user space', 'INVALID_INPUT');
|
|
397
|
+
}
|
|
398
|
+
const realRoot = await fs.realpath(root).catch(() => root);
|
|
399
|
+
const realTarget = await fs.realpath(target);
|
|
400
|
+
const rel = relative(realRoot, realTarget);
|
|
401
|
+
if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) {
|
|
402
|
+
throw new UserStoreError('User space escapes USER_SPACES_DIR', 'INVALID_INPUT');
|
|
403
|
+
}
|
|
404
|
+
await fs.rm(realTarget, { recursive: true, force: true });
|
|
405
|
+
}
|
|
406
|
+
/** The synthetic admin used in single-user mode so downstream has one code path. */
|
|
407
|
+
export const SYNTHETIC_ADMIN = { username: 'admin', role: 'admin' };
|
|
408
|
+
/**
|
|
409
|
+
* Whether a username may run arbitrary commands (shell mode, cron launchCommand,
|
|
410
|
+
* other CLIs' bypass). Single-user or an unset owner: allowed. In multi-user a
|
|
411
|
+
* MISSING user (e.g. deleted) fails closed (non-privileged). Used at cron fire time.
|
|
412
|
+
*/
|
|
413
|
+
export async function canUsernameRunPrivilegedCommands(username) {
|
|
414
|
+
if (!isMultiUserMode() || !username)
|
|
415
|
+
return true;
|
|
416
|
+
const user = await findUser(username);
|
|
417
|
+
return canRunPrivilegedCommands(user ?? { role: 'user' });
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Resolve the effective Claude mode for a username by looking up the grant. In
|
|
421
|
+
* single-user mode (or for an unknown owner) the global mode passes through.
|
|
422
|
+
*/
|
|
423
|
+
export async function resolveClaudeModeForUsername(globalMode, username) {
|
|
424
|
+
const fallback = globalMode ?? 'dangerously-skip-permissions';
|
|
425
|
+
if (!isMultiUserMode() || !username)
|
|
426
|
+
return fallback;
|
|
427
|
+
// Fail closed: an unknown/deleted owner in multi-user mode is treated as a
|
|
428
|
+
// non-granted regular user so a stale-owned spawn (e.g. an orphaned cron job)
|
|
429
|
+
// is downgraded to `auto` rather than inheriting the global bypass.
|
|
430
|
+
const user = await findUser(username);
|
|
431
|
+
return resolveClaudeModeForUser(globalMode, user ?? { role: 'user' });
|
|
432
|
+
}
|
|
433
|
+
//# sourceMappingURL=user-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-store.js","sourceRoot":"","sources":["../src/user-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Append-only admin audit log (~/.codeman/admin-audit.jsonl).
|
|
3
|
+
*
|
|
4
|
+
* Every user-management action (create/patch/reset/delete/logout/assign) writes one
|
|
5
|
+
* JSON line: timestamp, acting admin, action, target, request IP. Same idiom as
|
|
6
|
+
* session-lifecycle.jsonl. Best-effort: a write failure never blocks the action.
|
|
7
|
+
*/
|
|
8
|
+
export interface AdminAuditEntry {
|
|
9
|
+
ts: number;
|
|
10
|
+
admin: string;
|
|
11
|
+
action: string;
|
|
12
|
+
target?: string;
|
|
13
|
+
ip?: string;
|
|
14
|
+
detail?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export declare function appendAdminAudit(entry: Omit<AdminAuditEntry, 'ts'>): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=admin-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-audit.d.ts","sourceRoot":"","sources":["../../src/web/admin-audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Append-only admin audit log (~/.codeman/admin-audit.jsonl).
|
|
3
|
+
*
|
|
4
|
+
* Every user-management action (create/patch/reset/delete/logout/assign) writes one
|
|
5
|
+
* JSON line: timestamp, acting admin, action, target, request IP. Same idiom as
|
|
6
|
+
* session-lifecycle.jsonl. Best-effort: a write failure never blocks the action.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
import { dataPath } from '../config/instance.js';
|
|
10
|
+
export async function appendAdminAudit(entry) {
|
|
11
|
+
try {
|
|
12
|
+
const line = JSON.stringify({ ts: Date.now(), ...entry }) + '\n';
|
|
13
|
+
await fs.appendFile(dataPath('admin-audit.jsonl'), line, { mode: 0o600 });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
/* best-effort audit; never block the action */
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=admin-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-audit.js","sourceRoot":"","sources":["../../src/web/admin-audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAWjD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAkC;IACvE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QACjE,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;AACH,CAAC"}
|
|
@@ -11,6 +11,12 @@ import type { FastifyInstance } from 'fastify';
|
|
|
11
11
|
import { StaleExpirationMap } from '../../utils/index.js';
|
|
12
12
|
import type { AuthSessionRecord } from '../ports/auth-port.js';
|
|
13
13
|
import { type HostPolicy } from '../network-auth-policy.js';
|
|
14
|
+
import { type AuthUser } from '../../types.js';
|
|
15
|
+
declare module 'fastify' {
|
|
16
|
+
interface FastifyRequest {
|
|
17
|
+
authUser?: AuthUser;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
14
20
|
export declare const AUTH_COOKIE_NAME = "codeman_session";
|
|
15
21
|
/** State returned from registerAuthMiddleware for cleanup in server stop() */
|
|
16
22
|
interface AuthState {
|
|
@@ -18,6 +24,8 @@ interface AuthState {
|
|
|
18
24
|
authFailures: StaleExpirationMap<string, number> | null;
|
|
19
25
|
qrAuthFailures: StaleExpirationMap<string, number> | null;
|
|
20
26
|
hookSecretFailures: StaleExpirationMap<string, number> | null;
|
|
27
|
+
/** Per-username Basic-auth failure bucket (multi-user only). */
|
|
28
|
+
userFailures: StaleExpirationMap<string, number> | null;
|
|
21
29
|
}
|
|
22
30
|
/**
|
|
23
31
|
* Register HTTP Basic Auth middleware with session cookies and rate limiting.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/web/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/web/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAU1G,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAIlF,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACrB;CACF;AAGD,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAElD,8EAA8E;AAC9E,UAAU,SAAS;IACjB,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAAC;IACnE,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACxD,cAAc,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC1D,kBAAkB,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9D,gEAAgE;IAChE,YAAY,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACzD;AA6ED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CAuJtF;AAmKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,CAazF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAqDlF"}
|