diff --git a/.dockerignore b/.dockerignore index 5bdda4f08..942fcb6f4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,6 +12,11 @@ apps/.nx # Build outputs target +dist +apps/dist +coverage +apps/coverage +*.log # Git .git diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 98f03c9df..52cc7580e 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,5 +1,6 @@ -FROM node:24-slim AS boxlite -# build: 2026-04-28-v2 +# syntax=docker/dockerfile:1 + +FROM node:24-slim AS base ENV CI=true RUN apt-get update && apt-get install -y --no-install-recommends bash curl && \ @@ -8,12 +9,18 @@ RUN npm install -g corepack && corepack enable WORKDIR /boxlite/apps +FROM base AS deps COPY apps/package.json apps/yarn.lock apps/.yarnrc.yml ./ RUN yarn install --immutable +FROM base AS runtime-deps +ENV NODE_ENV=production +COPY apps/package.json apps/yarn.lock apps/.yarnrc.yml ./ +RUN yarn workspaces focus --all --production + +FROM deps AS build COPY apps/nx.json apps/tsconfig.base.json apps/NOTICE ./ -ARG CACHE_BUST=1 ENV NX_DAEMON=false ENV NX_SKIP_NX_CACHE=true @@ -26,13 +33,31 @@ COPY apps/libs/analytics-api-client/ libs/analytics-api-client/ COPY apps/libs/toolbox-api-client/ libs/toolbox-api-client/ COPY apps/libs/sdk-typescript/ libs/sdk-typescript/ -RUN echo "$CACHE_BUST" && yarn nx build api --configuration=production --nxBail=true && node --check dist/apps/api/main.js +RUN yarn nx build api --configuration=production --nxBail=true --output-style=stream && node --check dist/apps/api/main.js RUN VITE_BASE_API_URL=%BOXLITE_BASE_API_URL% yarn nx build dashboard --configuration=production --nxBail=true --output-style=stream +FROM node:24-slim AS boxlite +ENV CI=true +ENV NODE_ENV=production + +RUN apt-get update && apt-get install -y --no-install-recommends bash curl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /boxlite/apps + +COPY --from=runtime-deps --chown=node:node /boxlite/apps/package.json ./package.json +COPY --from=runtime-deps --chown=node:node /boxlite/apps/node_modules ./node_modules +COPY --from=build --chown=node:node /boxlite/apps/NOTICE ./NOTICE +COPY --from=build --chown=node:node /boxlite/apps/dist/apps/api ./dist/apps/api +COPY --from=build --chown=node:node /boxlite/apps/dist/apps/dashboard ./dist/apps/dashboard + ARG VERSION=0.0.1 ENV VERSION=${VERSION} +EXPOSE 3000 HEALTHCHECK CMD [ "curl", "-f", "http://localhost:3000/api/config" ] +USER node + ENTRYPOINT ["node", "dist/apps/api/main.js"] diff --git a/apps/api/project.json b/apps/api/project.json index 7d1bf8351..41e65a60e 100644 --- a/apps/api/project.json +++ b/apps/api/project.json @@ -1,7 +1,7 @@ { "name": "api", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/api/src", + "sourceRoot": "api/src", "projectType": "application", "tags": [], "targets": { @@ -10,16 +10,16 @@ "options": { "outputPath": "dist/apps/api", "deleteOutputPath": false, - "main": "apps/api/src/main.ts", - "tsConfig": "apps/api/tsconfig.app.json", - "generatePackageJson": true, + "main": "api/src/main.ts", + "tsConfig": "api/tsconfig.app.json", + "generatePackageJson": false, "target": "node", "compiler": "tsc", "sourceMap": true, - "webpackConfig": "apps/api/webpack.config.js", + "webpackConfig": "api/webpack.config.js", "assets": [ { - "input": "apps/api/src/assets", + "input": "api/src/assets", "glob": "**/*", "output": "./assets/" } @@ -27,7 +27,6 @@ }, "configurations": { "development": { - "generatePackageJson": false, "skipTypeChecking": true }, "production": { @@ -95,14 +94,10 @@ "check-version-env": {}, "docker": { "options": { - "target": "boxlite" - }, - "dependsOn": [ - { - "target": "build-amd64", - "projects": "runner" - } - ] + "target": "boxlite", + "context": "..", + "file": "apps/api/Dockerfile" + } }, "push-manifest": {}, "lint": { diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index c1e2dd4e8..3773c2597 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "../tsconfig.base.json", "files": [], "include": [], "references": [ diff --git a/apps/daemon/project.json b/apps/daemon/project.json index 25b393f2f..b839b86d4 100644 --- a/apps/daemon/project.json +++ b/apps/daemon/project.json @@ -2,7 +2,7 @@ "name": "daemon", "$schema": "../../node_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "apps/daemon", + "sourceRoot": "daemon", "tags": [], "targets": { "prepare": { diff --git a/apps/dashboard/project.json b/apps/dashboard/project.json index 1f22c19cb..9266c12e8 100644 --- a/apps/dashboard/project.json +++ b/apps/dashboard/project.json @@ -1,7 +1,7 @@ { "name": "dashboard", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/dashboard/src", + "sourceRoot": "dashboard/src", "projectType": "application", "tags": [], "targets": { diff --git a/apps/dashboard/src/components/Box/CreateBoxSheet.tsx b/apps/dashboard/src/components/Box/CreateBoxSheet.tsx index b28baadaf..2cb3d9d99 100644 --- a/apps/dashboard/src/components/Box/CreateBoxSheet.tsx +++ b/apps/dashboard/src/components/Box/CreateBoxSheet.tsx @@ -148,6 +148,13 @@ export const CreateBoxSheet = ({ let boxId: string | undefined = undefined try { + const resources = { + cpu: parseOptionalInteger(value.cpu), + memory: parseOptionalInteger(value.memory), + disk: parseOptionalInteger(value.disk), + } + const hasResourceOverrides = Object.values(resources).some((resource) => resource !== undefined) + // TODO(image-rewrite): the image/template picker was removed with the image/template // subsystem; box creation no longer selects an image. Rebuild image selection here once // the new model lands. @@ -157,9 +164,7 @@ export const CreateBoxSheet = ({ networkBlockAll: false, autoStopInterval: parseOptionalInteger(value.autoStopInterval), autoDeleteInterval: parseOptionalInteger(value.autoDeleteInterval), - cpu: parseOptionalInteger(value.cpu), - memory: parseOptionalInteger(value.memory), - disk: parseOptionalInteger(value.disk), + ...(hasResourceOverrides ? { resources } : {}), }) boxId = getBoxRouteId(box) onCreated?.(box) diff --git a/apps/dashboard/src/components/BoxTable/BoxState.tsx b/apps/dashboard/src/components/BoxTable/BoxState.tsx index 8b2e495a4..f04fe2d15 100644 --- a/apps/dashboard/src/components/BoxTable/BoxState.tsx +++ b/apps/dashboard/src/components/BoxTable/BoxState.tsx @@ -22,7 +22,7 @@ export function BoxState({ state, errorReason, recoverable, className }: BoxStat const stateIcon = recoverable ? STATE_ICONS['RECOVERY'] : STATE_ICONS[state] || STATE_ICONS[BoxStateType.UNKNOWN] const label = getStateLabel(state) - if (state === BoxStateType.ERROR || state === BoxStateType.BUILD_FAILED) { + if (state === BoxStateType.ERROR) { const errorColor = recoverable ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400' const errorContent = ( diff --git a/apps/dashboard/src/components/BoxTable/constants.ts b/apps/dashboard/src/components/BoxTable/constants.ts index 95cc0d979..e7a895517 100644 --- a/apps/dashboard/src/components/BoxTable/constants.ts +++ b/apps/dashboard/src/components/BoxTable/constants.ts @@ -12,9 +12,6 @@ const STATE_LABEL_MAPPING: Record = { [BoxState.STARTED]: 'Running', [BoxState.STOPPED]: 'Stopped', [BoxState.ERROR]: 'Error', - [BoxState.BUILD_FAILED]: 'Build Failed', - [BoxState.BUILDING_ARTIFACT]: 'Preparing runtime', - [BoxState.PENDING_BUILD]: 'Pending Build', [BoxState.RESTORING]: 'Restoring', [BoxState.ARCHIVED]: 'Archived', [BoxState.CREATING]: 'Creating', @@ -23,6 +20,7 @@ const STATE_LABEL_MAPPING: Record = { [BoxState.DESTROYING]: 'Deleting', [BoxState.DESTROYED]: 'Deleted', [BoxState.UNKNOWN]: 'Unknown', + [BoxState.UNKNOWN_DEFAULT_OPEN_API]: 'Unknown', [BoxState.ARCHIVING]: 'Archiving', [BoxState.RESIZING]: 'Resizing', } @@ -35,7 +33,6 @@ export const STATUSES: FacetedFilterOption[] = [ }, { label: getStateLabel(BoxState.STOPPED), value: BoxState.STOPPED, icon: Circle }, { label: getStateLabel(BoxState.ERROR), value: BoxState.ERROR, icon: AlertTriangle }, - { label: getStateLabel(BoxState.BUILD_FAILED), value: BoxState.BUILD_FAILED, icon: AlertTriangle }, { label: getStateLabel(BoxState.STARTING), value: BoxState.STARTING, icon: Timer }, { label: getStateLabel(BoxState.STOPPING), value: BoxState.STOPPING, icon: Timer }, { label: getStateLabel(BoxState.DESTROYING), value: BoxState.DESTROYING, icon: Timer }, diff --git a/apps/dashboard/src/components/BoxTable/state-icons.tsx b/apps/dashboard/src/components/BoxTable/state-icons.tsx index 1e4d6b0c4..bf2af54f9 100644 --- a/apps/dashboard/src/components/BoxTable/state-icons.tsx +++ b/apps/dashboard/src/components/BoxTable/state-icons.tsx @@ -29,12 +29,10 @@ export const STATE_ICONS: Record = { [BoxState.DESTROYING]: , [BoxState.DESTROYED]: , [BoxState.ERROR]: , - [BoxState.BUILD_FAILED]: , - [BoxState.BUILDING_ARTIFACT]: , - [BoxState.PENDING_BUILD]: , [BoxState.ARCHIVED]: , [BoxState.ARCHIVING]: , [BoxState.RESTORING]: , [BoxState.RESIZING]: , + [BoxState.UNKNOWN_DEFAULT_OPEN_API]: , RECOVERY: , } diff --git a/apps/dashboard/src/components/RunnerDetailsSheet.tsx b/apps/dashboard/src/components/RunnerDetailsSheet.tsx index c0f2f5f08..18632ea8d 100644 --- a/apps/dashboard/src/components/RunnerDetailsSheet.tsx +++ b/apps/dashboard/src/components/RunnerDetailsSheet.tsx @@ -225,10 +225,6 @@ const RunnerDetailsSheet: React.FC = ({

Active Boxes

{runner.currentStartedBoxes ?? 0}

-
-

Images

-

{runner.currentArtifactCount ?? 0}

-
diff --git a/apps/dashboard/src/hooks/mutations/useDeleteBoxMutation.ts b/apps/dashboard/src/hooks/mutations/useDeleteBoxMutation.ts index 6a500da1b..d9324e807 100644 --- a/apps/dashboard/src/hooks/mutations/useDeleteBoxMutation.ts +++ b/apps/dashboard/src/hooks/mutations/useDeleteBoxMutation.ts @@ -8,19 +8,19 @@ import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' import { queryKeys } from '@/hooks/queries/queryKeys' import { useMutation, useQueryClient } from '@tanstack/react-query' -interface StartBoxVariables { +interface DeleteBoxVariables { boxId: string detailRef?: string } -export const useStartBoxMutation = () => { +export const useDeleteBoxMutation = () => { const { boxApi } = useApi() const { selectedOrganization } = useSelectedOrganization() const queryClient = useQueryClient() return useMutation({ - mutationFn: async ({ boxId }: StartBoxVariables) => { - await boxApi.startBox(boxId, selectedOrganization?.id) + mutationFn: async ({ boxId }: DeleteBoxVariables) => { + await boxApi.deleteBox(boxId, selectedOrganization?.id) }, onSuccess: (_, { boxId, detailRef }) => { queryClient.invalidateQueries({ diff --git a/apps/dashboard/src/hooks/mutations/useRecoverBoxMutation.ts b/apps/dashboard/src/hooks/mutations/useRecoverBoxMutation.ts index d9324e807..fb5e65e4a 100644 --- a/apps/dashboard/src/hooks/mutations/useRecoverBoxMutation.ts +++ b/apps/dashboard/src/hooks/mutations/useRecoverBoxMutation.ts @@ -8,19 +8,19 @@ import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' import { queryKeys } from '@/hooks/queries/queryKeys' import { useMutation, useQueryClient } from '@tanstack/react-query' -interface DeleteBoxVariables { +interface RecoverBoxVariables { boxId: string detailRef?: string } -export const useDeleteBoxMutation = () => { +export const useRecoverBoxMutation = () => { const { boxApi } = useApi() const { selectedOrganization } = useSelectedOrganization() const queryClient = useQueryClient() return useMutation({ - mutationFn: async ({ boxId }: DeleteBoxVariables) => { - await boxApi.deleteBox(boxId, selectedOrganization?.id) + mutationFn: async ({ boxId }: RecoverBoxVariables) => { + await boxApi.recoverBox(boxId, selectedOrganization?.id) }, onSuccess: (_, { boxId, detailRef }) => { queryClient.invalidateQueries({ diff --git a/apps/dashboard/src/hooks/mutations/useStartBoxMutation.ts b/apps/dashboard/src/hooks/mutations/useStartBoxMutation.ts new file mode 100644 index 000000000..6a500da1b --- /dev/null +++ b/apps/dashboard/src/hooks/mutations/useStartBoxMutation.ts @@ -0,0 +1,36 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { useApi } from '@/hooks/useApi' +import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' +import { queryKeys } from '@/hooks/queries/queryKeys' +import { useMutation, useQueryClient } from '@tanstack/react-query' + +interface StartBoxVariables { + boxId: string + detailRef?: string +} + +export const useStartBoxMutation = () => { + const { boxApi } = useApi() + const { selectedOrganization } = useSelectedOrganization() + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ boxId }: StartBoxVariables) => { + await boxApi.startBox(boxId, selectedOrganization?.id) + }, + onSuccess: (_, { boxId, detailRef }) => { + queryClient.invalidateQueries({ + queryKey: queryKeys.boxes.detail(selectedOrganization?.id ?? '', boxId), + }) + if (detailRef && detailRef !== boxId) { + queryClient.invalidateQueries({ + queryKey: queryKeys.boxes.detail(selectedOrganization?.id ?? '', detailRef), + }) + } + }, + }) +} diff --git a/apps/dashboard/src/hooks/mutations/useStopBoxMutation.ts b/apps/dashboard/src/hooks/mutations/useStopBoxMutation.ts index 06748080b..a346512ac 100644 --- a/apps/dashboard/src/hooks/mutations/useStopBoxMutation.ts +++ b/apps/dashboard/src/hooks/mutations/useStopBoxMutation.ts @@ -10,6 +10,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' interface StopBoxVariables { boxId: string + detailRef?: string } export const useStopBoxMutation = () => { @@ -21,10 +22,15 @@ export const useStopBoxMutation = () => { mutationFn: async ({ boxId }: StopBoxVariables) => { await boxApi.stopBox(boxId, selectedOrganization?.id) }, - onSuccess: (_, { boxId }) => { + onSuccess: (_, { boxId, detailRef }) => { queryClient.invalidateQueries({ queryKey: queryKeys.boxes.detail(selectedOrganization?.id ?? '', boxId), }) + if (detailRef && detailRef !== boxId) { + queryClient.invalidateQueries({ + queryKey: queryKeys.boxes.detail(selectedOrganization?.id ?? '', detailRef), + }) + } }, }) } diff --git a/apps/dashboard/src/hooks/useBoxWsSync.ts b/apps/dashboard/src/hooks/useBoxWsSync.ts index fb994c4a3..0a9c9ff5d 100644 --- a/apps/dashboard/src/hooks/useBoxWsSync.ts +++ b/apps/dashboard/src/hooks/useBoxWsSync.ts @@ -90,11 +90,8 @@ export function useBoxWsSync({ boxId, refetchOnCreate = false }: UseBoxWsSyncOpt let updatedState = data.newState - // error/build_failed with desiredState=DESTROYED should display as destroyed - if ( - data.box.desiredState === BoxDesiredState.DESTROYED && - (data.newState === BoxState.ERROR || data.newState === BoxState.BUILD_FAILED) - ) { + // error with desiredState=DESTROYED should display as destroyed + if (data.box.desiredState === BoxDesiredState.DESTROYED && data.newState === BoxState.ERROR) { updatedState = BoxState.DESTROYED } @@ -110,7 +107,7 @@ export function useBoxWsSync({ boxId, refetchOnCreate = false }: UseBoxWsSyncOpt if (!matchesActiveBox(data.box)) return if (data.newDesiredState !== BoxDesiredState.DESTROYED) return - if (data.box.state !== BoxState.ERROR && data.box.state !== BoxState.BUILD_FAILED) return + if (data.box.state !== BoxState.ERROR) return optimisticUpdate(data.box, BoxState.DESTROYED) invalidate() diff --git a/apps/dashboard/src/lib/utils/box.ts b/apps/dashboard/src/lib/utils/box.ts index 63cbd586d..8a10f5781 100644 --- a/apps/dashboard/src/lib/utils/box.ts +++ b/apps/dashboard/src/lib/utils/box.ts @@ -33,8 +33,7 @@ export function isTransitioning(box: Box): boolean { box.state === BoxState.STARTING || box.state === BoxState.STOPPING || box.state === BoxState.DESTROYING || - box.state === BoxState.RESTORING || - box.state === BoxState.BUILDING_ARTIFACT + box.state === BoxState.RESTORING ) } diff --git a/apps/dashboard/src/pages/Boxes.tsx b/apps/dashboard/src/pages/Boxes.tsx index 7bf5ab643..17fcbcca1 100644 --- a/apps/dashboard/src/pages/Boxes.tsx +++ b/apps/dashboard/src/pages/Boxes.tsx @@ -320,11 +320,8 @@ const Boxes: React.FC = () => { let updatedState = data.newState - // error,build_failed | destroyed should be displayed as destroyed in the UI - if ( - data.box.desiredState === BoxDesiredState.DESTROYED && - (data.newState === BoxState.ERROR || data.newState === BoxState.BUILD_FAILED) - ) { + // error | destroyed should be displayed as destroyed in the UI + if (data.box.desiredState === BoxDesiredState.DESTROYED && data.newState === BoxState.ERROR) { updatedState = BoxState.DESTROYED } @@ -342,13 +339,13 @@ const Boxes: React.FC = () => { oldDesiredState: BoxDesiredState newDesiredState: BoxDesiredState }) => { - // error,build_failed | destroyed should be displayed as destroyed in the UI + // error | destroyed should be displayed as destroyed in the UI if (data.newDesiredState !== BoxDesiredState.DESTROYED) { return } - if (data.box.state !== BoxState.ERROR && data.box.state !== BoxState.BUILD_FAILED) { + if (data.box.state !== BoxState.ERROR) { return } diff --git a/apps/infra/sst.config.ts b/apps/infra/sst.config.ts index bc0ea87b1..4d5f9b1b6 100644 --- a/apps/infra/sst.config.ts +++ b/apps/infra/sst.config.ts @@ -258,8 +258,6 @@ export default $config({ image: { context: '../..', dockerfile: 'apps/api/Dockerfile', - cache: false, - args: { CACHE_BUST: Date.now().toString() }, }, loadBalancer: { domain: serviceDomain('api'), diff --git a/apps/otel-collector/project.json b/apps/otel-collector/project.json index 34f51f9c4..f7102239a 100644 --- a/apps/otel-collector/project.json +++ b/apps/otel-collector/project.json @@ -2,7 +2,7 @@ "name": "otel-collector", "$schema": "../../runner_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "apps/otel-collector", + "sourceRoot": "otel-collector", "tags": [], "targets": { "build": { diff --git a/apps/package.json b/apps/package.json index 4c8befad1..a3586e66c 100644 --- a/apps/package.json +++ b/apps/package.json @@ -4,7 +4,7 @@ "license": "SEE LICENSE IN NOTICE", "private": true, "scripts": { - "postinstall": "is-ci || husky", + "postinstall": "node -e \"process.exit(process.env.CI === 'true' || process.env.HUSKY === '0' ? 0 : 1)\" || husky", "start": "node scripts/start-dashboard.mjs", "clickstack:cloud": "node scripts/clickstack-cloud.mjs", "start:dev": "node scripts/start-dashboard.mjs --target=dev", @@ -210,6 +210,7 @@ "tailwind-scrollbar": "^4.0.2", "tailwindcss-animate": "^1.0.7", "tar": "^7.5.16", + "tslib": "^2.8.1", "typeorm": "0.3.28", "usehooks-ts": "^3.1.1", "uuid": "^14.0.0", @@ -282,7 +283,6 @@ "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "5.0.0", "husky": "^9.1.7", - "is-ci": "^4.1.0", "jest": "30.0.5", "jest-environment-node": "^29.7.0", "jest-util": "30.0.5", @@ -303,7 +303,6 @@ "tailwindcss": "3.4.3", "ts-jest": "29.4.1", "ts-node": "10.9.1", - "tslib": "^2.8.1", "tsx": "^4.20.5", "typedoc": "~0.27.7", "typedoc-plugin-markdown": "~4.4.2", diff --git a/apps/proxy/project.json b/apps/proxy/project.json index 748ca94fb..edfe7e334 100644 --- a/apps/proxy/project.json +++ b/apps/proxy/project.json @@ -2,7 +2,7 @@ "name": "proxy", "$schema": "../../runner_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "apps/proxy", + "sourceRoot": "proxy", "tags": [], "targets": { "build": { diff --git a/apps/runner/project.json b/apps/runner/project.json index 926756abe..683e5ac3f 100644 --- a/apps/runner/project.json +++ b/apps/runner/project.json @@ -2,7 +2,7 @@ "name": "runner", "$schema": "../../runner_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "apps/runner", + "sourceRoot": "runner", "tags": [], "targets": { "copy-daemon-bin": { diff --git a/apps/ssh-gateway/project.json b/apps/ssh-gateway/project.json index aa753ed0c..08f1c4564 100644 --- a/apps/ssh-gateway/project.json +++ b/apps/ssh-gateway/project.json @@ -2,7 +2,7 @@ "name": "ssh-gateway", "$schema": "../../runner_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "apps/ssh-gateway", + "sourceRoot": "ssh-gateway", "tags": [], "targets": { "build": { diff --git a/apps/yarn.lock b/apps/yarn.lock index dd6f0301d..92581dc22 100644 --- a/apps/yarn.lock +++ b/apps/yarn.lock @@ -16543,7 +16543,6 @@ __metadata: gtx-cli: "npm:^2.6.24" husky: "npm:^9.1.7" ioredis: "npm:^5.5.0" - is-ci: "npm:^4.1.0" isomorphic-ws: "npm:^5.0.0" jest: "npm:30.0.5" jest-environment-node: "npm:^29.7.0" @@ -17287,7 +17286,7 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.0.0, ci-info@npm:^4.1.0, ci-info@npm:^4.2.0, ci-info@npm:^4.4.0": +"ci-info@npm:^4.0.0, ci-info@npm:^4.2.0, ci-info@npm:^4.4.0": version: 4.4.0 resolution: "ci-info@npm:4.4.0" checksum: 10c0/44156201545b8dde01aa8a09ee2fe9fc7a73b1bef9adbd4606c9f61c8caeeb73fb7a575c88b0443f7b4edb5ee45debaa59ed54ba5f99698339393ca01349eb3a @@ -24018,17 +24017,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^4.1.0": - version: 4.1.0 - resolution: "is-ci@npm:4.1.0" - dependencies: - ci-info: "npm:^4.1.0" - bin: - is-ci: bin.js - checksum: 10c0/c58e733d21f8d7e6ba3d70686124004576b369b4a02a37c4783159ed5a668b3a9e4b359efa955e4a675bd021eca7305010c45a1e646bf77cce14b08808eda88d - languageName: node - linkType: hard - "is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.1, is-core-module@npm:^2.16.2": version: 2.16.2 resolution: "is-core-module@npm:2.16.2"