agentic-dev 0.2.9 → 0.2.11
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 +23 -8
- package/bin/agentic-dev.mjs +692 -55
- package/lib/scaffold.mjs +109 -6
- package/package.json +1 -1
- package/client/admin/.dockerignore +0 -3
- package/client/admin/.env.example +0 -1
- package/client/admin/Dockerfile +0 -16
- package/client/admin/Dockerfile.dev +0 -18
- package/client/admin/README.md +0 -20
- package/client/admin/index.html +0 -12
- package/client/admin/package.json +0 -41
- package/client/admin/postcss.config.js +0 -6
- package/client/admin/scripts/ui-parity-admin-adapter.mjs +0 -65
- package/client/admin/src/api/alerts.ts +0 -33
- package/client/admin/src/api/client.ts +0 -71
- package/client/admin/src/api/orders.ts +0 -33
- package/client/admin/src/api/support.ts +0 -11
- package/client/admin/src/app/App.tsx +0 -23
- package/client/admin/src/auth/AuthProvider.tsx +0 -122
- package/client/admin/src/auth/ProtectedRoute.tsx +0 -22
- package/client/admin/src/auth/auth-client.ts +0 -38
- package/client/admin/src/auth/types.ts +0 -18
- package/client/admin/src/components/AdminNotificationsDrawer.tsx +0 -162
- package/client/admin/src/components/AdminShell.tsx +0 -76
- package/client/admin/src/components/ui/button.tsx +0 -34
- package/client/admin/src/components/ui/input.tsx +0 -21
- package/client/admin/src/lib/cn.ts +0 -6
- package/client/admin/src/lib/specRouteCatalog.json +0 -30
- package/client/admin/src/lib/specScreens.json +0 -22
- package/client/admin/src/main.tsx +0 -17
- package/client/admin/src/pages/AdminDashboardPage.tsx +0 -171
- package/client/admin/src/pages/AdminLoginPage.tsx +0 -75
- package/client/admin/src/pages/AdminQueuePage.tsx +0 -107
- package/client/admin/src/pages/AdminSupportPage.tsx +0 -61
- package/client/admin/src/styles/globals.css +0 -17
- package/client/admin/src/theme-vars.ts +0 -18
- package/client/admin/src/theme.ts +0 -25
- package/client/admin/src/vite-env.d.ts +0 -1
- package/client/admin/tailwind.config.js +0 -8
- package/client/admin/tsconfig.json +0 -25
- package/client/admin/vite.config.ts +0 -12
- package/client/landing/.dockerignore +0 -3
- package/client/landing/.env.example +0 -1
- package/client/landing/Dockerfile +0 -16
- package/client/landing/Dockerfile.dev +0 -18
- package/client/landing/README.md +0 -18
- package/client/landing/index.html +0 -12
- package/client/landing/package.json +0 -41
- package/client/landing/postcss.config.js +0 -6
- package/client/landing/scripts/ui-parity-landing-adapter.mjs +0 -65
- package/client/landing/src/App.tsx +0 -21
- package/client/landing/src/api/catalog.ts +0 -30
- package/client/landing/src/api/client.ts +0 -30
- package/client/landing/src/auth/AuthProvider.tsx +0 -122
- package/client/landing/src/auth/ProtectedRoute.tsx +0 -22
- package/client/landing/src/auth/auth-client.ts +0 -38
- package/client/landing/src/auth/types.ts +0 -18
- package/client/landing/src/components/LandingShell.tsx +0 -34
- package/client/landing/src/lib/specRouteCatalog.json +0 -23
- package/client/landing/src/lib/specScreens.json +0 -17
- package/client/landing/src/main.tsx +0 -17
- package/client/landing/src/pages/LandingHomePage.tsx +0 -215
- package/client/landing/src/pages/LandingLoginPage.tsx +0 -90
- package/client/landing/src/pages/LandingWorkspacePage.tsx +0 -126
- package/client/landing/src/styles/globals.css +0 -17
- package/client/landing/src/theme-vars.ts +0 -16
- package/client/landing/src/theme.ts +0 -21
- package/client/landing/src/vite-env.d.ts +0 -1
- package/client/landing/tailwind.config.js +0 -8
- package/client/landing/tsconfig.json +0 -25
- package/client/landing/vite.config.ts +0 -12
- package/client/mobile/.dockerignore +0 -2
- package/client/mobile/.env.example +0 -1
- package/client/mobile/Dockerfile +0 -16
- package/client/mobile/Dockerfile.dev +0 -18
- package/client/mobile/README.md +0 -19
- package/client/mobile/index.html +0 -12
- package/client/mobile/package.json +0 -42
- package/client/mobile/postcss.config.js +0 -6
- package/client/mobile/scripts/ui-parity-mobile-adapter.mjs +0 -67
- package/client/mobile/src/App.tsx +0 -1
- package/client/mobile/src/api/client.ts +0 -62
- package/client/mobile/src/api/fulfillment.ts +0 -55
- package/client/mobile/src/api/shipping.ts +0 -56
- package/client/mobile/src/app/App.tsx +0 -23
- package/client/mobile/src/auth/AuthProvider.tsx +0 -122
- package/client/mobile/src/auth/ProtectedRoute.tsx +0 -27
- package/client/mobile/src/auth/auth-client.ts +0 -38
- package/client/mobile/src/auth/types.ts +0 -18
- package/client/mobile/src/components/InShell.tsx +0 -74
- package/client/mobile/src/components/ui/button.tsx +0 -35
- package/client/mobile/src/components/ui/card.tsx +0 -15
- package/client/mobile/src/components/ui/input.tsx +0 -21
- package/client/mobile/src/lib/cn.ts +0 -6
- package/client/mobile/src/lib/specRouteCatalog.json +0 -26
- package/client/mobile/src/lib/specScreens.json +0 -22
- package/client/mobile/src/lib/useSpeechRecognitionInput.ts +0 -271
- package/client/mobile/src/main.tsx +0 -17
- package/client/mobile/src/pages/DashboardPage.tsx +0 -172
- package/client/mobile/src/pages/FulfillmentPage.tsx +0 -138
- package/client/mobile/src/pages/LoginPage.tsx +0 -74
- package/client/mobile/src/pages/ShippingPage.tsx +0 -338
- package/client/mobile/src/styles/globals.css +0 -23
- package/client/mobile/src/theme-vars.ts +0 -16
- package/client/mobile/src/theme.ts +0 -21
- package/client/mobile/src/vite-env.d.ts +0 -1
- package/client/mobile/tailwind.config.js +0 -8
- package/client/mobile/tsconfig.json +0 -25
- package/client/mobile/vite.config.ts +0 -12
- package/client/web/.dockerignore +0 -3
- package/client/web/.env.example +0 -1
- package/client/web/Dockerfile +0 -16
- package/client/web/Dockerfile.dev +0 -18
- package/client/web/README.md +0 -47
- package/client/web/index.html +0 -12
- package/client/web/package.json +0 -42
- package/client/web/postcss.config.js +0 -6
- package/client/web/scripts/ui-parity-web-adapter.mjs +0 -66
- package/client/web/src/api/client.ts +0 -30
- package/client/web/src/api/orders.ts +0 -42
- package/client/web/src/app/App.tsx +0 -21
- package/client/web/src/auth/AuthProvider.tsx +0 -122
- package/client/web/src/auth/ProtectedRoute.tsx +0 -22
- package/client/web/src/auth/auth-client.ts +0 -38
- package/client/web/src/auth/types.ts +0 -18
- package/client/web/src/components/AppShell.tsx +0 -59
- package/client/web/src/components/ui/button.tsx +0 -35
- package/client/web/src/components/ui/card.tsx +0 -7
- package/client/web/src/components/ui/input.tsx +0 -21
- package/client/web/src/lib/cn.ts +0 -6
- package/client/web/src/lib/specRouteCatalog.json +0 -23
- package/client/web/src/lib/specScreens.json +0 -17
- package/client/web/src/main.tsx +0 -17
- package/client/web/src/pages/DashboardPage.tsx +0 -158
- package/client/web/src/pages/LoginPage.tsx +0 -72
- package/client/web/src/pages/OrdersPage.tsx +0 -123
- package/client/web/src/styles/globals.css +0 -17
- package/client/web/src/theme-vars.ts +0 -18
- package/client/web/src/theme.ts +0 -25
- package/client/web/src/vite-env.d.ts +0 -1
- package/client/web/tailwind.config.js +0 -8
- package/client/web/tsconfig.json +0 -25
- package/client/web/vite.config.ts +0 -12
- package/server/.dockerignore +0 -4
- package/server/.env.example +0 -19
- package/server/Dockerfile +0 -22
- package/server/Dockerfile.dev +0 -19
- package/server/README.md +0 -33
- package/server/__init__.py +0 -0
- package/server/api/__init__.py +0 -1
- package/server/api/http/__init__.py +0 -4
- package/server/api/http/app.py +0 -53
- package/server/api/http/router.py +0 -24
- package/server/config.py +0 -52
- package/server/contexts/__init__.py +0 -12
- package/server/contexts/alerts/__init__.py +0 -1
- package/server/contexts/alerts/application/__init__.py +0 -13
- package/server/contexts/alerts/application/services.py +0 -41
- package/server/contexts/alerts/contracts/__init__.py +0 -3
- package/server/contexts/alerts/contracts/http/__init__.py +0 -3
- package/server/contexts/alerts/contracts/http/router.py +0 -37
- package/server/contexts/alerts/domain/__init__.py +0 -15
- package/server/contexts/alerts/domain/models.py +0 -29
- package/server/contexts/alerts/infrastructure/__init__.py +0 -11
- package/server/contexts/alerts/infrastructure/repository.py +0 -41
- package/server/contexts/auth/__init__.py +0 -1
- package/server/contexts/auth/application/__init__.py +0 -3
- package/server/contexts/auth/application/ports.py +0 -10
- package/server/contexts/auth/application/services.py +0 -64
- package/server/contexts/auth/contracts/__init__.py +0 -4
- package/server/contexts/auth/contracts/http/__init__.py +0 -4
- package/server/contexts/auth/contracts/http/dependencies.py +0 -37
- package/server/contexts/auth/contracts/http/router.py +0 -19
- package/server/contexts/auth/domain/__init__.py +0 -3
- package/server/contexts/auth/domain/models.py +0 -24
- package/server/contexts/auth/infrastructure/__init__.py +0 -4
- package/server/contexts/auth/infrastructure/adapters/memory.py +0 -19
- package/server/contexts/auth/infrastructure/adapters/mongodb.py +0 -24
- package/server/contexts/auth/infrastructure/adapters/sqlalchemy.py +0 -74
- package/server/contexts/auth/infrastructure/repository.py +0 -28
- package/server/contexts/catalog/__init__.py +0 -1
- package/server/contexts/catalog/application/__init__.py +0 -28
- package/server/contexts/catalog/application/ports.py +0 -15
- package/server/contexts/catalog/application/services.py +0 -154
- package/server/contexts/catalog/contracts/__init__.py +0 -3
- package/server/contexts/catalog/contracts/http/__init__.py +0 -3
- package/server/contexts/catalog/contracts/http/router.py +0 -60
- package/server/contexts/catalog/domain/__init__.py +0 -45
- package/server/contexts/catalog/domain/models.py +0 -113
- package/server/contexts/catalog/infrastructure/__init__.py +0 -4
- package/server/contexts/catalog/infrastructure/adapters/memory.py +0 -62
- package/server/contexts/catalog/infrastructure/repository.py +0 -8
- package/server/contexts/fulfillment/__init__.py +0 -1
- package/server/contexts/fulfillment/application/__init__.py +0 -13
- package/server/contexts/fulfillment/application/ports.py +0 -20
- package/server/contexts/fulfillment/application/services.py +0 -85
- package/server/contexts/fulfillment/contracts/__init__.py +0 -3
- package/server/contexts/fulfillment/contracts/http/__init__.py +0 -3
- package/server/contexts/fulfillment/contracts/http/router.py +0 -40
- package/server/contexts/fulfillment/domain/__init__.py +0 -25
- package/server/contexts/fulfillment/domain/models.py +0 -73
- package/server/contexts/fulfillment/infrastructure/__init__.py +0 -13
- package/server/contexts/fulfillment/infrastructure/adapters/memory.py +0 -43
- package/server/contexts/fulfillment/infrastructure/repository.py +0 -97
- package/server/contexts/health/__init__.py +0 -1
- package/server/contexts/health/application/__init__.py +0 -3
- package/server/contexts/health/application/services.py +0 -2
- package/server/contexts/health/contracts/__init__.py +0 -3
- package/server/contexts/health/contracts/http/__init__.py +0 -3
- package/server/contexts/health/contracts/http/router.py +0 -10
- package/server/contexts/inventory/__init__.py +0 -1
- package/server/contexts/inventory/application/__init__.py +0 -28
- package/server/contexts/inventory/application/ports.py +0 -11
- package/server/contexts/inventory/application/services.py +0 -214
- package/server/contexts/inventory/contracts/__init__.py +0 -3
- package/server/contexts/inventory/contracts/http/__init__.py +0 -3
- package/server/contexts/inventory/contracts/http/router.py +0 -82
- package/server/contexts/inventory/domain/__init__.py +0 -33
- package/server/contexts/inventory/domain/models.py +0 -93
- package/server/contexts/inventory/infrastructure/__init__.py +0 -4
- package/server/contexts/inventory/infrastructure/adapters/memory.py +0 -24
- package/server/contexts/inventory/infrastructure/repository.py +0 -8
- package/server/contexts/orders/__init__.py +0 -1
- package/server/contexts/orders/application/__init__.py +0 -19
- package/server/contexts/orders/application/services.py +0 -127
- package/server/contexts/orders/contracts/__init__.py +0 -3
- package/server/contexts/orders/contracts/http/__init__.py +0 -3
- package/server/contexts/orders/contracts/http/router.py +0 -82
- package/server/contexts/orders/domain/__init__.py +0 -29
- package/server/contexts/orders/domain/models.py +0 -95
- package/server/contexts/orders/infrastructure/__init__.py +0 -7
- package/server/contexts/orders/infrastructure/repository.py +0 -104
- package/server/contexts/shipping/__init__.py +0 -1
- package/server/contexts/shipping/application/__init__.py +0 -13
- package/server/contexts/shipping/application/services.py +0 -92
- package/server/contexts/shipping/contracts/__init__.py +0 -3
- package/server/contexts/shipping/contracts/http/__init__.py +0 -3
- package/server/contexts/shipping/contracts/http/router.py +0 -40
- package/server/contexts/shipping/domain/__init__.py +0 -19
- package/server/contexts/shipping/domain/models.py +0 -48
- package/server/contexts/shipping/infrastructure/__init__.py +0 -9
- package/server/contexts/shipping/infrastructure/repository.py +0 -50
- package/server/contexts/support/__init__.py +0 -1
- package/server/contexts/support/application/__init__.py +0 -13
- package/server/contexts/support/application/services.py +0 -29
- package/server/contexts/support/contracts/__init__.py +0 -3
- package/server/contexts/support/contracts/http/__init__.py +0 -3
- package/server/contexts/support/contracts/http/router.py +0 -40
- package/server/contexts/support/domain/__init__.py +0 -13
- package/server/contexts/support/domain/models.py +0 -27
- package/server/contexts/support/infrastructure/__init__.py +0 -11
- package/server/contexts/support/infrastructure/repository.py +0 -70
- package/server/contexts/user/__init__.py +0 -1
- package/server/contexts/user/application/__init__.py +0 -3
- package/server/contexts/user/application/ports.py +0 -11
- package/server/contexts/user/application/services.py +0 -44
- package/server/contexts/user/contracts/__init__.py +0 -3
- package/server/contexts/user/contracts/http/__init__.py +0 -3
- package/server/contexts/user/contracts/http/router.py +0 -26
- package/server/contexts/user/domain/__init__.py +0 -3
- package/server/contexts/user/domain/models.py +0 -22
- package/server/contexts/user/infrastructure/__init__.py +0 -3
- package/server/contexts/user/infrastructure/adapters/memory.py +0 -27
- package/server/contexts/user/infrastructure/adapters/mongodb.py +0 -41
- package/server/contexts/user/infrastructure/adapters/sqlalchemy.py +0 -94
- package/server/contexts/user/infrastructure/factory.py +0 -28
- package/server/data/README.md +0 -24
- package/server/data/bootstrap/alerts.json +0 -38
- package/server/data/bootstrap/auth_accounts.json +0 -18
- package/server/data/bootstrap/catalog_products.json +0 -179
- package/server/data/bootstrap/fulfillment_events.json +0 -5
- package/server/data/bootstrap/fulfillment_notes.json +0 -5
- package/server/data/bootstrap/fulfillment_tasks.json +0 -50
- package/server/data/bootstrap/inventory_levels.json +0 -80
- package/server/data/bootstrap/orders.json +0 -62
- package/server/data/bootstrap/shipping_shipments.json +0 -50
- package/server/data/bootstrap/support_faqs.json +0 -26
- package/server/data/bootstrap/users.json +0 -20
- package/server/data/bootstrap_loader.py +0 -15
- package/server/docker-entrypoint.sh +0 -56
- package/server/main.py +0 -3
- package/server/pyproject.toml +0 -36
- package/server/shared/__init__.py +0 -1
- package/server/shared/application/__init__.py +0 -3
- package/server/shared/application/health.py +0 -2
- package/server/shared/infrastructure/__init__.py +0 -10
- package/server/shared/infrastructure/runtime.py +0 -6
- package/server/shared/infrastructure/security.py +0 -33
- package/server/tests/e2e/test_domain_feature_flows.py +0 -483
- package/server/tests/test_health.py +0 -49
- package/server/uv.lock +0 -1169
package/lib/scaffold.mjs
CHANGED
|
@@ -43,7 +43,8 @@ function copyDirectoryContents(sourceRoot, destinationRoot) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function fetchPage(url) {
|
|
46
|
-
const token =
|
|
46
|
+
const token =
|
|
47
|
+
process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.AGENTIC_GITHUB_TOKEN;
|
|
47
48
|
return fetch(url, {
|
|
48
49
|
headers: {
|
|
49
50
|
Accept: "application/vnd.github+json",
|
|
@@ -58,8 +59,13 @@ export function parseArgs(argv) {
|
|
|
58
59
|
const options = {
|
|
59
60
|
command: "init",
|
|
60
61
|
targetDir: "",
|
|
62
|
+
projectName: "",
|
|
61
63
|
template: "",
|
|
62
64
|
owner: DEFAULT_TEMPLATE_OWNER,
|
|
65
|
+
appMode: "",
|
|
66
|
+
aiProviders: [],
|
|
67
|
+
githubAuthMode: "",
|
|
68
|
+
githubPat: "",
|
|
63
69
|
force: false,
|
|
64
70
|
yes: false,
|
|
65
71
|
skipBootstrap: false,
|
|
@@ -76,9 +82,30 @@ export function parseArgs(argv) {
|
|
|
76
82
|
case "-t":
|
|
77
83
|
options.template = args.shift() ?? "";
|
|
78
84
|
break;
|
|
85
|
+
case "--project-name":
|
|
86
|
+
options.projectName = args.shift() ?? "";
|
|
87
|
+
break;
|
|
79
88
|
case "--owner":
|
|
80
89
|
options.owner = args.shift() ?? DEFAULT_TEMPLATE_OWNER;
|
|
81
90
|
break;
|
|
91
|
+
case "--app-mode":
|
|
92
|
+
options.appMode = args.shift() ?? "";
|
|
93
|
+
break;
|
|
94
|
+
case "--providers":
|
|
95
|
+
case "--provider": {
|
|
96
|
+
const value = args.shift() ?? "";
|
|
97
|
+
options.aiProviders = value
|
|
98
|
+
.split(",")
|
|
99
|
+
.map((entry) => entry.trim())
|
|
100
|
+
.filter(Boolean);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case "--github-auth":
|
|
104
|
+
options.githubAuthMode = args.shift() ?? "";
|
|
105
|
+
break;
|
|
106
|
+
case "--github-pat":
|
|
107
|
+
options.githubPat = args.shift() ?? "";
|
|
108
|
+
break;
|
|
82
109
|
case "--force":
|
|
83
110
|
options.force = true;
|
|
84
111
|
break;
|
|
@@ -115,10 +142,16 @@ export function usage() {
|
|
|
115
142
|
" npx agentic-dev",
|
|
116
143
|
" npx agentic-dev init my-app",
|
|
117
144
|
" npx agentic-dev init my-app --template template-web --yes",
|
|
145
|
+
" npx agentic-dev init my-app --template template-web --project-name my-app --providers codex,claude",
|
|
118
146
|
"",
|
|
119
147
|
"Options:",
|
|
120
148
|
" --template, -t Template repo name or suffix to use",
|
|
149
|
+
" --project-name Project name written into scaffold metadata",
|
|
121
150
|
" --owner GitHub owner for template-* repos (default: say828)",
|
|
151
|
+
" --app-mode App mode: fullstack, frontend, backend",
|
|
152
|
+
" --providers Comma-separated AI providers: codex, claude, ollama",
|
|
153
|
+
" --github-auth GitHub auth mode: public, env, pat",
|
|
154
|
+
" --github-pat GitHub PAT for this run only",
|
|
122
155
|
" --force Allow scaffolding into a non-empty directory",
|
|
123
156
|
" --yes, -y Skip interactive prompts",
|
|
124
157
|
" --skip-bootstrap Copy the template but skip install/bootstrap",
|
|
@@ -177,6 +210,22 @@ export async function fetchTemplateRepos({ owner = DEFAULT_TEMPLATE_OWNER } = {}
|
|
|
177
210
|
return repos;
|
|
178
211
|
}
|
|
179
212
|
|
|
213
|
+
function updateJsonFile(filePath, updater) {
|
|
214
|
+
if (!fs.existsSync(filePath)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let current = {};
|
|
219
|
+
try {
|
|
220
|
+
current = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const next = updater(current);
|
|
226
|
+
fs.writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`);
|
|
227
|
+
}
|
|
228
|
+
|
|
180
229
|
export function resolveTemplateRepo(input, repos) {
|
|
181
230
|
const normalized = (input || "").trim();
|
|
182
231
|
if (!normalized) {
|
|
@@ -323,7 +372,51 @@ function validateTemplateRepoContents(destinationRoot, templateRepo) {
|
|
|
323
372
|
}
|
|
324
373
|
}
|
|
325
374
|
|
|
326
|
-
function
|
|
375
|
+
function writeSetupConfig(destinationRoot, setupSelections = {}) {
|
|
376
|
+
const outputDir = path.join(destinationRoot, ".agentic-dev");
|
|
377
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
378
|
+
const outputPath = path.join(outputDir, "setup.json");
|
|
379
|
+
|
|
380
|
+
const setupConfig = {
|
|
381
|
+
project_name: setupSelections.projectName || path.basename(destinationRoot),
|
|
382
|
+
template_repo: setupSelections.templateRepo || "",
|
|
383
|
+
template_owner: setupSelections.owner || DEFAULT_TEMPLATE_OWNER,
|
|
384
|
+
app_mode: setupSelections.appMode || "fullstack",
|
|
385
|
+
ai_providers: Array.isArray(setupSelections.aiProviders) ? setupSelections.aiProviders : [],
|
|
386
|
+
github_auth_mode: setupSelections.githubAuthMode || "public",
|
|
387
|
+
github_pat_supplied: Boolean(setupSelections.githubPat),
|
|
388
|
+
skip_bootstrap: Boolean(setupSelections.skipBootstrap),
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(setupConfig, null, 2)}\n`);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function applyProviderSelections(destinationRoot, providers = []) {
|
|
395
|
+
if (providers.length === 0) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!providers.includes("claude")) {
|
|
400
|
+
fs.rmSync(path.join(destinationRoot, ".claude"), { recursive: true, force: true });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!providers.includes("codex")) {
|
|
404
|
+
fs.rmSync(path.join(destinationRoot, ".codex"), { recursive: true, force: true });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function applyProjectMetadata(destinationRoot, setupSelections = {}) {
|
|
409
|
+
if (!setupSelections.projectName) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
updateJsonFile(path.join(destinationRoot, ".claude", "workspace-config.json"), (current) => ({
|
|
414
|
+
...current,
|
|
415
|
+
nickname: setupSelections.projectName,
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function runInstallSteps(destinationRoot, bootstrapTarget, { installBrowser = true } = {}) {
|
|
327
420
|
maybeCreateEnvFile(destinationRoot);
|
|
328
421
|
|
|
329
422
|
const pnpmCommand = ensurePnpm();
|
|
@@ -334,7 +427,7 @@ function runInstallSteps(destinationRoot, bootstrapTarget) {
|
|
|
334
427
|
|
|
335
428
|
const executedSteps = ["pnpm install"];
|
|
336
429
|
|
|
337
|
-
if (bootstrapTarget) {
|
|
430
|
+
if (bootstrapTarget && installBrowser) {
|
|
338
431
|
runCommand(
|
|
339
432
|
pnpmCommand[0],
|
|
340
433
|
[
|
|
@@ -395,14 +488,24 @@ export function scaffoldFromTemplateRepo({ destinationRoot, templateRepo }) {
|
|
|
395
488
|
export function installTemplateRepo({
|
|
396
489
|
destinationRoot,
|
|
397
490
|
templateRepo,
|
|
491
|
+
setupSelections = {},
|
|
398
492
|
skipBootstrap = false,
|
|
399
493
|
}) {
|
|
400
494
|
scaffoldFromTemplateRepo({ destinationRoot, templateRepo });
|
|
495
|
+
applyProviderSelections(destinationRoot, setupSelections.aiProviders);
|
|
496
|
+
applyProjectMetadata(destinationRoot, setupSelections);
|
|
497
|
+
writeSetupConfig(destinationRoot, {
|
|
498
|
+
...setupSelections,
|
|
499
|
+
templateRepo: templateRepo.name,
|
|
500
|
+
skipBootstrap,
|
|
501
|
+
});
|
|
401
502
|
validateTemplateRepoContents(destinationRoot, templateRepo);
|
|
402
503
|
const bootstrapTarget = resolveBootstrapTarget(destinationRoot);
|
|
504
|
+
const effectiveSkipBootstrap = skipBootstrap || setupSelections.appMode === "backend";
|
|
505
|
+
const installBrowser = setupSelections.appMode !== "backend";
|
|
403
506
|
|
|
404
|
-
const executedSteps = runInstallSteps(destinationRoot, bootstrapTarget);
|
|
405
|
-
if (!
|
|
507
|
+
const executedSteps = runInstallSteps(destinationRoot, bootstrapTarget, { installBrowser });
|
|
508
|
+
if (!effectiveSkipBootstrap) {
|
|
406
509
|
executedSteps.push(...runBootstrapStep(destinationRoot, bootstrapTarget));
|
|
407
510
|
}
|
|
408
511
|
const bootstrapHint = bootstrapTarget
|
|
@@ -412,6 +515,6 @@ export function installTemplateRepo({
|
|
|
412
515
|
destinationRoot,
|
|
413
516
|
templateRepo: templateRepo.name,
|
|
414
517
|
executedSteps,
|
|
415
|
-
nextSteps:
|
|
518
|
+
nextSteps: effectiveSkipBootstrap ? [bootstrapHint] : [],
|
|
416
519
|
};
|
|
417
520
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VITE_API_BASE_URL=http://127.0.0.1:8000/api/v1
|
package/client/admin/Dockerfile
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
FROM node:20-slim
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
6
|
-
COPY client/admin/package.json ./client/admin/package.json
|
|
7
|
-
|
|
8
|
-
RUN npm install -g pnpm@10.32.0 && pnpm install --filter @do4ai/client-admin-template...
|
|
9
|
-
|
|
10
|
-
COPY . .
|
|
11
|
-
|
|
12
|
-
WORKDIR /app/client/admin
|
|
13
|
-
|
|
14
|
-
EXPOSE 4000
|
|
15
|
-
|
|
16
|
-
CMD ["pnpm", "dev", "--host", "0.0.0.0", "--port", "4000"]
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
FROM node:20-slim
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
RUN npm install -g pnpm@10.32.0
|
|
6
|
-
|
|
7
|
-
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
8
|
-
COPY client/admin/package.json ./client/admin/package.json
|
|
9
|
-
|
|
10
|
-
RUN pnpm install --frozen-lockfile --filter @do4ai/client-admin-template...
|
|
11
|
-
|
|
12
|
-
COPY client/admin ./client/admin
|
|
13
|
-
|
|
14
|
-
WORKDIR /app/client/admin
|
|
15
|
-
|
|
16
|
-
EXPOSE 4000
|
|
17
|
-
|
|
18
|
-
CMD ["sh", "-lc", "pnpm exec vite --host 0.0.0.0 --port ${PORT:-4000}"]
|
package/client/admin/README.md
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# admin
|
|
2
|
-
|
|
3
|
-
운영 콘솔형 관리자 UI 보일러플레이트다.
|
|
4
|
-
|
|
5
|
-
포함 패턴:
|
|
6
|
-
|
|
7
|
-
- 좌측 sidebar + 상단 topbar
|
|
8
|
-
- route 이동 대신 열리는 알림 drawer
|
|
9
|
-
- 목록형 운영 테이블
|
|
10
|
-
- modal/drawer를 제품 shell에서 직접 제어하는 구조
|
|
11
|
-
- CSS variable 기반 parity-friendly token surface
|
|
12
|
-
|
|
13
|
-
시작:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install
|
|
17
|
-
npm run dev
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
기본 DEV 포트는 `4000`이다.
|
package/client/admin/index.html
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="ko">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>client-admin template</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@do4ai/client-admin-template",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.1.0",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@10.32.0",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"dev": "vite --host 0.0.0.0 --port 4000",
|
|
9
|
-
"build": "tsc -b && vite build",
|
|
10
|
-
"preview": "vite preview --host 0.0.0.0 --port 4400",
|
|
11
|
-
"ui:parity:init": "bash ../../sdd/99_toolchain/01_automation/agentic-dev/init_frontend_parity.sh ../.. admin",
|
|
12
|
-
"ui:parity:bootstrap": "bash ../../sdd/99_toolchain/01_automation/agentic-dev/bootstrap_frontend_parity.sh ../.. admin",
|
|
13
|
-
"ui:parity:scaffold": "bash ../../sdd/99_toolchain/01_automation/agentic-dev/run_frontend_target.sh scaffold ../.. admin",
|
|
14
|
-
"ui:parity:materialize-references": "bash ../../sdd/99_toolchain/01_automation/agentic-dev/run_frontend_target.sh materialize_references ../.. admin",
|
|
15
|
-
"ui:parity:route-gap": "bash ../../sdd/99_toolchain/01_automation/agentic-dev/run_frontend_target.sh route_gap ../.. admin",
|
|
16
|
-
"ui:parity:proof": "bash ../../sdd/99_toolchain/01_automation/agentic-dev/run_frontend_target.sh proof ../.. admin"
|
|
17
|
-
},
|
|
18
|
-
"dependencies": {
|
|
19
|
-
"class-variance-authority": "^0.7.1",
|
|
20
|
-
"clsx": "^2.1.1",
|
|
21
|
-
"lucide-react": "^0.576.0",
|
|
22
|
-
"react": "^18.3.1",
|
|
23
|
-
"react-dom": "^18.3.1",
|
|
24
|
-
"react-router-dom": "^6.30.1",
|
|
25
|
-
"tailwind-merge": "^3.5.0"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@playwright/test": "^1.58.2",
|
|
29
|
-
"@types/react": "^18.3.18",
|
|
30
|
-
"@types/react-dom": "^18.3.5",
|
|
31
|
-
"@vitejs/plugin-react": "^4.7.0",
|
|
32
|
-
"autoprefixer": "^10.4.21",
|
|
33
|
-
"pixelmatch": "^7.1.0",
|
|
34
|
-
"pngjs": "^7.0.0",
|
|
35
|
-
"postcss": "^8.5.3",
|
|
36
|
-
"tailwindcss": "^3.4.17",
|
|
37
|
-
"typescript": "^5.8.3",
|
|
38
|
-
"vite": "^5.4.19",
|
|
39
|
-
"yaml": "^2.8.1"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const screens = JSON.parse(
|
|
7
|
-
fs.readFileSync(path.resolve(scriptDir, "../src/lib/specScreens.json"), "utf8"),
|
|
8
|
-
);
|
|
9
|
-
const routes = JSON.parse(
|
|
10
|
-
fs.readFileSync(path.resolve(scriptDir, "../src/lib/specRouteCatalog.json"), "utf8"),
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
const routeMap = new Map(routes.map((entry) => [entry.id, entry.route]));
|
|
14
|
-
|
|
15
|
-
export default {
|
|
16
|
-
service: "templates-admin",
|
|
17
|
-
targetBaseUrl: "http://127.0.0.1:4400",
|
|
18
|
-
viewport: {
|
|
19
|
-
width: 1440,
|
|
20
|
-
height: 1024,
|
|
21
|
-
},
|
|
22
|
-
screens: screens.map((screen) => ({
|
|
23
|
-
id: screen.id,
|
|
24
|
-
title: screen.title,
|
|
25
|
-
route: routeMap.get(screen.id) ?? "/",
|
|
26
|
-
referenceImage: `sdd/03_verify/10_test/ui_parity/reference/${screen.id}.png`,
|
|
27
|
-
readySelector: "body",
|
|
28
|
-
readyTimeoutMs: 10000,
|
|
29
|
-
tags: ["template", "admin"],
|
|
30
|
-
})),
|
|
31
|
-
async preparePage(page, { route }) {
|
|
32
|
-
await page.route("**/auth/me", async (routeRequest) => {
|
|
33
|
-
await routeRequest.fulfill({
|
|
34
|
-
status: 200,
|
|
35
|
-
contentType: "application/json",
|
|
36
|
-
body: JSON.stringify({
|
|
37
|
-
id: "tmpl-admin",
|
|
38
|
-
name: "Template Admin",
|
|
39
|
-
email: "admin@example.com",
|
|
40
|
-
role: "admin",
|
|
41
|
-
}),
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
await page.addInitScript(() => {
|
|
45
|
-
window.localStorage.setItem(
|
|
46
|
-
"admin.auth.token",
|
|
47
|
-
JSON.stringify({
|
|
48
|
-
access_token: "template-access-token",
|
|
49
|
-
token_type: "bearer",
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
if (route === "/login") {
|
|
54
|
-
await page.addInitScript(() => {
|
|
55
|
-
window.localStorage.removeItem("admin.auth.token");
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
async waitForReady(page) {
|
|
60
|
-
await page.waitForLoadState("networkidle");
|
|
61
|
-
},
|
|
62
|
-
async resolveMaskRects() {
|
|
63
|
-
return [];
|
|
64
|
-
},
|
|
65
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { apiGet, apiPost } from "@/api/client";
|
|
2
|
-
|
|
3
|
-
export interface AlertItem {
|
|
4
|
-
id: string;
|
|
5
|
-
source: string;
|
|
6
|
-
title: string;
|
|
7
|
-
message: string;
|
|
8
|
-
tone: string;
|
|
9
|
-
created_at: string;
|
|
10
|
-
read: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface AlertsPayload {
|
|
14
|
-
unread_count: number;
|
|
15
|
-
items: AlertItem[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface AlertsReadAllResult {
|
|
19
|
-
updated_count: number;
|
|
20
|
-
unread_count: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function fetchAlerts(accessToken: string) {
|
|
24
|
-
return apiGet<AlertsPayload>("/alerts", { accessToken });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function markAlertRead(alertId: string, accessToken: string) {
|
|
28
|
-
return apiPost<AlertItem>(`/alerts/${alertId}/read`, { accessToken });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function markAllAlertsRead(accessToken: string) {
|
|
32
|
-
return apiPost<AlertsReadAllResult>("/alerts/read-all", { accessToken });
|
|
33
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
const API_BASE_URL =
|
|
2
|
-
(import.meta.env.VITE_API_BASE_URL as string | undefined)?.replace(/\/$/, "") ??
|
|
3
|
-
"";
|
|
4
|
-
|
|
5
|
-
interface RequestOptions {
|
|
6
|
-
accessToken?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface WriteOptions extends RequestOptions {
|
|
10
|
-
body?: unknown;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function readJson<T>(response: Response): Promise<T> {
|
|
14
|
-
if (!response.ok) {
|
|
15
|
-
let message = "Request failed";
|
|
16
|
-
try {
|
|
17
|
-
const data = (await response.json()) as { detail?: string };
|
|
18
|
-
if (typeof data.detail === "string" && data.detail.length > 0) {
|
|
19
|
-
message = data.detail;
|
|
20
|
-
}
|
|
21
|
-
} catch {
|
|
22
|
-
message = response.statusText || message;
|
|
23
|
-
}
|
|
24
|
-
throw new Error(message);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (await response.json()) as T;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function apiGet<T>(
|
|
31
|
-
path: string,
|
|
32
|
-
options?: RequestOptions,
|
|
33
|
-
): Promise<T> {
|
|
34
|
-
const response = await fetch(`${API_BASE_URL}${path}`, {
|
|
35
|
-
headers: options?.accessToken
|
|
36
|
-
? { Authorization: `Bearer ${options.accessToken}` }
|
|
37
|
-
: undefined,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return readJson<T>(response);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function writeJson<T>(
|
|
44
|
-
method: "POST" | "PATCH",
|
|
45
|
-
path: string,
|
|
46
|
-
options?: WriteOptions,
|
|
47
|
-
): Promise<T> {
|
|
48
|
-
const headers: Record<string, string> = {
|
|
49
|
-
"Content-Type": "application/json",
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
if (options?.accessToken) {
|
|
53
|
-
headers.Authorization = `Bearer ${options.accessToken}`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const response = await fetch(`${API_BASE_URL}${path}`, {
|
|
57
|
-
method,
|
|
58
|
-
headers,
|
|
59
|
-
body: JSON.stringify(options?.body ?? {}),
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return readJson<T>(response);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function apiPost<T>(path: string, options?: WriteOptions): Promise<T> {
|
|
66
|
-
return writeJson("POST", path, options);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function apiPatch<T>(path: string, options?: WriteOptions): Promise<T> {
|
|
70
|
-
return writeJson("PATCH", path, options);
|
|
71
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { apiGet } from "@/api/client";
|
|
2
|
-
|
|
3
|
-
export interface DashboardCard {
|
|
4
|
-
label: string;
|
|
5
|
-
value: string;
|
|
6
|
-
tone?: string | null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface StageStatus {
|
|
10
|
-
label: string;
|
|
11
|
-
value: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface AdminOverviewResponse {
|
|
15
|
-
cards: DashboardCard[];
|
|
16
|
-
stage_statuses: StageStatus[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface AdminQueueItem {
|
|
20
|
-
order_id: string;
|
|
21
|
-
product_name: string;
|
|
22
|
-
customer_name: string;
|
|
23
|
-
status: string;
|
|
24
|
-
sla: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function fetchAdminOverview(accessToken: string) {
|
|
28
|
-
return apiGet<AdminOverviewResponse>("/orders/admin/overview", { accessToken });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function fetchAdminQueue(accessToken: string) {
|
|
32
|
-
return apiGet<AdminQueueItem[]>("/orders/admin/queue", { accessToken });
|
|
33
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { apiGet } from "@/api/client";
|
|
2
|
-
|
|
3
|
-
export interface SupportFaq {
|
|
4
|
-
id: string;
|
|
5
|
-
question: string;
|
|
6
|
-
visibility: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function fetchSupportFaqs(accessToken: string) {
|
|
10
|
-
return apiGet<SupportFaq[]>("/support/faqs", { accessToken });
|
|
11
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Route, Routes } from "react-router-dom";
|
|
2
|
-
|
|
3
|
-
import { ProtectedRoute } from "@/auth/ProtectedRoute";
|
|
4
|
-
import { AdminShell } from "@/components/AdminShell";
|
|
5
|
-
import { AdminDashboardPage } from "@/pages/AdminDashboardPage";
|
|
6
|
-
import { AdminLoginPage } from "@/pages/AdminLoginPage";
|
|
7
|
-
import { AdminQueuePage } from "@/pages/AdminQueuePage";
|
|
8
|
-
import { AdminSupportPage } from "@/pages/AdminSupportPage";
|
|
9
|
-
|
|
10
|
-
export function App() {
|
|
11
|
-
return (
|
|
12
|
-
<Routes>
|
|
13
|
-
<Route path="/login" element={<AdminLoginPage />} />
|
|
14
|
-
<Route element={<ProtectedRoute />}>
|
|
15
|
-
<Route element={<AdminShell />}>
|
|
16
|
-
<Route path="/" element={<AdminDashboardPage />} />
|
|
17
|
-
<Route path="/queue" element={<AdminQueuePage />} />
|
|
18
|
-
<Route path="/support" element={<AdminSupportPage />} />
|
|
19
|
-
</Route>
|
|
20
|
-
</Route>
|
|
21
|
-
</Routes>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
import { getMe, login as loginRequest } from "@/auth/auth-client";
|
|
4
|
-
import type { AuthToken, AuthUser, LoginCommand } from "@/auth/types";
|
|
5
|
-
|
|
6
|
-
const STORAGE_KEY = "admin.auth.token";
|
|
7
|
-
|
|
8
|
-
interface AuthContextValue {
|
|
9
|
-
user: AuthUser | null;
|
|
10
|
-
token: AuthToken | null;
|
|
11
|
-
initializing: boolean;
|
|
12
|
-
authenticating: boolean;
|
|
13
|
-
login: (command: LoginCommand) => Promise<void>;
|
|
14
|
-
logout: () => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
|
|
18
|
-
|
|
19
|
-
function readStoredToken(): AuthToken | null {
|
|
20
|
-
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
21
|
-
if (!raw) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(raw) as AuthToken;
|
|
27
|
-
} catch {
|
|
28
|
-
window.localStorage.removeItem(STORAGE_KEY);
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function storeToken(token: AuthToken | null) {
|
|
34
|
-
if (token) {
|
|
35
|
-
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(token));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
window.localStorage.removeItem(STORAGE_KEY);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
43
|
-
const [token, setToken] = useState<AuthToken | null>(() => readStoredToken());
|
|
44
|
-
const [user, setUser] = useState<AuthUser | null>(null);
|
|
45
|
-
const [initializing, setInitializing] = useState(true);
|
|
46
|
-
const [authenticating, setAuthenticating] = useState(false);
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
let cancelled = false;
|
|
50
|
-
|
|
51
|
-
async function bootstrap() {
|
|
52
|
-
if (!token) {
|
|
53
|
-
setUser(null);
|
|
54
|
-
setInitializing(false);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
const currentUser = await getMe(token.access_token);
|
|
60
|
-
if (!cancelled) {
|
|
61
|
-
setUser(currentUser);
|
|
62
|
-
}
|
|
63
|
-
} catch {
|
|
64
|
-
if (!cancelled) {
|
|
65
|
-
setToken(null);
|
|
66
|
-
setUser(null);
|
|
67
|
-
storeToken(null);
|
|
68
|
-
}
|
|
69
|
-
} finally {
|
|
70
|
-
if (!cancelled) {
|
|
71
|
-
setInitializing(false);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
void bootstrap();
|
|
77
|
-
|
|
78
|
-
return () => {
|
|
79
|
-
cancelled = true;
|
|
80
|
-
};
|
|
81
|
-
}, [token]);
|
|
82
|
-
|
|
83
|
-
const value = useMemo<AuthContextValue>(
|
|
84
|
-
() => ({
|
|
85
|
-
user,
|
|
86
|
-
token,
|
|
87
|
-
initializing,
|
|
88
|
-
authenticating,
|
|
89
|
-
async login(command) {
|
|
90
|
-
setAuthenticating(true);
|
|
91
|
-
try {
|
|
92
|
-
const nextToken = await loginRequest(command);
|
|
93
|
-
storeToken(nextToken);
|
|
94
|
-
setToken(nextToken);
|
|
95
|
-
const currentUser = await getMe(nextToken.access_token);
|
|
96
|
-
setUser(currentUser);
|
|
97
|
-
} finally {
|
|
98
|
-
setAuthenticating(false);
|
|
99
|
-
setInitializing(false);
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
logout() {
|
|
103
|
-
storeToken(null);
|
|
104
|
-
setToken(null);
|
|
105
|
-
setUser(null);
|
|
106
|
-
setInitializing(false);
|
|
107
|
-
},
|
|
108
|
-
}),
|
|
109
|
-
[authenticating, initializing, token, user],
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function useAuth() {
|
|
116
|
-
const context = useContext(AuthContext);
|
|
117
|
-
if (!context) {
|
|
118
|
-
throw new Error("useAuth must be used within an AuthProvider");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return context;
|
|
122
|
-
}
|