From 7b16e88b2faae1fc6b1b77de26d9d7427c32173a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 4 Aug 2026 14:00:33 +0000 Subject: [PATCH] scripts: give every user a root under DATA_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three non-owner accounts had no directory of their own — only the owner did, grown organically as features created what they needed. Adds an idempotent script that creates the skeleton, and runs it for those three. DATA_PATH//{home,attachments,cache,dashboards,email_accounts, general_chat_sessions,logs,sidecar} Most of those are also created on demand by whichever feature owns them, so pre-creating buys legibility rather than function: the tree now shows the shape a user has without having to use it first. `home` is the exception and the reason this exists — nothing creates it today, because getOwnerHomeDir returns process.env.HOME_DIR whenever it is set, which it always is on a real install. It is where a non-owner's sessions will run. Takes emails as arguments rather than reading the user table. The layout does not depend on the database, keeping the DB out means it runs with nothing else up, and at invite time the caller already knows the email. It refuses arguments that are not email addresses, since a stray one would create a junk directory sitting next to real user roots and looking like one. Deliberately NOT a revival of the provision.ts deleted two commits ago. That one was ~90% per-container scaffolding — shell rc files, a generated CLAUDE.md and settings.json describing the container — wrapped around the two useful lines this keeps. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/provision-user-dirs.ts | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 scripts/provision-user-dirs.ts diff --git a/scripts/provision-user-dirs.ts b/scripts/provision-user-dirs.ts new file mode 100644 index 00000000..403a69f9 --- /dev/null +++ b/scripts/provision-user-dirs.ts @@ -0,0 +1,73 @@ +// Create the per-user root under DATA_PATH for the given accounts. +// +// bun scripts/provision-user-dirs.ts a@b.com c@d.com +// DRY_RUN=1 bun scripts/provision-user-dirs.ts a@b.com +// +// Takes emails as arguments rather than reading the user table: the directory layout does not depend +// on the database, and keeping the DB out means this runs with nothing else up. At invite time the +// caller already knows the email. +// +// This is the directory skeleton ONLY. It is deliberately not a revival of the old provision.ts, which +// also seeded shell rc files and wrote a generated CLAUDE.md + settings.json describing the user's +// Docker container. That architecture is gone. What survives from it is the useful part: a user has a +// root, and a home inside it. +// +// Idempotent — an existing directory is left exactly as it is, so re-running is safe. + +import { mkdirSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; + +const DATA_PATH = process.env.DATA_PATH ?? join(process.cwd(), 'data'); + +// Mirrors the shape the owner's root grew organically. Most of these are also created on demand by +// whichever feature owns them (attachments, dashboards, email_accounts…), so pre-creating them buys +// legibility more than function — the tree shows what a user has without having to use it first. +// +// `home` is the exception, and the reason this exists at all: nothing creates it today. getOwnerHomeDir +// returns process.env.HOME_DIR whenever it is set, which it always is on a real install, so the +// per-user home has never actually been reached. It is where a non-owner's sessions will run. +const USER_DIRS = [ + 'home', + 'attachments', + 'cache', + 'dashboards', + 'email_accounts', + 'general_chat_sessions', + 'logs', + 'sidecar', +] as const; + +const DRY_RUN = process.env.DRY_RUN === '1'; +const emails = process.argv.slice(2).filter(Boolean); + +if (!emails.length) { + console.error('Usage: bun scripts/provision-user-dirs.ts [email...]'); + process.exit(2); +} + +// A stray argument would create a junk directory next to real user roots, and it would look like a +// user. Cheap to refuse. +const invalid = emails.filter((e) => !/^[^\s@/]+@[^\s@/]+\.[^\s@/]+$/.test(e)); +if (invalid.length) { + console.error(`Not valid email addresses: ${invalid.join(', ')}`); + process.exit(2); +} + +console.log(`DATA_PATH = ${DATA_PATH}`); +console.log(`${DRY_RUN ? 'Would provision' : 'Provisioning'} ${emails.length} user root(s)\n`); + +for (const email of emails) { + const root = join(DATA_PATH, email); + console.log(`${email}${existsSync(root) ? '' : ' [new root]'}`); + + for (const dir of USER_DIRS) { + const path = join(root, dir); + if (existsSync(path)) { + console.log(` · ${dir} (exists)`); + continue; + } + if (!DRY_RUN) mkdirSync(path, { recursive: true }); + console.log(` ${DRY_RUN ? '+' : '✓'} ${dir}`); + } + console.log(''); +}