diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index fd4954c4..ef2a7b41 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -76,12 +76,13 @@ "Explain the following code {% include 'Page Copy'%}": "Explain the following code {% include 'Page Copy'%}", "Hi! @powerpages can help you write, edit, and even summarize your website code.": "Hi! @powerpages can help you write, edit, and even summarize your website code.", "Authentication failed. Please try again.": "Authentication failed. Please try again.", - "Copilot is not available. Please contact your administrator.": "Copilot is not available. Please contact your administrator.", + "AI features have been disabled by your organization. Contact your admin for details. [Learn more](https://go.microsoft.com/fwlink/?linkid=2285848)": "AI features have been disabled by your organization. Contact your admin for details. [Learn more](https://go.microsoft.com/fwlink/?linkid=2285848)", "Active auth profile is not found or has expired. Please try again.": "Active auth profile is not found or has expired. Please try again.", "Something went wrong. Don’t worry, you can try again.": "Something went wrong. Don’t worry, you can try again.", "Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520)": "Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520)", "Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.": "Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.", "Checking for active auth profile...": "Checking for active auth profile...", + "@PowerPages is not yet available in your region.": "@PowerPages is not yet available in your region.", "Select Folder for new PCF Control/Do not translate 'PCF' as it is a product name.": { "message": "Select Folder for new PCF Control", "comment": [ @@ -255,4 +256,4 @@ "The {0} represents profile's Azure Cloud Instances" ] } -} \ No newline at end of file +} diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 4b26baf5..e324304d 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -1,6 +1,9 @@ + + @PowerPages is not yet available in your region. + A content snippet with the same name already exists. Please enter a different name. @@ -13,6 +16,9 @@ A webtemplate with the same name already exists. Please enter a different name. + + AI features have been disabled by your organization. Contact your admin for details. [Learn more](https://go.microsoft.com/fwlink/?linkid=2285848) + AI-generated content can contain mistakes @@ -88,9 +94,6 @@ Copied to clipboard! - - Copilot is not available. Please contact your administrator. - Copy to clipboard diff --git a/package.json b/package.json index 5d41c051..67982961 100644 --- a/package.json +++ b/package.json @@ -118,8 +118,7 @@ "name": "powerpages", "fullName": "Copilot for Power Pages", "description": "Copilot for Power Pages", - "isSticky": true, - "when": "never" + "isSticky": true } ], "problemMatchers": [ diff --git a/src/client/extension.ts b/src/client/extension.ts index b4abbc16..cd2e5ebd 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -42,7 +42,9 @@ import { ArtemisService } from "../common/services/ArtemisService"; import { workspaceContainsPortalConfigFolder } from "../common/utilities/PathFinderUtil"; import { getPortalsOrgURLs } from "../common/utilities/WorkspaceInfoFinderUtil"; import { EXTENSION_ID, SUCCESS } from "../common/constants"; -import { AadIdKey } from "../common/OneDSLoggerTelemetry/telemetryConstants"; +import { AadIdKey, EnvIdKey, TenantIdKey } from "../common/OneDSLoggerTelemetry/telemetryConstants"; +import { PowerPagesAppName, PowerPagesClientName } from "../common/ecs-features/constants"; +import { ECSFeaturesClient } from "../common/ecs-features/ecsFeatureClient"; let client: LanguageClient; let _context: vscode.ExtensionContext; @@ -193,12 +195,27 @@ export async function activate( const orgID = orgDetails.OrgId; const artemisResponse = await ArtemisService.getArtemisResponse(orgID, _telemetry, ""); if (artemisResponse !== null && artemisResponse.response !== null) { + const { geoName, geoLongName } = artemisResponse.response; const pacActiveAuth = await pacTerminal.getWrapper()?.activeAuth(); - let AadIdObject; + let AadIdObject, EnvID, TenantID; if ((pacActiveAuth && pacActiveAuth.Status === SUCCESS)) { AadIdObject = pacActiveAuth.Results?.filter(obj => obj.Key === AadIdKey); + EnvID = pacActiveAuth.Results?.filter(obj => obj.Key === EnvIdKey); + TenantID = pacActiveAuth.Results?.filter(obj => obj.Key === TenantIdKey); } - const { geoName, geoLongName } = artemisResponse.response; + + if (EnvID?.[0]?.Value && TenantID?.[0]?.Value && AadIdObject?.[0]?.Value) { + await ECSFeaturesClient.init(_telemetry, + { + AppName: PowerPagesAppName, + EnvID: EnvID[0].Value, + UserID: AadIdObject[0].Value, + TenantID: TenantID[0].Value, + Region: artemisResponse.stamp + }, + PowerPagesClientName); + } + oneDSLoggerWrapper.instantiate(geoName, geoLongName); let initContext: object = { ...orgDetails, orgGeo: geoName }; if (AadIdObject?.[0]?.Value) { diff --git a/src/common/OneDSLoggerTelemetry/telemetryConstants.ts b/src/common/OneDSLoggerTelemetry/telemetryConstants.ts index eab9ad97..fc98d128 100644 --- a/src/common/OneDSLoggerTelemetry/telemetryConstants.ts +++ b/src/common/OneDSLoggerTelemetry/telemetryConstants.ts @@ -39,4 +39,6 @@ export enum GeoNames { // Custom telemetry feature flag export const CUSTOM_TELEMETRY_FOR_POWER_PAGES_SETTING_NAME = 'enableTelemetry'; -export const AadIdKey= 'Entra ID Object Id:'; \ No newline at end of file +export const AadIdKey= 'Entra ID Object Id:'; +export const EnvIdKey = "Environment Id:"; +export const TenantIdKey = "Tenant Id:"; diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index dd2284a4..85de5fbc 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts @@ -12,13 +12,14 @@ import { sendApiRequest } from '../../copilot/IntelligenceApiService'; import { PacWrapper } from '../../../client/pac/PacWrapper'; import { intelligenceAPIAuthentication } from '../../services/AuthenticationProvider'; import { ActiveOrgOutput } from '../../../client/pac/PacTypes'; -import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; +import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, COPILOT_NOT_RELEASED_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; import { ORG_DETAILS_KEY, handleOrgChangeSuccess, initializeOrgDetails } from '../../utilities/OrgHandlerUtils'; import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables } from './PowerPagesChatParticipantUtils'; import { checkCopilotAvailability, fetchRelatedFiles, getActiveEditorContent } from '../../utilities/Utils'; import { IIntelligenceAPIEndpointInformation } from '../../services/Interfaces'; import { v4 as uuidv4 } from 'uuid'; import { orgChangeErrorEvent, orgChangeEvent } from '../../../client/OrgChangeNotifier'; +import { isPowerPagesGitHubCopilotEnabled } from '../../copilot/utils/copilotUtil'; import { ADX_WEBPAGE, IApiRequestParams, IRelatedFiles } from '../../constants'; import { oneDSLoggerWrapper } from '../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; @@ -42,7 +43,7 @@ export class PowerPagesChatParticipant { this.chatParticipant = createChatParticipant(POWERPAGES_CHAT_PARTICIPANT_ID, this.handler); //TODO: Check the icon image - this.chatParticipant.iconPath = vscode.Uri.joinPath(context.extensionUri, 'src', 'common', 'chat-participants', 'powerpages', 'assets', 'copilot.png'); + this.chatParticipant.iconPath = vscode.Uri.joinPath(context.extensionUri, 'src', 'common', 'chat-participants', 'powerpages', 'assets', 'copilot.svg'); this.chatParticipant.onDidReceiveFeedback((feedback: vscode.ChatResultFeedback) => { handleChatParticipantFeedback(feedback, this.powerPagesAgentSessionId, this.telemetry); @@ -107,6 +108,13 @@ export class PowerPagesChatParticipant { this.telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, { orgID: this.orgID, environmentID: this.environmentID, sessionId: this.powerPagesAgentSessionId }); oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, { orgID: this.orgID, environmentID: this.environmentID, sessionId: this.powerPagesAgentSessionId }); + if (!isPowerPagesGitHubCopilotEnabled()) { + stream.markdown(COPILOT_NOT_RELEASED_MSG); + this.telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, { sessionId: this.powerPagesAgentSessionId, orgID: this.orgID }); + oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, { sessionId: this.powerPagesAgentSessionId, orgID: this.orgID }); + return createSuccessResult('', RESPONSE_SCENARIOS.COPILOT_NOT_RELEASED, this.orgID); + } + const intelligenceApiAuthResponse = await intelligenceAPIAuthentication(this.telemetry, this.powerPagesAgentSessionId, this.orgID, true); if (!intelligenceApiAuthResponse) { @@ -219,7 +227,7 @@ export class PowerPagesChatParticipant { private async initializeOrgDetails(): Promise { try { - const { orgID, orgUrl, environmentID } = await initializeOrgDetails(this.isOrgDetailsInitialized, this.extensionContext, this._pacWrapper); + const { orgID, orgUrl, environmentID } = await initializeOrgDetails(this.isOrgDetailsInitialized, this._pacWrapper); if (!orgID) { return; @@ -234,7 +242,7 @@ export class PowerPagesChatParticipant { } private async handleOrgChangeSuccess(orgDetails: ActiveOrgOutput): Promise { - const { orgID, orgUrl, environmentID } = handleOrgChangeSuccess(orgDetails, this.extensionContext); + const { orgID, orgUrl, environmentID } = handleOrgChangeSuccess(orgDetails); this.orgID = orgID; this.orgUrl = orgUrl; this.environmentID = environmentID; diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts index 6d4e9e31..969f5bc5 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts @@ -23,7 +23,8 @@ export const RESPONSE_SCENARIOS = { FORM_PROMPT: 'FORM_PROMPT', LIST_PROMPT: 'LIST_PROMPT', WEB_API_PROMPT: 'WEB_API_PROMPT', - WELCOME_PROMPT: 'WELCOME_PROMPT' + WELCOME_PROMPT: 'WELCOME_PROMPT', + COPILOT_NOT_RELEASED: 'COPILOT_NOT_RELEASED' }; // Localized strings @@ -40,6 +41,7 @@ export const INVALID_RESPONSE = vscode.l10n.t('Something went wrong. Don’t wor export const DISCLAIMER_MESSAGE = vscode.l10n.t('Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520)'); export const NO_PROMPT_MESSAGE = vscode.l10n.t('Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.'); export const PAC_AUTH_INPUT = vscode.l10n.t("Checking for active auth profile..."); +export const COPILOT_NOT_RELEASED_MSG = vscode.l10n.t("@PowerPages is not yet available in your region.") // Telemetry Event Names export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED = 'VSCodeExtensionGitHubPowerPagesAgentInvoked'; @@ -54,5 +56,6 @@ export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED = 'VS export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentNoPrompt'; export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentWelcomePrompt'; export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT = 'VSCodeExtensionGitHubPowerPagesAgentSuccessfulPrompt'; +export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS = 'VSCodeExtensionGitHubPowerPagesAgentNotAvailableECS'; diff --git a/src/common/chat-participants/powerpages/assets/copilot.svg b/src/common/chat-participants/powerpages/assets/copilot.svg new file mode 100644 index 00000000..1f752210 --- /dev/null +++ b/src/common/chat-participants/powerpages/assets/copilot.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/copilot/utils/copilotUtil.ts b/src/common/copilot/utils/copilotUtil.ts index ce00f5a9..6825aae3 100644 --- a/src/common/copilot/utils/copilotUtil.ts +++ b/src/common/copilot/utils/copilotUtil.ts @@ -6,7 +6,7 @@ import * as vscode from "vscode"; import { PacWrapper } from "../../../client/pac/PacWrapper"; import { ECSFeaturesClient } from "../../ecs-features/ecsFeatureClient"; -import { CopilotDisableList, EnableProDevCopilot } from "../../ecs-features/ecsFeatureGates"; +import { CopilotDisableList, EnablePowerPagesGitHubCopilot, EnableProDevCopilot } from "../../ecs-features/ecsFeatureGates"; import { AUTH_CREATE_FAILED, AUTH_CREATE_MESSAGE } from "../constants"; import { showInputBoxAndGetOrgUrl, showProgressWithNotification } from "../../utilities/Utils"; import { SUCCESS } from "../../constants"; @@ -79,3 +79,13 @@ export function enableCrossGeoDataFlowInGeo() { return enableCrossGeoDataFlowInGeo.split(',').map(org => org.trim()); } + +export function isPowerPagesGitHubCopilotEnabled() { + const enablePowerpagesInGithubCopilot = ECSFeaturesClient.getConfig(EnablePowerPagesGitHubCopilot).enablePowerpagesInGithubCopilot + + if(enablePowerpagesInGithubCopilot === undefined) { + return false; + } + + return enablePowerpagesInGithubCopilot; +} diff --git a/src/common/ecs-features/ecsFeatureGates.ts b/src/common/ecs-features/ecsFeatureGates.ts index 6a66a98f..b49fe645 100644 --- a/src/common/ecs-features/ecsFeatureGates.ts +++ b/src/common/ecs-features/ecsFeatureGates.ts @@ -38,3 +38,13 @@ export const { "capiSupportedProDevCopilotGeoWithCrossGeoDataFlow": "eu,se,ch,fr,de,no" }, }); + +export const { + feature: EnablePowerPagesGitHubCopilot +} = getFeatureConfigs({ + teamName: PowerPagesClientName, + description: 'Enable Power Pages GitHub Copilot', + fallback: { + enablePowerpagesInGithubCopilot: false, + }, +}); diff --git a/src/common/utilities/OrgHandlerUtils.ts b/src/common/utilities/OrgHandlerUtils.ts index 11df4959..f6f49241 100644 --- a/src/common/utilities/OrgHandlerUtils.ts +++ b/src/common/utilities/OrgHandlerUtils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ -import { ExtensionContext } from 'vscode'; import { ActiveOrgOutput } from '../../client/pac/PacTypes'; import { PacWrapper } from '../../client/pac/PacWrapper'; import { IOrgDetails } from '../chat-participants/powerpages/PowerPagesChatParticipantTypes'; @@ -15,26 +14,22 @@ export const ORG_DETAILS_KEY = 'orgDetails'; export function handleOrgChangeSuccess( orgDetails: ActiveOrgOutput, - extensionContext: ExtensionContext ): IOrgDetails { const { OrgId: orgID, OrgUrl: orgUrl, EnvironmentId: environmentID } = orgDetails; - extensionContext.globalState.update(ORG_DETAILS_KEY, { orgID, orgUrl, environmentID }); - return { orgID, orgUrl, environmentID }; } -async function fetchOrgDetailsFromPac(pacWrapper: PacWrapper, extensionContext: ExtensionContext): Promise { +async function fetchOrgDetailsFromPac(pacWrapper: PacWrapper): Promise { const pacActiveOrg = await pacWrapper.activeOrg(); if (pacActiveOrg && pacActiveOrg.Status === SUCCESS) { - return handleOrgChangeSuccess(pacActiveOrg.Results, extensionContext); + return handleOrgChangeSuccess(pacActiveOrg.Results); } throw new Error(ERROR_CONSTANTS.PAC_AUTH_FAILED); } export async function initializeOrgDetails( isOrgDetailsInitialized: boolean, - extensionContext: ExtensionContext, pacWrapper?: PacWrapper ): Promise { const orgDetails: IOrgDetails = { orgID: '', orgUrl: '', environmentID: '' }; @@ -43,21 +38,15 @@ export async function initializeOrgDetails( return orgDetails; } - // Get stored organization details from global state - const storedOrgDetails: IOrgDetails | undefined = extensionContext.globalState.get(ORG_DETAILS_KEY); - if (storedOrgDetails && storedOrgDetails.orgID && storedOrgDetails.orgUrl && storedOrgDetails.environmentID) { - return storedOrgDetails; - } - if (pacWrapper) { try { - const fetchedOrgDetails = await fetchOrgDetailsFromPac(pacWrapper, extensionContext); + const fetchedOrgDetails = await fetchOrgDetailsFromPac(pacWrapper); orgDetails.orgID = fetchedOrgDetails.orgID; orgDetails.orgUrl = fetchedOrgDetails.orgUrl; orgDetails.environmentID = fetchedOrgDetails.environmentID; } catch (error) { await createAuthProfileExp(pacWrapper); - const fetchedOrgDetails = await fetchOrgDetailsFromPac(pacWrapper, extensionContext); + const fetchedOrgDetails = await fetchOrgDetailsFromPac(pacWrapper); orgDetails.orgID = fetchedOrgDetails.orgID; orgDetails.orgUrl = fetchedOrgDetails.orgUrl; orgDetails.environmentID = fetchedOrgDetails.environmentID;