mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 08:15:56 +01:00
Merge branch 'main' into dev/mjbvz/apparent-trout
This commit is contained in:
837
build/win32/Cargo.lock
generated
837
build/win32/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "inno_updater"
|
||||
version = "0.18.2"
|
||||
version = "0.19.0"
|
||||
authors = ["Microsoft <monacotools@microsoft.com>"]
|
||||
build = "build.rs"
|
||||
|
||||
@@ -9,7 +9,7 @@ byteorder = "1.4.3"
|
||||
crc = "3.0.1"
|
||||
slog = "2.7.0"
|
||||
slog-async = "2.7.0"
|
||||
slog-term = "2.9.1"
|
||||
slog-term = "2.9.2"
|
||||
tempfile = "3.5.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows-sys]
|
||||
|
||||
@@ -73,6 +73,11 @@ export class NodeExtHostMpcService extends ExtHostMcpService {
|
||||
}
|
||||
}
|
||||
for (const [key, value] of Object.entries(launch.env)) {
|
||||
// For PATH, we want to append to the existing PATH instead of overwriting it.
|
||||
if (key.toUpperCase() === 'PATH' && value !== null) {
|
||||
env[key] = env[key] ? `${env[key]}${path.delimiter}${String(value)}` : String(value);
|
||||
continue;
|
||||
}
|
||||
env[key] = value === null ? undefined : String(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export const OpenBrowserToolData: IToolData = {
|
||||
properties: {
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'The URL to open in the browser.'
|
||||
description: 'The full URL to open in the browser.'
|
||||
},
|
||||
},
|
||||
required: ['url'],
|
||||
@@ -60,6 +60,9 @@ export class OpenBrowserTool implements IToolImpl {
|
||||
if (!params.url) {
|
||||
return errorResult('The "url" parameter is required.');
|
||||
}
|
||||
if (!URL.parse(params.url)) {
|
||||
return errorResult('You must provide a complete, valid URL.');
|
||||
}
|
||||
|
||||
const { pageId, summary } = await this.playwrightService.openPage(params.url);
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ export class OpenBrowserToolNonAgentic implements IToolImpl {
|
||||
if (!params.url) {
|
||||
return errorResult('The "url" parameter is required.');
|
||||
}
|
||||
if (!URL.parse(params.url)) {
|
||||
return errorResult('You must provide a complete, valid URL.');
|
||||
}
|
||||
|
||||
logBrowserOpen(this.telemetryService, 'chatTool');
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import { IEditorService } from '../../../../services/editor/common/editorService
|
||||
import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js';
|
||||
import { IMouseEvent } from '../../../../../base/browser/mouseEvent.js';
|
||||
import { IChatWidget } from '../chat.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
|
||||
|
||||
export interface IAgentSessionsControlOptions extends IAgentSessionsSorterOptions {
|
||||
readonly overrideStyles: IStyleOverride<IListStyles>;
|
||||
@@ -78,6 +79,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
|
||||
private sessionsList: WorkbenchCompressibleAsyncDataTree<IAgentSessionsModel, AgentSessionListItem, FuzzyScore> | undefined;
|
||||
private sessionsListFindIsOpen = false;
|
||||
private _isProgrammaticCollapseChange = false;
|
||||
|
||||
private readonly updateSessionsListThrottler = this._register(new Throttler());
|
||||
|
||||
@@ -103,6 +105,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
@IAgentSessionsService private readonly agentSessionsService: IAgentSessionsService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -173,9 +176,49 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
}));
|
||||
}
|
||||
|
||||
private static readonly SECTION_COLLAPSE_STATE_KEY = 'agentSessions.sectionCollapseState';
|
||||
|
||||
private getSavedCollapseState(section: AgentSessionSection): boolean | undefined {
|
||||
const raw = this.storageService.get(AgentSessionsControl.SECTION_COLLAPSE_STATE_KEY, StorageScope.PROFILE);
|
||||
if (raw) {
|
||||
try {
|
||||
const state: Record<string, boolean> = JSON.parse(raw);
|
||||
if (typeof state[section] === 'boolean') {
|
||||
return state[section];
|
||||
}
|
||||
} catch {
|
||||
// ignore corrupt data
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private saveSectionCollapseState(section: AgentSessionSection, collapsed: boolean): void {
|
||||
let state: Record<string, boolean> = {};
|
||||
const raw = this.storageService.get(AgentSessionsControl.SECTION_COLLAPSE_STATE_KEY, StorageScope.PROFILE);
|
||||
if (raw) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
||||
state = parsed;
|
||||
}
|
||||
} catch {
|
||||
// ignore corrupt data
|
||||
}
|
||||
}
|
||||
state[section] = collapsed;
|
||||
this.storageService.store(AgentSessionsControl.SECTION_COLLAPSE_STATE_KEY, JSON.stringify(state), StorageScope.PROFILE, StorageTarget.USER);
|
||||
}
|
||||
|
||||
private createList(container: HTMLElement): void {
|
||||
const collapseByDefault = (element: unknown) => {
|
||||
if (isAgentSessionSection(element)) {
|
||||
// Check for persisted user preference first
|
||||
const saved = this.getSavedCollapseState(element.section);
|
||||
if (saved !== undefined) {
|
||||
return saved;
|
||||
}
|
||||
|
||||
if (element.section === AgentSessionSection.More && !this.options.filter.getExcludes().read) {
|
||||
return true; // More section is always collapsed unless only showing unread
|
||||
}
|
||||
@@ -285,6 +328,16 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
|
||||
this.updateSectionCollapseStates();
|
||||
}));
|
||||
|
||||
this._register(list.onDidChangeCollapseState(e => {
|
||||
if (this._isProgrammaticCollapseChange) {
|
||||
return;
|
||||
}
|
||||
const element = e.node.element?.element;
|
||||
if (element && isAgentSessionSection(element)) {
|
||||
this.saveSectionCollapseState(element.section, e.node.collapsed);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private updateEmpty(isEmpty: boolean): void {
|
||||
@@ -396,6 +449,19 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
|
||||
return;
|
||||
}
|
||||
|
||||
this._isProgrammaticCollapseChange = true;
|
||||
try {
|
||||
this._updateSectionCollapseStatesCore();
|
||||
} finally {
|
||||
this._isProgrammaticCollapseChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _updateSectionCollapseStatesCore(): void {
|
||||
if (!this.sessionsList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.agentSessionsService.model;
|
||||
for (const child of this.sessionsList.getNode(model).children) {
|
||||
if (!isAgentSessionSection(child.element)) {
|
||||
|
||||
@@ -331,6 +331,17 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear stale size constraints first so this step can shrink after
|
||||
// navigating from a taller question.
|
||||
if (scrollableNode.style.height !== '' || scrollableNode.style.maxHeight !== '') {
|
||||
scrollableNode.style.height = '';
|
||||
scrollableNode.style.maxHeight = '';
|
||||
}
|
||||
if (scrollableContent.style.height !== '' || scrollableContent.style.maxHeight !== '') {
|
||||
scrollableContent.style.height = '';
|
||||
scrollableContent.style.maxHeight = '';
|
||||
}
|
||||
|
||||
// Use the flex-resolved container height (constrained by CSS max-height)
|
||||
// instead of window.innerHeight, so the scroll viewport tracks actual chat space.
|
||||
const maxContainerHeight = this._questionContainer.clientHeight;
|
||||
@@ -345,12 +356,19 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
|
||||
.reduce((sum, child) => sum + (child as HTMLElement).offsetHeight, 0);
|
||||
|
||||
const availableScrollableHeight = Math.floor(maxContainerHeight - contentVerticalPadding - nonScrollableContentHeight);
|
||||
const constrainedScrollableHeight = Math.max(0, availableScrollableHeight);
|
||||
|
||||
const contentScrollableHeight = scrollableContent.scrollHeight;
|
||||
const constrainedScrollableHeight = Math.max(0, Math.min(availableScrollableHeight, contentScrollableHeight));
|
||||
const constrainedScrollableHeightPx = `${constrainedScrollableHeight}px`;
|
||||
|
||||
// Constrain wrapper + content so no stale flex sizing survives between steps.
|
||||
if (scrollableNode.style.height !== constrainedScrollableHeightPx || scrollableNode.style.maxHeight !== constrainedScrollableHeightPx) {
|
||||
scrollableNode.style.height = constrainedScrollableHeightPx;
|
||||
scrollableNode.style.maxHeight = constrainedScrollableHeightPx;
|
||||
}
|
||||
|
||||
// Constrain the content element (DomScrollableElement._element) so that
|
||||
// scanDomNode sees clientHeight < scrollHeight and enables scrolling.
|
||||
// The wrapper inherits the same constraint via CSS flex.
|
||||
if (scrollableContent.style.height !== constrainedScrollableHeightPx || scrollableContent.style.maxHeight !== constrainedScrollableHeightPx) {
|
||||
scrollableContent.style.height = constrainedScrollableHeightPx;
|
||||
scrollableContent.style.maxHeight = constrainedScrollableHeightPx;
|
||||
@@ -582,6 +600,14 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
|
||||
inputScrollableNode.classList.add('chat-question-input-scrollable');
|
||||
this._questionContainer.appendChild(inputScrollableNode);
|
||||
|
||||
// Render footer before first layout so the scrollable area is measured against
|
||||
// its final available height and does not visibly resize twice.
|
||||
if (!isSingleQuestion) {
|
||||
this.renderFooter();
|
||||
} else {
|
||||
this.renderSingleQuestionFooter();
|
||||
}
|
||||
|
||||
let relayoutScheduled = false;
|
||||
const relayoutScheduler = questionRenderStore.add(new MutableDisposable());
|
||||
const scheduleLayoutInputScrollable = () => {
|
||||
@@ -600,7 +626,6 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
|
||||
questionRenderStore.add(inputResizeObserver.observe(inputScrollableNode));
|
||||
questionRenderStore.add(inputResizeObserver.observe(inputContainer));
|
||||
scheduleLayoutInputScrollable();
|
||||
this.layoutInputScrollable(inputScrollable);
|
||||
questionRenderStore.add(dom.runAtThisOrScheduleAtNextAnimationFrame(dom.getWindow(this.domNode), () => {
|
||||
inputContainer.scrollTop = 0;
|
||||
inputContainer.scrollLeft = 0;
|
||||
@@ -608,13 +633,6 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
|
||||
inputScrollable.scanDomNode();
|
||||
}));
|
||||
|
||||
// Render footer for multi-question carousels or single-question carousels.
|
||||
if (!isSingleQuestion) {
|
||||
this.renderFooter();
|
||||
} else {
|
||||
this.renderSingleQuestionFooter();
|
||||
}
|
||||
|
||||
// Update aria-label to reflect the current question
|
||||
this._updateAriaLabel();
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ import { InlineChatConfigKeys } from '../../../../inlineChat/common/inlineChat.j
|
||||
import { IChatViewTitleActionContext } from '../../../common/actions/chatActions.js';
|
||||
import { ChatContextKeys } from '../../../common/actions/chatContextKeys.js';
|
||||
import { ChatRequestVariableSet, IChatRequestVariableEntry, isElementVariableEntry, isImageVariableEntry, isNotebookOutputVariableEntry, isPasteVariableEntry, isPromptFileVariableEntry, isPromptTextVariableEntry, isSCMHistoryItemChangeRangeVariableEntry, isSCMHistoryItemChangeVariableEntry, isSCMHistoryItemVariableEntry, isStringVariableEntry } from '../../../common/attachments/chatVariableEntries.js';
|
||||
import { ChatMode, IChatMode, IChatModeService } from '../../../common/chatModes.js';
|
||||
import { ChatMode, getModeNameForTelemetry, IChatMode, IChatModeService } from '../../../common/chatModes.js';
|
||||
import { IChatFollowup, IChatQuestionCarousel, IChatService, IChatSessionContext } from '../../../common/chatService/chatService.js';
|
||||
import { agentOptionId, IChatSessionProviderOptionGroup, IChatSessionProviderOptionItem, IChatSessionsService, isIChatSessionFileChange2, localChatSessionType } from '../../../common/chatSessionsService.js';
|
||||
import { ChatAgentLocation, ChatConfiguration, ChatModeKind, ChatPermissionLevel, validateChatMode } from '../../../common/constants.js';
|
||||
@@ -444,6 +444,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
isBuiltin: mode.isBuiltin
|
||||
} : undefined,
|
||||
modeId: modeId,
|
||||
modeName: getModeNameForTelemetry(mode),
|
||||
applyCodeBlockSuggestionId: undefined,
|
||||
permissionLevel: this._currentPermissionLevel.get(),
|
||||
};
|
||||
|
||||
@@ -150,6 +150,8 @@ export type ChatProviderInvokedEvent = {
|
||||
attachmentKinds: string[];
|
||||
model: string | undefined;
|
||||
permissionLevel: ChatPermissionLevel | undefined;
|
||||
chatMode: string | undefined;
|
||||
sessionType: string | undefined;
|
||||
};
|
||||
|
||||
export type ChatProviderInvokedClassification = {
|
||||
@@ -169,6 +171,8 @@ export type ChatProviderInvokedClassification = {
|
||||
attachmentKinds: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The types of variables/attachments that the user included with their query.' };
|
||||
model: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The model used to generate the response.' };
|
||||
permissionLevel: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The tool auto-approval permission level selected in the permission picker (default, autoApprove, or autopilot). Undefined when the picker is not applicable (e.g. ask mode or API-driven requests).' };
|
||||
chatMode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The chat mode used for the request. Built-in modes (ask, agent, edit), extension-contributed names (e.g. Plan), or a hashed identifier for user-created custom agents.' };
|
||||
sessionType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The session type scheme (e.g. vscodeLocalChatSession for local, or remote session scheme).' };
|
||||
owner: 'roblourens';
|
||||
comment: 'Provides insight into the performance of Chat agents.';
|
||||
};
|
||||
@@ -306,6 +310,8 @@ export class ChatRequestTelemetry {
|
||||
attachmentKinds: this.attachmentKindsForTelemetry(request.variableData),
|
||||
model: this.resolveModelId(this.opts.options?.userSelectedModelId),
|
||||
permissionLevel: this.opts.options?.modeInfo?.kind === ChatModeKind.Ask ? undefined : this.opts.options?.modeInfo?.permissionLevel,
|
||||
chatMode: this.opts.options?.modeInfo?.modeName ?? this.opts.options?.modeInfo?.modeId,
|
||||
sessionType: this.opts.sessionResource.scheme,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -313,6 +313,7 @@ export interface IChatRequestModeInfo {
|
||||
isBuiltin: boolean;
|
||||
modeInstructions: IChatRequestModeInstructions | undefined;
|
||||
modeId: 'ask' | 'agent' | 'edit' | 'custom' | 'applyCodeBlock' | undefined;
|
||||
modeName?: string;
|
||||
applyCodeBlockSuggestionId: EditSuggestionId | undefined;
|
||||
permissionLevel?: ChatPermissionLevel;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ import { IMcpResourceScannerService, McpResourceTarget } from '../../../../platf
|
||||
import { IRemoteAgentEnvironment } from '../../../../platform/remote/common/remoteAgentEnvironment.js';
|
||||
import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
|
||||
import { IMcpSandboxConfiguration } from '../../../../platform/mcp/common/mcpPlatformTypes.js';
|
||||
import { IMcpPotentialSandboxBlock, McpServerDefinition, McpServerLaunch, McpServerTransportType } from './mcpTypes.js';
|
||||
import { IMcpPotentialSandboxBlock, McpServerDefinition, McpServerLaunch, McpServerTransportStdio, McpServerTransportType } from './mcpTypes.js';
|
||||
|
||||
|
||||
export const IMcpSandboxService = createDecorator<IMcpSandboxService>('mcpSandboxService');
|
||||
|
||||
@@ -45,6 +46,7 @@ type SandboxConfigSuggestionResult = {
|
||||
type SandboxLaunchDetails = {
|
||||
execPath: string | undefined;
|
||||
srtPath: string | undefined;
|
||||
rgPath: string | undefined;
|
||||
sandboxConfigPath: string | undefined;
|
||||
tempDir: URI | undefined;
|
||||
};
|
||||
@@ -86,15 +88,14 @@ export class McpSandboxService extends Disposable implements IMcpSandboxService
|
||||
this._logService.trace(`McpSandboxService: Launching with config target ${configTarget}`);
|
||||
const launchDetails = await this._resolveSandboxLaunchDetails(configTarget, remoteAuthority, launch.sandbox, launch.cwd);
|
||||
const sandboxArgs = this._getSandboxCommandArgs(launch.command, launch.args, launchDetails.sandboxConfigPath);
|
||||
const sandboxEnv = this._getSandboxEnvVariables(launchDetails.tempDir, remoteAuthority);
|
||||
const sandboxEnv = await this._getSandboxEnvVariables(launch.env, launchDetails.tempDir, launchDetails.rgPath, remoteAuthority);
|
||||
if (launchDetails.srtPath) {
|
||||
const envWithSandbox = sandboxEnv ? { ...launch.env, ...sandboxEnv } : launch.env;
|
||||
if (launchDetails.execPath) {
|
||||
return {
|
||||
...launch,
|
||||
command: launchDetails.execPath,
|
||||
args: [launchDetails.srtPath, ...sandboxArgs],
|
||||
env: envWithSandbox,
|
||||
env: sandboxEnv,
|
||||
type: McpServerTransportType.Stdio,
|
||||
};
|
||||
} else {
|
||||
@@ -102,7 +103,7 @@ export class McpSandboxService extends Disposable implements IMcpSandboxService
|
||||
...launch,
|
||||
command: launchDetails.srtPath,
|
||||
args: sandboxArgs,
|
||||
env: envWithSandbox,
|
||||
env: sandboxEnv,
|
||||
type: McpServerTransportType.Stdio,
|
||||
};
|
||||
}
|
||||
@@ -252,16 +253,17 @@ export class McpSandboxService extends Disposable implements IMcpSandboxService
|
||||
private async _resolveSandboxLaunchDetails(configTarget: ConfigurationTarget, remoteAuthority?: string, sandboxConfig?: IMcpSandboxConfiguration, launchCwd?: string): Promise<SandboxLaunchDetails> {
|
||||
const os = await this._getOperatingSystem(remoteAuthority);
|
||||
if (os === OperatingSystem.Windows) {
|
||||
return { execPath: undefined, srtPath: undefined, sandboxConfigPath: undefined, tempDir: undefined };
|
||||
return { execPath: undefined, srtPath: undefined, rgPath: undefined, sandboxConfigPath: undefined, tempDir: undefined };
|
||||
}
|
||||
|
||||
const appRoot = await this._getAppRoot(remoteAuthority);
|
||||
const execPath = await this._getExecPath(os, appRoot, remoteAuthority);
|
||||
const tempDir = await this._getTempDir(remoteAuthority);
|
||||
const srtPath = this._pathJoin(os, appRoot, 'node_modules', '@anthropic-ai', 'sandbox-runtime', 'dist', 'cli.js');
|
||||
const rgPath = this._pathJoin(os, appRoot, 'node_modules', '@vscode', 'ripgrep', 'bin', 'rg');
|
||||
const sandboxConfigPath = tempDir ? await this._updateSandboxConfig(tempDir, configTarget, sandboxConfig, launchCwd) : undefined;
|
||||
this._logService.debug(`McpSandboxService: Updated sandbox config path: ${sandboxConfigPath}`);
|
||||
return { execPath, srtPath, sandboxConfigPath, tempDir };
|
||||
return { execPath, srtPath, rgPath, sandboxConfigPath, tempDir };
|
||||
}
|
||||
|
||||
private async _getExecPath(os: OperatingSystem, appRoot: string, remoteAuthority?: string): Promise<string | undefined> {
|
||||
@@ -271,10 +273,13 @@ export class McpSandboxService extends Disposable implements IMcpSandboxService
|
||||
return undefined; // Use Electron executable as the default exec path for local development, which will run the sandbox runtime wrapper with Electron in node mode. For remote, we need to specify the node executable to ensure it runs with Node.js.
|
||||
}
|
||||
|
||||
private _getSandboxEnvVariables(tempDir: URI | undefined, remoteAuthority?: string): Record<string, string | null> | undefined {
|
||||
let env: Record<string, string | null> = {};
|
||||
private async _getSandboxEnvVariables(baseEnv: McpServerTransportStdio['env'], tempDir: URI | undefined, rgPath: string | undefined, remoteAuthority?: string): Promise<McpServerTransportStdio['env']> {
|
||||
let env: McpServerTransportStdio['env'] = { ...baseEnv };
|
||||
if (tempDir) {
|
||||
env = { TMPDIR: tempDir.path, SRT_DEBUG: 'true' };
|
||||
env = { ...env, TMPDIR: tempDir.path, SRT_DEBUG: 'true' };
|
||||
}
|
||||
if (rgPath) {
|
||||
env = { ...env, PATH: env['PATH'] ? `${env['PATH']}${await this._getPathDelimiter(remoteAuthority)}${dirname(rgPath)}` : dirname(rgPath) };
|
||||
}
|
||||
if (!remoteAuthority) {
|
||||
// Add any remote-specific environment variables here
|
||||
@@ -391,4 +396,9 @@ export class McpSandboxService extends Disposable implements IMcpSandboxService
|
||||
return path.join(...segments);
|
||||
};
|
||||
|
||||
private _getPathDelimiter = async (remoteAuthority?: string) => {
|
||||
const os = await this._getOperatingSystem(remoteAuthority);
|
||||
return os === OperatingSystem.Windows ? win32.delimiter : posix.delimiter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
6
test/mcp/package-lock.json
generated
6
test/mcp/package-lock.json
generated
@@ -702,9 +702,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.3.tgz",
|
||||
"integrity": "sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==",
|
||||
"version": "4.12.5",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.5.tgz",
|
||||
"integrity": "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
Reference in New Issue
Block a user