github-issue-tower-defence-management 1.88.1 → 1.90.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/.github/CODEOWNERS +1 -2
- package/CHANGELOG.md +14 -0
- package/README.md +13 -2
- package/bin/adapter/entry-points/cli/index.js +21 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleServer.js +40 -13
- package/bin/adapter/entry-points/console/consoleServer.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-DcOZ02ON.js → index-DDjYPXRT.js} +10 -10
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DHlBLm7d.css +1 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js +97 -0
- package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js.map +1 -0
- package/bin/adapter/proxy/RateLimitCache.js +3 -3
- package/bin/adapter/proxy/RateLimitCache.js.map +1 -1
- package/bin/domain/usecases/OauthTokenSelectUseCase.js +87 -0
- package/bin/domain/usecases/OauthTokenSelectUseCase.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.ts +38 -0
- package/src/adapter/entry-points/console/consoleServer.test.ts +81 -0
- package/src/adapter/entry-points/console/consoleServer.ts +48 -16
- package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleProjectHeader.stories.tsx +29 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleProjectHeader.tsx +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleList.ts +17 -4
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.test.ts +24 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.ts +17 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +5 -1
- package/src/adapter/entry-points/console/ui/tsconfig.json +2 -1
- package/src/adapter/entry-points/console/ui/vite.config.ts +1 -1
- package/src/adapter/entry-points/console/ui-dist/assets/{index-DcOZ02ON.js → index-DDjYPXRT.js} +10 -10
- package/src/adapter/entry-points/console/ui-dist/assets/index-DHlBLm7d.css +1 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +204 -0
- package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +132 -0
- package/src/adapter/proxy/RateLimitCache.ts +9 -4
- package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +179 -0
- package/src/domain/usecases/OauthTokenSelectUseCase.ts +158 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleServer.d.ts +1 -0
- package/types/adapter/entry-points/console/consoleServer.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts +20 -0
- package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts.map +1 -0
- package/types/adapter/proxy/RateLimitCache.d.ts +2 -2
- package/types/adapter/proxy/RateLimitCache.d.ts.map +1 -1
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +35 -0
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts.map +1 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DFxrSRH4.css +0 -1
- package/src/adapter/entry-points/console/ui-dist/assets/index-DFxrSRH4.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-border:#e5e5e5;--color-input:#e5e5e5;--color-ring:#0a0a0a;--color-background:#fff;--color-foreground:#0a0a0a;--color-primary:#171717;--color-primary-foreground:#fafafa;--color-secondary:#f5f5f5;--color-secondary-foreground:#171717;--color-muted-foreground:#737373;--color-accent:#f5f5f5;--color-accent-foreground:#171717;--color-destructive:#ef4444}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.flex{display:flex}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.max-w-3xl{max-width:var(--container-3xl)}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-2{gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.border-input{border-color:var(--color-input)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-destructive{color:var(--color-destructive)}.text-foreground{color:var(--color-foreground)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.underline-offset-2{text-underline-offset:2px}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--color-primary) 90%,transparent)}}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--color-secondary) 80%,transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}}body{background-color:var(--color-background);color:var(--color-foreground);margin:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TDPM Console</title>
|
|
7
|
-
<script type="module" crossorigin src="
|
|
8
|
-
<link rel="stylesheet" crossorigin href="
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DDjYPXRT.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DHlBLm7d.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OauthTokenSelectHandler = exports.resolveCacheDirectory = exports.resolveTokenListJsonPath = void 0;
|
|
4
|
+
const OauthTokenSelectUseCase_1 = require("../../../domain/usecases/OauthTokenSelectUseCase");
|
|
5
|
+
const RateLimitCache_1 = require("../../proxy/RateLimitCache");
|
|
6
|
+
const TokenListLoader_1 = require("../../proxy/TokenListLoader");
|
|
7
|
+
const DEFAULT_TOKEN_LIST_PATH_ENV = 'CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH';
|
|
8
|
+
const DEFAULT_CACHE_DIRECTORY_ENV = 'TDPM_RATELIMIT_CACHE_DIR';
|
|
9
|
+
const resolveTokenListJsonPath = (explicitPath) => {
|
|
10
|
+
if (explicitPath !== null && explicitPath.length > 0) {
|
|
11
|
+
return explicitPath;
|
|
12
|
+
}
|
|
13
|
+
const fromEnv = process.env[DEFAULT_TOKEN_LIST_PATH_ENV];
|
|
14
|
+
if (fromEnv !== undefined && fromEnv.length > 0) {
|
|
15
|
+
return fromEnv;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
exports.resolveTokenListJsonPath = resolveTokenListJsonPath;
|
|
20
|
+
const resolveCacheDirectory = (explicitDirectory) => {
|
|
21
|
+
if (explicitDirectory !== null && explicitDirectory.length > 0) {
|
|
22
|
+
return explicitDirectory;
|
|
23
|
+
}
|
|
24
|
+
const fromEnv = process.env[DEFAULT_CACHE_DIRECTORY_ENV];
|
|
25
|
+
if (fromEnv !== undefined && fromEnv.length > 0) {
|
|
26
|
+
return fromEnv;
|
|
27
|
+
}
|
|
28
|
+
return (0, RateLimitCache_1.cacheDir)();
|
|
29
|
+
};
|
|
30
|
+
exports.resolveCacheDirectory = resolveCacheDirectory;
|
|
31
|
+
class OauthTokenSelectHandler {
|
|
32
|
+
constructor(useCase = new OauthTokenSelectUseCase_1.OauthTokenSelectUseCase()) {
|
|
33
|
+
this.useCase = useCase;
|
|
34
|
+
this.handle = (input) => {
|
|
35
|
+
const tokenListJsonPath = (0, exports.resolveTokenListJsonPath)(input.tokenListJsonPath);
|
|
36
|
+
if (tokenListJsonPath === null) {
|
|
37
|
+
return {
|
|
38
|
+
selectedToken: null,
|
|
39
|
+
selectedName: null,
|
|
40
|
+
diagnostics: [
|
|
41
|
+
`No token list path provided. Pass --tokenListJsonPath or set ${DEFAULT_TOKEN_LIST_PATH_ENV}.`,
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const entries = (0, TokenListLoader_1.loadTokenEntries)(tokenListJsonPath);
|
|
46
|
+
if (entries === null) {
|
|
47
|
+
return {
|
|
48
|
+
selectedToken: null,
|
|
49
|
+
selectedName: null,
|
|
50
|
+
diagnostics: [
|
|
51
|
+
`No usable token entries loaded from ${tokenListJsonPath}.`,
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const cacheDirectory = (0, exports.resolveCacheDirectory)(input.cacheDirectory);
|
|
56
|
+
const candidates = entries.map(({ name, token }) => {
|
|
57
|
+
const snapshot = (0, RateLimitCache_1.readRateLimit)(token, cacheDirectory);
|
|
58
|
+
return {
|
|
59
|
+
name,
|
|
60
|
+
token,
|
|
61
|
+
snapshot: snapshot === null
|
|
62
|
+
? null
|
|
63
|
+
: {
|
|
64
|
+
fiveHourUtilization: snapshot.fiveHourUtilization,
|
|
65
|
+
fiveHourReset: snapshot.fiveHourReset,
|
|
66
|
+
sevenDayUtilization: snapshot.sevenDayUtilization,
|
|
67
|
+
sevenDayReset: snapshot.sevenDayReset,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
const result = this.useCase.run(candidates, input.nowEpochSeconds);
|
|
72
|
+
return {
|
|
73
|
+
selectedToken: result.selected?.token ?? null,
|
|
74
|
+
selectedName: result.selected?.name ?? null,
|
|
75
|
+
diagnostics: this.formatDiagnostics(result, input.nowEpochSeconds),
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
this.formatDiagnostics = (result, nowEpochSeconds) => {
|
|
79
|
+
const lines = result.metrics.map((metric) => {
|
|
80
|
+
const secondsUntilSevenDayEnd = Math.round(metric.sevenDayEndEpoch - nowEpochSeconds);
|
|
81
|
+
const status = metric.eligible
|
|
82
|
+
? 'eligible'
|
|
83
|
+
: `excluded (${metric.exclusionReason})`;
|
|
84
|
+
return `${metric.name}: 5h ${Math.round(metric.fiveHourFreeRatio * 100)}% free, 7d ${Math.round(metric.sevenDayFreeRatio * 100)}% free, 7d-end in ${secondsUntilSevenDayEnd}s -> ${status}`;
|
|
85
|
+
});
|
|
86
|
+
if (result.selected === null) {
|
|
87
|
+
lines.push('No eligible token passed the rate-limit filter.');
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
lines.push(`Selected ${result.selected.name} (soonest 7d reset among eligible tokens).`);
|
|
91
|
+
}
|
|
92
|
+
return lines;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.OauthTokenSelectHandler = OauthTokenSelectHandler;
|
|
97
|
+
//# sourceMappingURL=OauthTokenSelectHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,7 +49,7 @@ const cacheDir = () => {
|
|
|
49
49
|
exports.cacheDir = cacheDir;
|
|
50
50
|
const hashToken = (token) => crypto.createHash(HASH_ALGORITHM).update(token).digest('hex');
|
|
51
51
|
exports.hashToken = hashToken;
|
|
52
|
-
const cachePathForToken = (token
|
|
52
|
+
const cachePathForToken = (token, baseDir = (0, exports.cacheDir)()) => path.join(baseDir, `${(0, exports.hashToken)(token)}.json`);
|
|
53
53
|
exports.cachePathForToken = cachePathForToken;
|
|
54
54
|
const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
55
55
|
const readPayload = (filePath) => {
|
|
@@ -211,8 +211,8 @@ const parseModelRateLimitsFromHeaders = (headers) => {
|
|
|
211
211
|
return result;
|
|
212
212
|
};
|
|
213
213
|
exports.parseModelRateLimitsFromHeaders = parseModelRateLimitsFromHeaders;
|
|
214
|
-
const readRateLimit = (token) => {
|
|
215
|
-
const filePath = (0, exports.cachePathForToken)(token);
|
|
214
|
+
const readRateLimit = (token, baseDir = (0, exports.cacheDir)()) => {
|
|
215
|
+
const filePath = (0, exports.cachePathForToken)(token, baseDir);
|
|
216
216
|
if (!fs.existsSync(filePath))
|
|
217
217
|
return null;
|
|
218
218
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RateLimitCache.js","sourceRoot":"","sources":["../../../src/adapter/proxy/RateLimitCache.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAsBhB,QAAA,UAAU,GAAG,IAAI,CAAC;AAE/B,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEnB,QAAA,uCAAuC,GAAG,EAAE,CAAC;AAE7C,QAAA,mCAAmC,GAAG,GAAG,CAAC;AAEhD,MAAM,QAAQ,GAAG,GAAW,EAAE;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC9C,CAAC,CAAC;AAHW,QAAA,QAAQ,YAGnB;AAEK,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CACjD,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AADnD,QAAA,SAAS,aAC0C;AAEzD,MAAM,iBAAiB,GAAG,
|
|
1
|
+
{"version":3,"file":"RateLimitCache.js","sourceRoot":"","sources":["../../../src/adapter/proxy/RateLimitCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OauthTokenSelectUseCase = void 0;
|
|
4
|
+
const SECONDS_PER_DAY = 86400;
|
|
5
|
+
const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
|
|
6
|
+
const FIVE_HOUR_MIN_FREE_RATIO = 0.6;
|
|
7
|
+
const SEVEN_DAY_MIN_FREE_RATIO = 0.3;
|
|
8
|
+
class OauthTokenSelectUseCase {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.run = (candidates, nowEpochSeconds) => {
|
|
11
|
+
const evaluated = candidates.map((candidate) => ({
|
|
12
|
+
candidate,
|
|
13
|
+
metric: this.evaluate(candidate, nowEpochSeconds),
|
|
14
|
+
}));
|
|
15
|
+
const metrics = evaluated.map((entry) => entry.metric);
|
|
16
|
+
const eligible = evaluated.filter((entry) => entry.metric.eligible);
|
|
17
|
+
if (eligible.length === 0) {
|
|
18
|
+
return { selected: null, metrics };
|
|
19
|
+
}
|
|
20
|
+
const best = eligible.reduce((bestEntry, currentEntry) => currentEntry.metric.sevenDayEndEpoch < bestEntry.metric.sevenDayEndEpoch
|
|
21
|
+
? currentEntry
|
|
22
|
+
: bestEntry);
|
|
23
|
+
return { selected: best.candidate, metrics };
|
|
24
|
+
};
|
|
25
|
+
this.evaluate = (candidate, nowEpochSeconds) => {
|
|
26
|
+
const fiveHourFreeRatio = this.fiveHourFreeRatio(candidate.snapshot, nowEpochSeconds);
|
|
27
|
+
const sevenDayFreeRatio = this.sevenDayFreeRatio(candidate.snapshot, nowEpochSeconds);
|
|
28
|
+
const sevenDayEndEpoch = this.sevenDayEndEpoch(candidate.snapshot, nowEpochSeconds);
|
|
29
|
+
const exclusionReason = this.exclusionReason(fiveHourFreeRatio, sevenDayFreeRatio);
|
|
30
|
+
return {
|
|
31
|
+
name: candidate.name,
|
|
32
|
+
fiveHourFreeRatio,
|
|
33
|
+
sevenDayFreeRatio,
|
|
34
|
+
sevenDayEndEpoch,
|
|
35
|
+
eligible: exclusionReason === null,
|
|
36
|
+
exclusionReason,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
this.exclusionReason = (fiveHourFreeRatio, sevenDayFreeRatio) => {
|
|
40
|
+
if (fiveHourFreeRatio < FIVE_HOUR_MIN_FREE_RATIO) {
|
|
41
|
+
return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(FIVE_HOUR_MIN_FREE_RATIO)}%)`;
|
|
42
|
+
}
|
|
43
|
+
if (sevenDayFreeRatio < SEVEN_DAY_MIN_FREE_RATIO) {
|
|
44
|
+
return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(SEVEN_DAY_MIN_FREE_RATIO)}%)`;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
this.fiveHourFreeRatio = (snapshot, nowEpochSeconds) => {
|
|
49
|
+
if (snapshot === null) {
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
if (this.windowExpired(snapshot.fiveHourReset, nowEpochSeconds)) {
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
return this.freeRatioFromUtilization(snapshot.fiveHourUtilization);
|
|
56
|
+
};
|
|
57
|
+
this.sevenDayFreeRatio = (snapshot, nowEpochSeconds) => {
|
|
58
|
+
if (snapshot === null) {
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
if (this.windowExpired(snapshot.sevenDayReset, nowEpochSeconds)) {
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
return this.freeRatioFromUtilization(snapshot.sevenDayUtilization);
|
|
65
|
+
};
|
|
66
|
+
this.sevenDayEndEpoch = (snapshot, nowEpochSeconds) => {
|
|
67
|
+
if (snapshot === null) {
|
|
68
|
+
return nowEpochSeconds + SEVEN_DAYS_IN_SECONDS;
|
|
69
|
+
}
|
|
70
|
+
if (snapshot.sevenDayReset <= 0) {
|
|
71
|
+
return nowEpochSeconds + SEVEN_DAYS_IN_SECONDS;
|
|
72
|
+
}
|
|
73
|
+
if (this.windowExpired(snapshot.sevenDayReset, nowEpochSeconds)) {
|
|
74
|
+
return nowEpochSeconds + SEVEN_DAYS_IN_SECONDS;
|
|
75
|
+
}
|
|
76
|
+
return snapshot.sevenDayReset;
|
|
77
|
+
};
|
|
78
|
+
this.windowExpired = (resetEpoch, nowEpochSeconds) => resetEpoch > 0 && nowEpochSeconds > resetEpoch;
|
|
79
|
+
this.freeRatioFromUtilization = (utilization) => {
|
|
80
|
+
const bounded = Math.min(Math.max(utilization, 0), 1);
|
|
81
|
+
return 1 - bounded;
|
|
82
|
+
};
|
|
83
|
+
this.toPercent = (ratio) => Math.round(ratio * 100);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.OauthTokenSelectUseCase = OauthTokenSelectUseCase;
|
|
87
|
+
//# sourceMappingURL=OauthTokenSelectUseCase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
startConsoleServer,
|
|
38
38
|
} from '../console/consoleServer';
|
|
39
39
|
import { IssueTitleStateCache } from '../console/consoleReadApi';
|
|
40
|
+
import { OauthTokenSelectHandler } from '../handlers/OauthTokenSelectHandler';
|
|
40
41
|
|
|
41
42
|
type StartDaemonOptions = {
|
|
42
43
|
projectUrl?: string;
|
|
@@ -72,6 +73,11 @@ type ServeConsoleOptions = {
|
|
|
72
73
|
consoleDataOutputDir?: string;
|
|
73
74
|
};
|
|
74
75
|
|
|
76
|
+
type SelectOauthTokenOptions = {
|
|
77
|
+
tokenListJsonPath?: string;
|
|
78
|
+
cacheDir?: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
75
81
|
const buildGithubRepositoryParams = (
|
|
76
82
|
localStorageRepository: LocalStorageRepository,
|
|
77
83
|
token: string,
|
|
@@ -681,6 +687,38 @@ program
|
|
|
681
687
|
console.log(`TDPM Console server listening on port ${port}`);
|
|
682
688
|
});
|
|
683
689
|
|
|
690
|
+
program
|
|
691
|
+
.command('selectOauthToken')
|
|
692
|
+
.description(
|
|
693
|
+
'Print exactly one Claude Code OAuth token chosen by a rate-limit-aware filter. The token string is written to stdout (pipeable); the per-candidate decision trace is written to stderr. Exits non-zero when no token passes the filter.',
|
|
694
|
+
)
|
|
695
|
+
.option(
|
|
696
|
+
'--tokenListJsonPath <path>',
|
|
697
|
+
'Path to the JSON array of { name, token } records. Falls back to the CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH environment variable.',
|
|
698
|
+
)
|
|
699
|
+
.option(
|
|
700
|
+
'--cacheDir <path>',
|
|
701
|
+
'Directory holding per-token rate-limit cache files. Falls back to the TDPM_RATELIMIT_CACHE_DIR environment variable, then to ${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit.',
|
|
702
|
+
)
|
|
703
|
+
.action((options: SelectOauthTokenOptions) => {
|
|
704
|
+
const handler = new OauthTokenSelectHandler();
|
|
705
|
+
const output = handler.handle({
|
|
706
|
+
tokenListJsonPath: options.tokenListJsonPath ?? null,
|
|
707
|
+
cacheDirectory: options.cacheDir ?? null,
|
|
708
|
+
nowEpochSeconds: Date.now() / 1000,
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
for (const line of output.diagnostics) {
|
|
712
|
+
console.error(line);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (output.selectedToken === null) {
|
|
716
|
+
process.exit(1);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
process.stdout.write(`${output.selectedToken}\n`);
|
|
720
|
+
});
|
|
721
|
+
|
|
684
722
|
/* istanbul ignore next */
|
|
685
723
|
if (process.argv && require.main === module) {
|
|
686
724
|
program.parse(process.argv);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
hasDotSegment,
|
|
10
10
|
requiresToken,
|
|
11
11
|
isTokenValid,
|
|
12
|
+
isConsoleAppRoute,
|
|
12
13
|
extractProvidedToken,
|
|
13
14
|
startConsoleServer,
|
|
14
15
|
} from './consoleServer';
|
|
@@ -61,6 +62,35 @@ describe('consoleServer pure helpers', () => {
|
|
|
61
62
|
});
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
describe('isConsoleAppRoute', () => {
|
|
66
|
+
it('matches a per-project root route', () => {
|
|
67
|
+
expect(isConsoleAppRoute('/projects/umino')).toBe(true);
|
|
68
|
+
expect(isConsoleAppRoute('/projects/umino/')).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('matches a per-project tab route for every list tab', () => {
|
|
72
|
+
expect(isConsoleAppRoute('/projects/umino/prs')).toBe(true);
|
|
73
|
+
expect(isConsoleAppRoute('/projects/xmile/triage')).toBe(true);
|
|
74
|
+
expect(isConsoleAppRoute('/projects/xcare/unread')).toBe(true);
|
|
75
|
+
expect(isConsoleAppRoute('/projects/utage3/failed-preparation')).toBe(
|
|
76
|
+
true,
|
|
77
|
+
);
|
|
78
|
+
expect(isConsoleAppRoute('/projects/utage3/todo-by-human')).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('does not match data, api, or unknown tab routes', () => {
|
|
82
|
+
expect(isConsoleAppRoute('/projects/umino/prs/list.json')).toBe(false);
|
|
83
|
+
expect(isConsoleAppRoute('/projects/umino/unknown')).toBe(false);
|
|
84
|
+
expect(isConsoleAppRoute('/projects')).toBe(false);
|
|
85
|
+
expect(isConsoleAppRoute('/api/review')).toBe(false);
|
|
86
|
+
expect(isConsoleAppRoute('/')).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not match a dot-prefixed pjcode', () => {
|
|
90
|
+
expect(isConsoleAppRoute('/projects/.git')).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
64
94
|
describe('isTokenValid', () => {
|
|
65
95
|
it('accepts a matching token', () => {
|
|
66
96
|
expect(isTokenValid('expected', 'expected')).toBe(true);
|
|
@@ -230,6 +260,57 @@ describe('consoleServer integration', () => {
|
|
|
230
260
|
}
|
|
231
261
|
});
|
|
232
262
|
|
|
263
|
+
it('serves the SPA index for per-project app routes without a token', async () => {
|
|
264
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
265
|
+
const uiDistDir = path.join(tmpDir, 'ui-dist');
|
|
266
|
+
fs.mkdirSync(uiDistDir, { recursive: true });
|
|
267
|
+
fs.writeFileSync(
|
|
268
|
+
path.join(uiDistDir, 'index.html'),
|
|
269
|
+
'<!DOCTYPE html><title>spa</title><div id="root"></div>',
|
|
270
|
+
);
|
|
271
|
+
const server = await startConsoleServer({
|
|
272
|
+
accessToken: testToken,
|
|
273
|
+
uiDistDir,
|
|
274
|
+
consoleDataOutputDir: null,
|
|
275
|
+
port: 0,
|
|
276
|
+
});
|
|
277
|
+
try {
|
|
278
|
+
const projectRoot = await requestServer(server, '/projects/umino');
|
|
279
|
+
expect(projectRoot.statusCode).toBe(200);
|
|
280
|
+
expect(projectRoot.body).toContain('spa');
|
|
281
|
+
expect(projectRoot.contentType).toContain('text/html');
|
|
282
|
+
expect(projectRoot.cacheControl).toBe('no-store');
|
|
283
|
+
|
|
284
|
+
const projectTab = await requestServer(server, '/projects/xmile/prs');
|
|
285
|
+
expect(projectTab.statusCode).toBe(200);
|
|
286
|
+
expect(projectTab.body).toContain('spa');
|
|
287
|
+
|
|
288
|
+
const unknownTab = await requestServer(server, '/projects/xmile/unknown');
|
|
289
|
+
expect(unknownTab.statusCode).toBe(404);
|
|
290
|
+
} finally {
|
|
291
|
+
await closeServer(server);
|
|
292
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('serves the placeholder index for per-project routes when ui-dist is absent', async () => {
|
|
297
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
298
|
+
const server = await startConsoleServer({
|
|
299
|
+
accessToken: testToken,
|
|
300
|
+
uiDistDir: path.join(tmpDir, 'missing-ui-dist'),
|
|
301
|
+
consoleDataOutputDir: null,
|
|
302
|
+
port: 0,
|
|
303
|
+
});
|
|
304
|
+
try {
|
|
305
|
+
const projectRoot = await requestServer(server, '/projects/umino/triage');
|
|
306
|
+
expect(projectRoot.statusCode).toBe(200);
|
|
307
|
+
expect(projectRoot.body).toContain('TDPM Console');
|
|
308
|
+
} finally {
|
|
309
|
+
await closeServer(server);
|
|
310
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
233
314
|
it('rejects dot-prefixed paths with 404', async () => {
|
|
234
315
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
235
316
|
const uiDistDir = path.join(tmpDir, 'ui-dist');
|
|
@@ -4,6 +4,7 @@ import * as path from 'path';
|
|
|
4
4
|
import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
5
5
|
import { Project } from '../../../domain/entities/Project';
|
|
6
6
|
import {
|
|
7
|
+
CONSOLE_LIST_TAB_NAMES,
|
|
7
8
|
buildConsoleDataResponse,
|
|
8
9
|
parseConsoleDataRoute,
|
|
9
10
|
} from './consoleDataDelivery';
|
|
@@ -70,6 +71,29 @@ export const requiresToken = (requestPath: string): boolean =>
|
|
|
70
71
|
requestPath === '/api' ||
|
|
71
72
|
requestPath.endsWith('.json');
|
|
72
73
|
|
|
74
|
+
const SAFE_PJCODE = /^[A-Za-z0-9._-]+$/;
|
|
75
|
+
|
|
76
|
+
export const isConsoleAppRoute = (requestPath: string): boolean => {
|
|
77
|
+
const segments = requestPath
|
|
78
|
+
.split('/')
|
|
79
|
+
.filter((segment) => segment.length > 0);
|
|
80
|
+
if (segments.length < 2 || segments[0] !== 'projects') {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const pjcode = segments[1];
|
|
84
|
+
if (!SAFE_PJCODE.test(pjcode) || pjcode.startsWith('.')) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (segments.length === 2) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
if (segments.length !== 3) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const tab = segments[2];
|
|
94
|
+
return CONSOLE_LIST_TAB_NAMES.includes(tab);
|
|
95
|
+
};
|
|
96
|
+
|
|
73
97
|
export const isTokenValid = (
|
|
74
98
|
expectedToken: string,
|
|
75
99
|
providedToken: string | null,
|
|
@@ -159,6 +183,24 @@ const serveBootstrapIndex = (response: http.ServerResponse): void => {
|
|
|
159
183
|
response.end(PLACEHOLDER_INDEX_HTML);
|
|
160
184
|
};
|
|
161
185
|
|
|
186
|
+
const serveIndexHtml = (
|
|
187
|
+
options: ConsoleServerOptions,
|
|
188
|
+
response: http.ServerResponse,
|
|
189
|
+
): void => {
|
|
190
|
+
const indexFilePath = resolveStaticFilePath(options.uiDistDir, '/index.html');
|
|
191
|
+
const indexContent =
|
|
192
|
+
indexFilePath === null ? null : readStaticFile(indexFilePath);
|
|
193
|
+
if (indexContent === null) {
|
|
194
|
+
serveBootstrapIndex(response);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
response.writeHead(200, {
|
|
198
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
199
|
+
'Cache-Control': 'no-store',
|
|
200
|
+
});
|
|
201
|
+
response.end(indexContent);
|
|
202
|
+
};
|
|
203
|
+
|
|
162
204
|
const sendJson = (
|
|
163
205
|
response: http.ServerResponse,
|
|
164
206
|
statusCode: number,
|
|
@@ -369,22 +411,12 @@ export const handleConsoleRequest = async (
|
|
|
369
411
|
return;
|
|
370
412
|
}
|
|
371
413
|
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
indexFilePath === null ? null : readStaticFile(indexFilePath);
|
|
379
|
-
if (indexContent === null) {
|
|
380
|
-
serveBootstrapIndex(response);
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
response.writeHead(200, {
|
|
384
|
-
'Content-Type': 'text/html; charset=utf-8',
|
|
385
|
-
'Cache-Control': 'no-store',
|
|
386
|
-
});
|
|
387
|
-
response.end(indexContent);
|
|
414
|
+
if (
|
|
415
|
+
requestPath === '/' ||
|
|
416
|
+
requestPath === '/index.html' ||
|
|
417
|
+
isConsoleAppRoute(requestPath)
|
|
418
|
+
) {
|
|
419
|
+
serveIndexHtml(options, response);
|
|
388
420
|
return;
|
|
389
421
|
}
|
|
390
422
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { ConsoleProjectHeader } from './ConsoleProjectHeader';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof ConsoleProjectHeader> = {
|
|
5
|
+
title: 'Console/ConsoleProjectHeader',
|
|
6
|
+
component: ConsoleProjectHeader,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof ConsoleProjectHeader>;
|
|
12
|
+
|
|
13
|
+
export const WithProject: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
pjcode: 'umino',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const AnotherProject: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
pjcode: 'xmile',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const NoProject: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
pjcode: null,
|
|
28
|
+
},
|
|
29
|
+
};
|
package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleProjectHeader.tsx
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ConsoleProjectHeaderProps = {
|
|
2
|
+
pjcode: string | null;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const ConsoleProjectHeader = ({ pjcode }: ConsoleProjectHeaderProps) => (
|
|
6
|
+
<header className="flex items-baseline gap-2 border-b border-border p-3">
|
|
7
|
+
<h1 className="text-base font-semibold">TDPM Console</h1>
|
|
8
|
+
{pjcode === null ? (
|
|
9
|
+
<span className="text-sm text-muted-foreground">no project selected</span>
|
|
10
|
+
) : (
|
|
11
|
+
<span className="text-sm text-muted-foreground">project: {pjcode}</span>
|
|
12
|
+
)}
|
|
13
|
+
</header>
|
|
14
|
+
);
|