@shepai/cli 1.103.1 → 1.104.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/dist/packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.d.ts +27 -0
- package/dist/packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.d.ts.map +1 -0
- package/dist/packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.js +128 -0
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +5 -0
- package/dist/src/presentation/cli/ui/symbols.d.ts +1 -1
- package/dist/src/presentation/web/app/api/cli-upgrade/route.d.ts +3 -0
- package/dist/src/presentation/web/app/api/cli-upgrade/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/cli-upgrade/route.js +31 -0
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +50 -8
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts +4 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +31 -11
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +35 -2
- package/dist/src/presentation/web/components/common/version-badge/version-badge.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/version-badge/version-badge.js +6 -2
- package/dist/src/presentation/web/components/common/version-badge/version-badge.stories.d.ts +2 -2
- package/dist/src/presentation/web/components/common/version-badge/version-badge.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/version-badge/version-badge.stories.js +20 -3
- package/dist/src/presentation/web/hooks/use-cli-upgrade.d.ts +11 -0
- package/dist/src/presentation/web/hooks/use-cli-upgrade.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-cli-upgrade.js +73 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -0
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +2 -2
- package/web/.next/required-server-files.json +2 -2
- package/web/.next/routes-manifest.json +6 -0
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +18 -18
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
- package/web/.next/server/app/api/cli-upgrade/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/cli-upgrade/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/cli-upgrade/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/cli-upgrade/route.js +6 -0
- package/web/.next/server/app/api/cli-upgrade/route.js.map +5 -0
- package/web/.next/server/app/api/cli-upgrade/route.js.nft.json +1 -0
- package/web/.next/server/app/api/cli-upgrade/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
- package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page.js.nft.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_cli-upgrade_route_actions_dc509b52.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_cli-upgrade_route_actions_dc509b52.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__d2c18946._.js +11 -0
- package/web/.next/server/chunks/[root-of-the-server]__d2c18946._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_b3352e34._.js → _1e9bf571._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_b3352e34._.js.map → _1e9bf571._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
- package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_23e5ab2c._.js → _c189deb2._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_23e5ab2c._.js.map → _c189deb2._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_82e0cb85._.js → _eafbd006._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_82e0cb85._.js.map → _eafbd006._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_7275ce5d._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_7275ce5d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_758dfa10._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_758dfa10._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +30 -30
- package/web/.next/static/chunks/114abb475679448b.js +2 -0
- package/web/.next/static/chunks/{8d98f1d9341e7698.js → 3596ba73f2cbc86f.js} +1 -1
- package/web/.next/static/chunks/{2016bacd1f5fcc12.js → 37b1807da4cd1ea0.js} +1 -1
- package/web/.next/static/chunks/3bb35e0689e8b6b7.js +1 -0
- package/web/.next/static/chunks/{4fd27329675da3a8.js → 4cef83745ab38234.js} +1 -1
- package/web/.next/static/chunks/{2e2bbd77e10e036c.js → 7f84301a5dd54554.js} +1 -1
- package/web/.next/static/chunks/{8b1d0f725365fc83.js → 85b34b1ecd6b8562.js} +1 -1
- package/web/.next/static/chunks/{a0f87e10178f4c0e.js → b352ef3d923492dc.js} +1 -1
- package/web/.next/static/chunks/{396a8b5222dfe0f1.js → dcf2a8f422f008df.js} +1 -1
- package/web/.next/static/chunks/{d7cc1b6cf702a840.js → e87a7aacbfc2862a.js} +2 -2
- package/web/.next/static/chunks/{3e1f5c0f67be293f.js → e8c5a627803b1701.js} +1 -1
- package/web/.next/static/chunks/0c1c3c49b2145c9b.js +0 -2
- package/web/.next/static/chunks/2a6f05312c4f37ce.js +0 -1
- /package/web/.next/static/{7gAZbyat8KbbWMGw1K8nJ → gjuhrSkwZrfQPA7lKPGEW}/_buildManifest.js +0 -0
- /package/web/.next/static/{7gAZbyat8KbbWMGw1K8nJ → gjuhrSkwZrfQPA7lKPGEW}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{7gAZbyat8KbbWMGw1K8nJ → gjuhrSkwZrfQPA7lKPGEW}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UpgradeCliUseCase
|
|
3
|
+
*
|
|
4
|
+
* Executes a self-upgrade of @shepai/cli to the latest published version.
|
|
5
|
+
* Mirrors the CLI upgrade command logic but designed for use from the web layer.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. Check latest version from npm registry (fail-open)
|
|
9
|
+
* 2. Compare with current version — return early if up to date
|
|
10
|
+
* 3. Run `npm i -g @shepai/cli@latest` with streamed output
|
|
11
|
+
* 4. Return result with old/new version info
|
|
12
|
+
*/
|
|
13
|
+
import type { IVersionService } from '../../ports/output/services/version-service.interface.js';
|
|
14
|
+
export interface UpgradeResult {
|
|
15
|
+
status: 'up-to-date' | 'upgraded' | 'error';
|
|
16
|
+
currentVersion: string;
|
|
17
|
+
latestVersion: string | null;
|
|
18
|
+
errorMessage?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class UpgradeCliUseCase {
|
|
21
|
+
private readonly versionService;
|
|
22
|
+
constructor(versionService: IVersionService);
|
|
23
|
+
execute(onOutput?: (data: string) => void): Promise<UpgradeResult>;
|
|
24
|
+
private getLatestVersion;
|
|
25
|
+
private runNpmInstall;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=upgrade-cli.use-case.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-cli.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAEhG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,qBACa,iBAAiB;IAG1B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAAd,cAAc,EAAE,eAAe;IAG5C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC;IAkCxE,OAAO,CAAC,gBAAgB;IA6CxB,OAAO,CAAC,aAAa;CAuBtB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UpgradeCliUseCase
|
|
3
|
+
*
|
|
4
|
+
* Executes a self-upgrade of @shepai/cli to the latest published version.
|
|
5
|
+
* Mirrors the CLI upgrade command logic but designed for use from the web layer.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. Check latest version from npm registry (fail-open)
|
|
9
|
+
* 2. Compare with current version — return early if up to date
|
|
10
|
+
* 3. Run `npm i -g @shepai/cli@latest` with streamed output
|
|
11
|
+
* 4. Return result with old/new version info
|
|
12
|
+
*/
|
|
13
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
14
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
15
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
16
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
17
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
18
|
+
};
|
|
19
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
21
|
+
};
|
|
22
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
23
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
24
|
+
};
|
|
25
|
+
import { injectable, inject } from 'tsyringe';
|
|
26
|
+
import { spawn } from 'node:child_process';
|
|
27
|
+
const VERSION_CHECK_TIMEOUT_MS = 10_000;
|
|
28
|
+
let UpgradeCliUseCase = class UpgradeCliUseCase {
|
|
29
|
+
versionService;
|
|
30
|
+
constructor(versionService) {
|
|
31
|
+
this.versionService = versionService;
|
|
32
|
+
}
|
|
33
|
+
async execute(onOutput) {
|
|
34
|
+
const { version: currentVersion } = this.versionService.getVersion();
|
|
35
|
+
// 1. Check latest version (fail-open)
|
|
36
|
+
const latestVersion = await this.getLatestVersion(onOutput);
|
|
37
|
+
// 2. Already up to date
|
|
38
|
+
if (latestVersion && latestVersion === currentVersion) {
|
|
39
|
+
onOutput?.(`Already up to date (v${currentVersion})\n`);
|
|
40
|
+
return { status: 'up-to-date', currentVersion, latestVersion };
|
|
41
|
+
}
|
|
42
|
+
// 3. Run upgrade
|
|
43
|
+
const target = latestVersion ? `v${latestVersion}` : 'latest';
|
|
44
|
+
onOutput?.(`Upgrading from v${currentVersion} to ${target}...\n`);
|
|
45
|
+
try {
|
|
46
|
+
const exitCode = await this.runNpmInstall(onOutput);
|
|
47
|
+
if (exitCode === 0) {
|
|
48
|
+
onOutput?.(`Successfully upgraded to ${target}\n`);
|
|
49
|
+
return { status: 'upgraded', currentVersion, latestVersion };
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const msg = `npm install exited with code ${exitCode}`;
|
|
53
|
+
onOutput?.(`Error: ${msg}\n`);
|
|
54
|
+
return { status: 'error', currentVersion, latestVersion, errorMessage: msg };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
const msg = error instanceof Error ? error.message : 'Installation failed';
|
|
59
|
+
onOutput?.(`Error: ${msg}\n`);
|
|
60
|
+
return { status: 'error', currentVersion, latestVersion, errorMessage: msg };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
getLatestVersion(onOutput) {
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
let output = '';
|
|
66
|
+
let settled = false;
|
|
67
|
+
const child = spawn('npm', ['view', '@shepai/cli', 'version'], {
|
|
68
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
69
|
+
});
|
|
70
|
+
const timeout = setTimeout(() => {
|
|
71
|
+
if (!settled) {
|
|
72
|
+
settled = true;
|
|
73
|
+
child.kill();
|
|
74
|
+
onOutput?.('Version check timed out, proceeding with upgrade...\n');
|
|
75
|
+
resolve(null);
|
|
76
|
+
}
|
|
77
|
+
}, VERSION_CHECK_TIMEOUT_MS);
|
|
78
|
+
child.stdout?.on('data', (data) => {
|
|
79
|
+
output += data.toString();
|
|
80
|
+
});
|
|
81
|
+
child.on('close', (code) => {
|
|
82
|
+
if (!settled) {
|
|
83
|
+
settled = true;
|
|
84
|
+
clearTimeout(timeout);
|
|
85
|
+
if (code === 0 && output.trim()) {
|
|
86
|
+
resolve(output.trim());
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
resolve(null);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
child.on('error', () => {
|
|
94
|
+
if (!settled) {
|
|
95
|
+
settled = true;
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
onOutput?.('Could not check latest version\n');
|
|
98
|
+
resolve(null);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
runNpmInstall(onOutput) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
const child = spawn('npm', ['i', '-g', '@shepai/cli@latest'], {
|
|
106
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
107
|
+
});
|
|
108
|
+
child.stdout?.on('data', (data) => {
|
|
109
|
+
onOutput?.(data.toString());
|
|
110
|
+
});
|
|
111
|
+
child.stderr?.on('data', (data) => {
|
|
112
|
+
onOutput?.(data.toString());
|
|
113
|
+
});
|
|
114
|
+
child.on('close', (code) => {
|
|
115
|
+
resolve(code ?? 1);
|
|
116
|
+
});
|
|
117
|
+
child.on('error', (err) => {
|
|
118
|
+
reject(err);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
UpgradeCliUseCase = __decorate([
|
|
124
|
+
injectable(),
|
|
125
|
+
__param(0, inject('IVersionService')),
|
|
126
|
+
__metadata("design:paramtypes", [Object])
|
|
127
|
+
], UpgradeCliUseCase);
|
|
128
|
+
export { UpgradeCliUseCase };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAkHrC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,SAAS,CAAC,CA8SrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -77,6 +77,7 @@ import { DeleteRepositoryUseCase } from '../../application/use-cases/repositorie
|
|
|
77
77
|
import { CheckAndUnblockFeaturesUseCase } from '../../application/use-cases/features/check-and-unblock-features.use-case.js';
|
|
78
78
|
import { UpdateFeatureLifecycleUseCase } from '../../application/use-cases/features/update/update-feature-lifecycle.use-case.js';
|
|
79
79
|
import { CleanupFeatureWorktreeUseCase } from '../../application/use-cases/features/cleanup-feature-worktree.use-case.js';
|
|
80
|
+
import { UpgradeCliUseCase } from '../../application/use-cases/upgrade/upgrade-cli.use-case.js';
|
|
80
81
|
// Session listing
|
|
81
82
|
import { ClaudeCodeSessionRepository } from '../services/agents/sessions/claude-code-session.repository.js';
|
|
82
83
|
import { StubSessionRepository } from '../services/agents/sessions/stub-session.repository.js';
|
|
@@ -279,6 +280,7 @@ export async function initializeContainer() {
|
|
|
279
280
|
container.registerSingleton(CheckAndUnblockFeaturesUseCase);
|
|
280
281
|
container.registerSingleton(UpdateFeatureLifecycleUseCase);
|
|
281
282
|
container.registerSingleton(CleanupFeatureWorktreeUseCase);
|
|
283
|
+
container.registerSingleton(UpgradeCliUseCase);
|
|
282
284
|
// Session repositories (per-AgentType string tokens)
|
|
283
285
|
container.register(`IAgentSessionRepository:${AgentType.ClaudeCode}`, {
|
|
284
286
|
useFactory: () => new ClaudeCodeSessionRepository(),
|
|
@@ -360,6 +362,9 @@ export async function initializeContainer() {
|
|
|
360
362
|
container.register('CleanupFeatureWorktreeUseCase', {
|
|
361
363
|
useFactory: (c) => c.resolve(CleanupFeatureWorktreeUseCase),
|
|
362
364
|
});
|
|
365
|
+
container.register('UpgradeCliUseCase', {
|
|
366
|
+
useFactory: (c) => c.resolve(UpgradeCliUseCase),
|
|
367
|
+
});
|
|
363
368
|
_initialized = true;
|
|
364
369
|
return container;
|
|
365
370
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/app/api/cli-upgrade/route.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC,wBAAsB,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CA+B9C"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { resolve } from '../../../lib/server-container.js';
|
|
2
|
+
// Force dynamic — SSE streams must never be statically optimized or cached
|
|
3
|
+
export const dynamic = 'force-dynamic';
|
|
4
|
+
export async function POST() {
|
|
5
|
+
const useCase = resolve('UpgradeCliUseCase');
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const stream = new ReadableStream({
|
|
8
|
+
async start(controller) {
|
|
9
|
+
try {
|
|
10
|
+
const result = await useCase.execute((chunk) => {
|
|
11
|
+
controller.enqueue(encoder.encode(`data: ${chunk.replace(/\n/g, '\ndata: ')}\n\n`));
|
|
12
|
+
});
|
|
13
|
+
controller.enqueue(encoder.encode(`event: done\ndata: ${JSON.stringify(result)}\n\n`));
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
const message = error instanceof Error ? error.message : 'Upgrade failed';
|
|
17
|
+
controller.enqueue(encoder.encode(`event: done\ndata: ${JSON.stringify({ status: 'error', currentVersion: '', latestVersion: null, errorMessage: message })}\n\n`));
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
controller.close();
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return new Response(stream, {
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'text/event-stream',
|
|
27
|
+
'Cache-Control': 'no-cache',
|
|
28
|
+
Connection: 'keep-alive',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,
|
|
1
|
+
{"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CA6iB1F"}
|
package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js
CHANGED
|
@@ -329,7 +329,7 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
|
|
|
329
329
|
// ── Body ──────────────────────────────────────────────────────────────
|
|
330
330
|
let body = null;
|
|
331
331
|
if (view.type === 'feature' && featureNode) {
|
|
332
|
-
body = (_jsx(FeatureDrawerTabs, { featureNode: featureNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput }));
|
|
332
|
+
body = (_jsx(FeatureDrawerTabs, { featureNode: featureNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, sseEvents: events, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput }));
|
|
333
333
|
}
|
|
334
334
|
return (_jsx(BaseDrawer, { open: isOpen, onClose: attemptClose, size: "md", modal: false, header: header, "data-testid": view.type === 'feature' ? 'feature-drawer' : 'repository-drawer', children: body }));
|
|
335
335
|
}
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AA8CjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AA6BD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EAAE,EAC1B,QAAQ,CAAC,EAAE,qBAAqB,EAAE,GACjC,iBAAiB,EAAE,CAgErB;AAMD,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CA4D3F"}
|
|
@@ -1,8 +1,48 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
4
|
import { Loader2, AlertCircle, Clock } from 'lucide-react';
|
|
4
5
|
import { InlineAttachments } from '../../common/inline-attachments/index.js';
|
|
5
6
|
import { formatDuration } from '../../../lib/format-duration.js';
|
|
7
|
+
/**
|
|
8
|
+
* Computes the effective duration for a phase timing entry.
|
|
9
|
+
* For completed phases, uses the server-provided durationMs.
|
|
10
|
+
* For in-progress phases (has startedAt but no completedAt), computes
|
|
11
|
+
* elapsed time client-side so the UI shows a live-updating timer.
|
|
12
|
+
*/
|
|
13
|
+
function getEffectiveDuration(timing, now) {
|
|
14
|
+
if (timing.durationMs != null && timing.durationMs > 0)
|
|
15
|
+
return timing.durationMs;
|
|
16
|
+
if (!timing.completedAt && timing.startedAt) {
|
|
17
|
+
const started = new Date(timing.startedAt).getTime();
|
|
18
|
+
if (!Number.isNaN(started))
|
|
19
|
+
return Math.max(0, now - started);
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
/** Returns true if any non-lifecycle phase is still in progress. */
|
|
24
|
+
function hasRunningPhase(timings) {
|
|
25
|
+
return timings.some((t) => !t.phase.startsWith('run:') && !t.completedAt && t.startedAt);
|
|
26
|
+
}
|
|
27
|
+
/** Hook that ticks every second while there are running phases. */
|
|
28
|
+
function useTickingNow(timings) {
|
|
29
|
+
const [now, setNow] = useState(Date.now);
|
|
30
|
+
const intervalRef = useRef(null);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const running = timings ? hasRunningPhase(timings) : false;
|
|
33
|
+
if (running) {
|
|
34
|
+
// Tick every second
|
|
35
|
+
intervalRef.current = setInterval(() => setNow(Date.now()), 1000);
|
|
36
|
+
}
|
|
37
|
+
return () => {
|
|
38
|
+
if (intervalRef.current) {
|
|
39
|
+
clearInterval(intervalRef.current);
|
|
40
|
+
intervalRef.current = null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}, [timings]);
|
|
44
|
+
return now;
|
|
45
|
+
}
|
|
6
46
|
const NODE_TO_PHASE = {
|
|
7
47
|
analyze: 'Analyzing',
|
|
8
48
|
requirements: 'Requirements',
|
|
@@ -94,6 +134,7 @@ export function buildLifecycleTimeline(timings, feedback) {
|
|
|
94
134
|
* Component
|
|
95
135
|
* ------------------------------------------------------------------------- */
|
|
96
136
|
export function ActivityTab({ timings, loading, error, rejectionFeedback }) {
|
|
137
|
+
const now = useTickingNow(timings);
|
|
97
138
|
if (loading) {
|
|
98
139
|
return (_jsx("div", { "data-testid": "activity-tab-loading", className: "flex items-center justify-center p-8", children: _jsx(Loader2, { className: "text-muted-foreground h-6 w-6 animate-spin" }) }));
|
|
99
140
|
}
|
|
@@ -104,24 +145,24 @@ export function ActivityTab({ timings, loading, error, rejectionFeedback }) {
|
|
|
104
145
|
return (_jsxs("div", { className: "flex flex-col items-center justify-center gap-2 p-8", children: [_jsx(Clock, { className: "text-muted-foreground h-8 w-8" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "No activity recorded yet" })] }));
|
|
105
146
|
}
|
|
106
147
|
const nodeTimings = timings.filter((t) => !isLifecycleEvent(t.phase));
|
|
107
|
-
const maxDurationMs = Math.max(...nodeTimings.map((t) => t
|
|
148
|
+
const maxDurationMs = Math.max(...nodeTimings.map((t) => getEffectiveDuration(t, now)), ...nodeTimings.map((t) => t.approvalWaitMs ?? 0), 0);
|
|
108
149
|
const iterations = buildLifecycleTimeline(timings, rejectionFeedback);
|
|
109
150
|
const hasMultipleIterations = iterations.length > 1;
|
|
110
|
-
const totalExecMs = nodeTimings.reduce((sum, t) => sum + (t
|
|
151
|
+
const totalExecMs = nodeTimings.reduce((sum, t) => sum + getEffectiveDuration(t, now), 0);
|
|
111
152
|
const totalWaitMs = nodeTimings.reduce((sum, t) => sum + (t.approvalWaitMs ?? 0), 0);
|
|
112
|
-
return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsx("div", { "data-testid": "activity-timings", className: "flex flex-col gap-3", children: iterations.map((iteration) => (_jsx(IterationGroup, { iteration: iteration, showHeader: hasMultipleIterations, maxDurationMs: maxDurationMs }, iteration.number))) }), totalExecMs > 0 ? (_jsx(SummaryTotals, { totalExecMs: totalExecMs, totalWaitMs: totalWaitMs })) : null] }));
|
|
153
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsx("div", { "data-testid": "activity-timings", className: "flex flex-col gap-3", children: iterations.map((iteration) => (_jsx(IterationGroup, { iteration: iteration, showHeader: hasMultipleIterations, maxDurationMs: maxDurationMs, now: now }, iteration.number))) }), totalExecMs > 0 ? (_jsx(SummaryTotals, { totalExecMs: totalExecMs, totalWaitMs: totalWaitMs })) : null] }));
|
|
113
154
|
}
|
|
114
155
|
/* ---------------------------------------------------------------------------
|
|
115
156
|
* Iteration group — one cycle of work
|
|
116
157
|
* ------------------------------------------------------------------------- */
|
|
117
|
-
function IterationGroup({ iteration, showHeader, maxDurationMs, }) {
|
|
158
|
+
function IterationGroup({ iteration, showHeader, maxDurationMs, now, }) {
|
|
118
159
|
return (_jsxs("div", { "data-testid": `iteration-${iteration.number}`, className: "flex flex-col gap-1.5", children: [showHeader ? (_jsxs("div", { className: "text-muted-foreground text-xs font-medium", children: ["Iteration ", iteration.number] })) : null, iteration.timings.map((t) => {
|
|
119
160
|
if (isLifecycleEvent(t.phase)) {
|
|
120
161
|
// For run:rejected, the message comes from the iteration, not a separate map
|
|
121
162
|
const isRejection = t.phase === 'run:rejected';
|
|
122
163
|
return (_jsx(LifecycleEventRow, { timing: t, message: isRejection ? iteration.rejectionMessage : undefined, attachments: isRejection ? iteration.rejectionAttachments : undefined }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
|
|
123
164
|
}
|
|
124
|
-
return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
|
|
165
|
+
return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs, now: now }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
|
|
125
166
|
})] }));
|
|
126
167
|
}
|
|
127
168
|
/* ---------------------------------------------------------------------------
|
|
@@ -134,14 +175,15 @@ function LifecycleEventRow({ timing, message, attachments, }) {
|
|
|
134
175
|
const hasAttachments = attachments && attachments.length > 0;
|
|
135
176
|
return (_jsxs("div", { className: `text-xs ${event.colorClass}`, children: [_jsx("span", { children: event.label }), message ? (_jsxs("span", { "data-testid": "rejection-feedback-text", className: "text-muted-foreground ml-2 font-normal italic", children: ["\u2014 ", message] })) : null, hasAttachments ? (_jsx("div", { "data-testid": "rejection-feedback-attachments", className: "mt-1.5 ml-2", children: _jsx(InlineAttachments, { text: "", attachmentPaths: attachments }) })) : null] }));
|
|
136
177
|
}
|
|
137
|
-
function NodeTimingRow({ timing, maxDurationMs, }) {
|
|
178
|
+
function NodeTimingRow({ timing, maxDurationMs, now, }) {
|
|
138
179
|
const label = timing.phase.includes(':')
|
|
139
180
|
? `rev ${timing.phase.split(':')[1]}`
|
|
140
181
|
: (NODE_TO_PHASE[timing.phase] ?? timing.phase);
|
|
141
|
-
const durationMs = timing
|
|
182
|
+
const durationMs = getEffectiveDuration(timing, now);
|
|
142
183
|
const barPercent = maxDurationMs > 0 ? Math.max(2, (durationMs / maxDurationMs) * 100) : 2;
|
|
184
|
+
const isRunning = !timing.completedAt && !!timing.startedAt;
|
|
143
185
|
const barColorClass = timing.completedAt ? 'bg-emerald-500' : 'bg-blue-500';
|
|
144
|
-
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { "data-testid": `timing-bar-${timing.phase}`, className: "flex items-center gap-3", children: [_jsx("span", { className: "text-muted-foreground w-24 shrink-0 text-xs", children: label }), _jsx("div", { className: "bg-muted h-3 flex-1 overflow-hidden rounded-full", children: _jsx("div", { className: `h-full rounded-full ${barColorClass}`, style: { width: `${Math.min(barPercent, 100)}%` } }) }), _jsx("span", { className: "text-muted-foreground w-14 shrink-0 text-right text-xs", children: formatDuration(durationMs) })] }), timing.approvalWaitMs != null && timing.approvalWaitMs > 0 ? (_jsx(ApprovalWaitRow, { timing: timing, maxDurationMs: maxDurationMs })) : null] }));
|
|
186
|
+
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { "data-testid": `timing-bar-${timing.phase}`, className: "flex items-center gap-3", children: [_jsx("span", { className: "text-muted-foreground w-24 shrink-0 text-xs", children: label }), _jsx("div", { className: "bg-muted h-3 flex-1 overflow-hidden rounded-full", children: _jsx("div", { className: `h-full rounded-full ${barColorClass}${isRunning ? 'animate-pulse' : ''}`, style: { width: `${Math.min(barPercent, 100)}%` } }) }), _jsx("span", { className: "text-muted-foreground w-14 shrink-0 text-right text-xs", children: formatDuration(durationMs) })] }), timing.approvalWaitMs != null && timing.approvalWaitMs > 0 ? (_jsx(ApprovalWaitRow, { timing: timing, maxDurationMs: maxDurationMs })) : null] }));
|
|
145
187
|
}
|
|
146
188
|
function ApprovalWaitRow({ timing, maxDurationMs, }) {
|
|
147
189
|
const waitMs = timing.approvalWaitMs ?? 0;
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NotificationEvent } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
1
2
|
import type { FeatureNodeData } from '../../common/feature-node/index.js';
|
|
2
3
|
import type { PrdQuestionnaireData } from '../../common/prd-questionnaire/index.js';
|
|
3
4
|
import type { TechDecisionsReviewData } from '../../common/tech-decisions-review/index.js';
|
|
@@ -11,6 +12,8 @@ export interface FeatureDrawerTabsProps {
|
|
|
11
12
|
initialTab?: FeatureTabKey;
|
|
12
13
|
/** Tab key from URL path segment (e.g. /feature/[id]/activity → 'activity'). */
|
|
13
14
|
urlTab?: FeatureTabKey;
|
|
15
|
+
/** SSE events from the agent events provider, used to trigger tab data refresh. */
|
|
16
|
+
sseEvents?: readonly NotificationEvent[];
|
|
14
17
|
prdData?: PrdQuestionnaireData | null;
|
|
15
18
|
prdSelections?: Record<string, string>;
|
|
16
19
|
onPrdSelect?: (questionId: string, optionId: string) => void;
|
|
@@ -30,5 +33,5 @@ export interface FeatureDrawerTabsProps {
|
|
|
30
33
|
chatInput?: string;
|
|
31
34
|
onChatInputChange?: (value: string) => void;
|
|
32
35
|
}
|
|
33
|
-
export declare function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, }: FeatureDrawerTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export declare function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, sseEvents, }: FeatureDrawerTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
34
37
|
//# sourceMappingURL=feature-drawer-tabs.d.ts.map
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-drawer-tabs.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-drawer-tabs.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAQ9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAiD3F,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,eAAe,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,gFAAgF;IAChF,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,mFAAmF;IACnF,SAAS,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAGzC,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,QAAQ,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,WAAW,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAGjD,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;IAGzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAgB,iBAAiB,CAAC,EAChC,WAAW,EACX,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,SAAS,GACV,EAAE,sBAAsB,2CAwTxB"}
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js
CHANGED
|
@@ -64,7 +64,7 @@ const TAB_FETCHERS = {
|
|
|
64
64
|
activity: fetchActivity,
|
|
65
65
|
plan: fetchPlan,
|
|
66
66
|
};
|
|
67
|
-
export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, }) {
|
|
67
|
+
export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, sseEvents, }) {
|
|
68
68
|
const pathname = usePathname();
|
|
69
69
|
const featureLogs = useFeatureLogs(featureId);
|
|
70
70
|
const visibleTabs = useMemo(() => computeVisibleTabs(featureNode), [featureNode]);
|
|
@@ -159,6 +159,7 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
|
|
|
159
159
|
}, [featureId]);
|
|
160
160
|
// When initialTab changes (e.g. SSE updates lifecycle), switch to it if visible
|
|
161
161
|
// and sync the URL with replaceState so the address bar reflects the new tab.
|
|
162
|
+
// Also trigger lazy fetch for the new tab so data is ready immediately.
|
|
162
163
|
const prevInitialTabRef = useRef(initialTab);
|
|
163
164
|
useEffect(() => {
|
|
164
165
|
if (prevInitialTabRef.current !== initialTab &&
|
|
@@ -166,13 +167,17 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
|
|
|
166
167
|
visibleTabs.includes(initialTab)) {
|
|
167
168
|
prevInitialTabRef.current = initialTab;
|
|
168
169
|
setActiveTab(initialTab);
|
|
170
|
+
// Trigger lazy fetch for the new tab if it's a lazy-loaded tab
|
|
171
|
+
if (initialTab === 'activity' || initialTab === 'plan') {
|
|
172
|
+
fetchTab(initialTab);
|
|
173
|
+
}
|
|
169
174
|
// Sync URL to match the new tab
|
|
170
175
|
const targetUrl = initialTab === 'overview' ? basePath : `${basePath}/${initialTab}`;
|
|
171
176
|
if (targetUrl !== window.location.pathname) {
|
|
172
177
|
window.history.replaceState(null, '', targetUrl);
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
|
-
}, [initialTab, visibleTabs, basePath]);
|
|
180
|
+
}, [initialTab, visibleTabs, basePath, fetchTab]);
|
|
176
181
|
// If the active tab becomes invisible (lifecycle changed), fall back to overview
|
|
177
182
|
// and sync the URL accordingly.
|
|
178
183
|
useEffect(() => {
|
|
@@ -183,19 +188,34 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
|
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
190
|
}, [visibleTabs, activeTab, basePath]);
|
|
186
|
-
// SSE refresh: re-fetch
|
|
187
|
-
|
|
191
|
+
// SSE refresh: re-fetch lazy tab data when relevant SSE events arrive
|
|
192
|
+
// for this feature (e.g. PhaseCompleted, AgentStarted, AgentCompleted).
|
|
193
|
+
// Always refresh 'activity' data (even if not the active tab) so switching
|
|
194
|
+
// to the activity tab shows up-to-date timings without a loading flash.
|
|
188
195
|
const activeTabRef = useRef(activeTab);
|
|
189
196
|
activeTabRef.current = activeTab;
|
|
197
|
+
const sseProcessedRef = useRef(0);
|
|
190
198
|
useEffect(() => {
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
if (!sseEvents || sseEvents.length === 0)
|
|
200
|
+
return;
|
|
201
|
+
// Clamp cursor if events were pruned
|
|
202
|
+
if (sseProcessedRef.current > sseEvents.length) {
|
|
203
|
+
sseProcessedRef.current = 0;
|
|
204
|
+
}
|
|
205
|
+
if (sseEvents.length <= sseProcessedRef.current)
|
|
206
|
+
return;
|
|
207
|
+
const newEvents = sseEvents.slice(sseProcessedRef.current);
|
|
208
|
+
sseProcessedRef.current = sseEvents.length;
|
|
209
|
+
const hasRelevantEvent = newEvents.some((e) => e.featureId === featureId);
|
|
210
|
+
if (!hasRelevantEvent)
|
|
211
|
+
return;
|
|
212
|
+
// Always refresh activity data so it stays current for when the user switches tabs
|
|
213
|
+
refreshTab('activity');
|
|
214
|
+
const current = activeTabRef.current;
|
|
215
|
+
if (current === 'plan') {
|
|
216
|
+
refreshTab(current);
|
|
197
217
|
}
|
|
198
|
-
}, [
|
|
218
|
+
}, [sseEvents, featureId, refreshTab]);
|
|
199
219
|
const handleTabChange = useCallback((value) => {
|
|
200
220
|
const tab = value;
|
|
201
221
|
isUserInteraction.current = true;
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAOxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
4
|
import { AlertTriangle, ExternalLink, GitBranch, GitCommitHorizontal, Zap } from 'lucide-react';
|
|
4
5
|
import { InlineAttachments } from '../../common/inline-attachments/index.js';
|
|
5
6
|
import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
@@ -10,6 +11,7 @@ import { CiStatusBadge } from '../../common/ci-status-badge/index.js';
|
|
|
10
11
|
import { CometSpinner } from '../../ui/comet-spinner.js';
|
|
11
12
|
import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/feature-node/index.js';
|
|
12
13
|
import { getAgentTypeIcon, agentTypeLabels, } from '../../common/feature-node/agent-type-icons.js';
|
|
14
|
+
import { formatDuration } from '../../../lib/format-duration.js';
|
|
13
15
|
export function OverviewTab({ data }) {
|
|
14
16
|
const isCompleted = data.lifecycle === 'maintain';
|
|
15
17
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-status", className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: lifecycleDisplayLabels[data.lifecycle] }), _jsx(FeatureStateBadge, { data: data }), !isCompleted && data.progress > 0 ? (_jsxs("div", { "data-testid": "feature-drawer-progress", className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-xs", children: [_jsx("span", { children: "Progress" }), _jsxs("span", { children: [data.progress, "%"] })] }), _jsx("div", { className: "bg-muted h-2 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', featureNodeStateConfig[data.state].progressClass), style: { width: `${data.progress}%` } }) })] })) : null, isCompleted && data.pr ? _jsx(FeaturePrInfo, { pr: data.pr }) : null] }), _jsx(FeatureInfo, { data: data }), !isCompleted && data.pr ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx("div", { className: "p-4", children: _jsx(FeaturePrInfo, { pr: data.pr }) })] })) : null, _jsx(FeatureDetails, { data: data })] }));
|
|
@@ -63,11 +65,42 @@ function FeaturePrInfo({ pr }) {
|
|
|
63
65
|
return (_jsx("div", { "data-testid": "feature-drawer-pr", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("a", { href: pr.url, target: "_blank", rel: "noopener noreferrer", className: "text-primary flex items-center gap-1.5 text-sm font-semibold underline underline-offset-2", children: ["PR #", pr.number, _jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }), _jsx(Badge, { className: prStatusStyles[pr.status], children: pr.status })] }), pr.ciStatus ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "CI Status" }), _jsx(CiStatusBadge, { status: pr.ciStatus })] })) : null, pr.mergeable === false ? (_jsxs("div", { "data-testid": "pr-merge-conflict", className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Merge Status" }), _jsxs(Badge, { className: "border-transparent bg-orange-50 text-orange-700 hover:bg-orange-50", children: [_jsx(AlertTriangle, { className: "mr-1 h-3.5 w-3.5" }), "Conflicts"] })] })) : null, pr.commitHash ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Commit" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(GitCommitHorizontal, { className: "text-muted-foreground h-3.5 w-3.5" }), _jsx("code", { className: "bg-muted text-foreground rounded-md px-1.5 py-0.5 font-mono text-[11px]", children: pr.commitHash.slice(0, 7) })] })] })) : null] }) }));
|
|
64
66
|
}
|
|
65
67
|
// ── Details section ──────────────────────────────────────────────────
|
|
68
|
+
/** Hook that returns a live-updating elapsed time string for running features. */
|
|
69
|
+
function useElapsedTime(startedAt) {
|
|
70
|
+
const [elapsed, setElapsed] = useState(null);
|
|
71
|
+
const intervalRef = useRef(null);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!startedAt) {
|
|
74
|
+
setElapsed(null);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Compute immediately
|
|
78
|
+
setElapsed(formatDuration(Math.max(0, Date.now() - startedAt)));
|
|
79
|
+
// Tick every second
|
|
80
|
+
intervalRef.current = setInterval(() => {
|
|
81
|
+
setElapsed(formatDuration(Math.max(0, Date.now() - startedAt)));
|
|
82
|
+
}, 1000);
|
|
83
|
+
return () => {
|
|
84
|
+
if (intervalRef.current) {
|
|
85
|
+
clearInterval(intervalRef.current);
|
|
86
|
+
intervalRef.current = null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}, [startedAt]);
|
|
90
|
+
return elapsed;
|
|
91
|
+
}
|
|
66
92
|
function FeatureDetails({ data }) {
|
|
67
|
-
const
|
|
93
|
+
const isRunning = data.state === 'running' || data.state === 'action-required';
|
|
94
|
+
const elapsedTime = useElapsedTime(isRunning ? data.startedAt : undefined);
|
|
95
|
+
const hasAnyDetail = data.fastMode ??
|
|
96
|
+
data.agentType ??
|
|
97
|
+
data.runtime ??
|
|
98
|
+
elapsedTime ??
|
|
99
|
+
data.blockedBy ??
|
|
100
|
+
data.errorMessage;
|
|
68
101
|
if (!hasAnyDetail)
|
|
69
102
|
return null;
|
|
70
|
-
return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.fastMode ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Mode" }), _jsxs("span", { className: "flex items-center gap-2 text-sm", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-amber-500" }), "Fast Mode"] })] })) : null, data.agentType ? _jsx(AgentDetailRow, { agentType: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] })] }));
|
|
103
|
+
return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.fastMode ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Mode" }), _jsxs("span", { className: "flex items-center gap-2 text-sm", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-amber-500" }), "Fast Mode"] })] })) : null, data.agentType ? _jsx(AgentDetailRow, { agentType: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, !data.runtime && elapsedTime ? (_jsx(DetailRow, { label: "Running for", value: elapsedTime })) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] })] }));
|
|
71
104
|
}
|
|
72
105
|
function AgentDetailRow({ agentType }) {
|
|
73
106
|
const Icon = getAgentTypeIcon(agentType);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-badge.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/version-badge/version-badge.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"version-badge.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/version-badge/version-badge.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EACN,UAAU,EACV,KAAa,EACb,WAA2B,EAC3B,WAAW,EACX,YAAY,GACb,EAAE,iBAAiB,2CA2EnB"}
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
|
|
4
4
|
import { useNpmVersionCheck } from '../../../hooks/use-npm-version-check.js';
|
|
5
|
+
import { useCliUpgrade } from '../../../hooks/use-cli-upgrade.js';
|
|
5
6
|
export function VersionBadge({ version, branch, commitHash, isDev = false, packageName = '@shepai/cli', description, instancePath, }) {
|
|
6
7
|
const shortHash = commitHash?.slice(0, 7);
|
|
7
8
|
const { latest, updateAvailable } = useNpmVersionCheck(version);
|
|
9
|
+
const { status: upgradeStatus, startUpgrade } = useCliUpgrade();
|
|
8
10
|
// In dev mode show "1.92.2-dev", in production show "v1.92.2"
|
|
9
11
|
const displayVersion = isDev ? `${version}-dev` : `v${version}`;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
+
const isUpgrading = upgradeStatus === 'upgrading';
|
|
13
|
+
const didUpgrade = upgradeStatus === 'upgraded';
|
|
14
|
+
const upgradeError = upgradeStatus === 'error';
|
|
15
|
+
return (_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("span", { className: "text-muted-foreground/80 hover:text-muted-foreground relative cursor-default text-[11px] leading-tight transition-colors", "data-testid": "version-label", children: [displayVersion, updateAvailable && !didUpgrade ? (_jsx("span", { className: "absolute -top-0.5 -right-1.5 size-1.5 rounded-full bg-emerald-400", "data-testid": "update-dot" })) : null] }) }), _jsxs(TooltipContent, { side: "right", className: "max-w-[280px] space-y-1 p-3 text-left", children: [_jsx("div", { className: "mb-1.5 text-xs font-semibold", children: packageName }), description ? (_jsx("div", { className: "text-[10px] leading-snug opacity-70", children: description })) : null, _jsxs("div", { className: "border-t border-white/10 pt-1.5", children: [_jsx(Row, { label: "Version", value: displayVersion }), isDev && branch ? _jsx(Row, { label: "Branch", value: branch }) : null, isDev && shortHash ? _jsx(Row, { label: "Commit", value: shortHash, mono: true }) : null, isDev && instancePath ? _jsx(Row, { label: "Path", value: instancePath, mono: true }) : null, latest ? (_jsx(Row, { label: "Latest", value: `v${latest}`, highlight: updateAvailable })) : null] }), didUpgrade ? (_jsx("div", { className: "border-t border-white/10 pt-1.5", children: _jsx("span", { className: "text-[10px] font-medium text-emerald-400", "data-testid": "upgrade-success", children: "Upgraded successfully \u2014 restart to apply" }) })) : upgradeError ? (_jsx("div", { className: "border-t border-white/10 pt-1.5", children: _jsx("span", { className: "text-[10px] font-medium text-red-400", "data-testid": "upgrade-error", children: "Upgrade failed" }) })) : updateAvailable ? (_jsx("div", { className: "border-t border-white/10 pt-1.5", children: _jsx("button", { type: "button", onClick: startUpgrade, disabled: isUpgrading, className: "inline-flex items-center gap-1 text-[10px] font-medium text-emerald-400 transition-colors hover:text-emerald-300 disabled:opacity-50", "data-testid": "upgrade-button", children: isUpgrading ? 'Upgrading...' : `Upgrade to v${latest}` }) })) : null] })] }) }));
|
|
12
16
|
}
|
|
13
17
|
function Row({ label, value, highlight, mono, }) {
|
|
14
18
|
return (_jsxs("div", { className: "flex items-center justify-between gap-4 text-[10px]", children: [_jsx("span", { className: "opacity-60", children: label }), _jsx("span", { className: [
|
package/dist/src/presentation/web/components/common/version-badge/version-badge.stories.d.ts
CHANGED
|
@@ -11,9 +11,9 @@ export declare const Development: Story;
|
|
|
11
11
|
export declare const LongBranch: Story;
|
|
12
12
|
/** Minimal info — no optional fields. */
|
|
13
13
|
export declare const Minimal: Story;
|
|
14
|
-
/** Update available — green dot appears, tooltip shows "Upgrade to v2.0.0"
|
|
14
|
+
/** Update available — green dot appears, tooltip shows "Upgrade to v2.0.0" button. */
|
|
15
15
|
export declare const UpdateAvailable: Story;
|
|
16
|
-
/** Update available in dev mode — green dot + upgrade
|
|
16
|
+
/** Update available in dev mode — green dot + upgrade button alongside dev info. */
|
|
17
17
|
export declare const UpdateAvailableDev: Story;
|
|
18
18
|
/** Fetch error — no update info shown, badge behaves normally. */
|
|
19
19
|
export declare const FetchError: Story;
|