Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pipelines/azure-build-publish-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,17 @@ stages:
artifact: vscode-shell
displayName: "Download vscode-shell"

- pwsh: |
$agentDir = "$(Pipeline.Workspace)/agent-server-win32-x64"
node "$agentDir/tools/copilotRuntime.mjs" verify-feed --non-interactive
if ($LASTEXITCODE -ne 0) {
Write-Error "The exact Copilot runtime packages are unavailable through the TypeAgent npm feed."
exit $LASTEXITCODE
}
displayName: "Verify Copilot runtime packages in TypeAgent feed"
env:
TYPEAGENT_FEED_TOKEN: $(System.AccessToken)

# Set up internal npm registry (required by org policy).
- bash: |
echo "registry=$INSTALL_REGISTRY" > .npmrc
Expand Down
8 changes: 7 additions & 1 deletion ts/packages/aiclient/src/copilotModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ export interface CopilotClientOptions {
}

function findCopilotPath(): string {
const configuredPath =
process.env.TYPEAGENT_COPILOT_CLI_PATH ?? process.env.COPILOT_CLI_PATH;
if (configuredPath) {
debug(`Using configured copilot CLI: ${configuredPath}`);
return configuredPath;
}
try {
const isWindows = process.platform === "win32";
const command = isWindows ? "where copilot" : "which copilot";
Expand Down Expand Up @@ -207,7 +213,7 @@ async function getClient(
`Failed to start GitHub Copilot CLI client (${target}). ` +
(cliUrl
? `Ensure a Copilot CLI server is running and reachable at '${cliUrl}'.\n`
: `Ensure 'copilot' is installed and authenticated (try 'copilot auth login').\n`) +
: `Run 'node typeagent-serve.mjs setup --provider copilot', or install and authenticate a compatible 'copilot' CLI.\n`) +
`Underlying error: ${err instanceof Error ? err.message : String(err)}`,
);
}
Expand Down
4 changes: 4 additions & 0 deletions ts/tools/installers/common/package-feed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"registry": "https://pkgs.dev.azure.com/msctoproj/AI_Systems/_packaging/typeagent-feed/npm/registry/",
"azureDevOpsResource": "499b84ac-1321-427f-aa17-267ca6975798"
}
42 changes: 33 additions & 9 deletions ts/tools/installers/common/register-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const scriptPath = fileURLToPath(import.meta.url);

function parseArgs(argv) {
const opts = {
Expand Down Expand Up @@ -113,6 +116,12 @@ function hasListedEntry(output, identifier) {
return findListedEntry(output, identifier) !== undefined;
}

export function listedEntryState(output, identifier) {
const entry = findListedEntry(output, identifier);
if (!entry) return "absent";
return /\(disabled\)/i.test(entry) ? "disabled" : "enabled";
}

function quoteCmdArgument(value) {
return `"${value.replace(/%/g, "%%").replace(/"/g, '""')}"`;
}
Expand Down Expand Up @@ -501,7 +510,11 @@ function installPlugin(opts, logger) {
logger,
);
const pluginIdentifier = `${opts.pluginName}@${opts.marketplaceName}`;
if (hasListedEntry(pluginListResult.output, pluginIdentifier)) {
const pluginState = listedEntryState(
pluginListResult.output,
pluginIdentifier,
);
if (pluginState === "enabled") {
const update = runCopilot(
opts.copilotPath,
["plugin", "update", pluginIdentifier],
Expand All @@ -521,6 +534,11 @@ function installPlugin(opts, logger) {
retryUpdateFromCleanSnapshot(opts, logger, pluginIdentifier);
}
} else {
if (pluginState === "disabled") {
logger.write(
`Plugin '${pluginIdentifier}' is available but disabled; installing it to enable the plugin.`,
);
}
runCopilot(
opts.copilotPath,
["plugin", "install", pluginIdentifier],
Expand All @@ -533,9 +551,13 @@ function installPlugin(opts, logger) {
["plugin", "list"],
logger,
);
if (!hasListedEntry(verifyListResult.output, pluginIdentifier)) {
const verifiedState = listedEntryState(
verifyListResult.output,
pluginIdentifier,
);
if (verifiedState !== "enabled") {
throw new Error(
`Plugin verification failed: '${pluginIdentifier}' not found in copilot plugin list.`,
`Plugin verification failed: '${pluginIdentifier}' is ${verifiedState}.`,
);
}

Expand Down Expand Up @@ -580,10 +602,12 @@ function main() {
process.exit(0);
}

try {
main();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`[TypeAgent] Registration failed: ${message}`);
process.exit(1);
if (path.resolve(process.argv[1] ?? "") === scriptPath) {
try {
main();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`[TypeAgent] Registration failed: ${message}`);
process.exit(1);
}
}
33 changes: 24 additions & 9 deletions ts/tools/installers/wix/TypeAgent-AgentServer.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
<ComponentRef Id="InstallVsCodeChatPs1Component" />
<ComponentRef Id="ExtractPayloadPs1Component" />
<ComponentRef Id="InstallPrereqsPs1Component" />
<ComponentRef Id="LaunchCopilotSetupPs1Component" />
<ComponentRef Id="ResolveNodePs1Component" />
<ComponentRef Id="RunServePs1Component" />
<ComponentRef Id="InstallShellPs1Component" />
Expand Down Expand Up @@ -264,6 +265,10 @@
<File Id="InstallPrereqsPs1" Name="install-prereqs.ps1"
Source="$(var.InstallerSourceDir)\install-prereqs.ps1" />
</Component>
<Component Id="LaunchCopilotSetupPs1Component" Guid="*">
<File Id="LaunchCopilotSetupPs1" Name="launch-copilot-setup.ps1"
Source="$(var.InstallerSourceDir)\launch-copilot-setup.ps1" />
</Component>
<Component Id="ResolveNodePs1Component" Guid="*">
<File Id="ResolveNodePs1" Name="resolve-node.ps1"
Source="$(var.InstallerSourceDir)\resolve-node.ps1" />
Expand Down Expand Up @@ -449,17 +454,16 @@
Impersonate="yes" />

<!-- ═══════════════════════════════════════════════
Custom action — external-CLI + Node prerequisites
The MSI ships the 'external' agent-server variant, which resolves the
claude/copilot CLIs from PATH. Install them per-user (npm i -g) and warn
on a missing/old Node. Impersonated (writes the user's npm prefix) and
non-fatal (Return="ignore") — a prerequisite hiccup must not roll back
the agent-server install; issues surface as warnings + the ExitDialog.
Custom action — external-runtime + Node prerequisites
Claude is installed from the authenticated TypeAgent feed when it is
absent. For the Copilot provider, the exact SDK-compatible runtime is
installed into TypeAgent's per-user cache. Impersonated and non-fatal
(Return="ignore") so a prerequisite hiccup never rolls back the MSI.
═══════════════════════════════════════════════ -->
<SetProperty Id="InstallPrereqs"
Before="InstallPrereqs"
Sequence="execute"
Value="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -ExecutionPolicy Bypass -NoProfile -NonInteractive -File &quot;[TYPEAGENTROOT]install-prereqs.ps1&quot; -LogPath &quot;[LocalAppDataFolder]TypeAgent\logs\msi-install-prereqs.log&quot;" />
Value="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -ExecutionPolicy Bypass -NoProfile -NonInteractive -File &quot;[TYPEAGENTROOT]install-prereqs.ps1&quot; -Provider &quot;[PROVIDER]&quot; -AgentServerDir &quot;[INSTALLFOLDER].&quot; -PluginInstallDir &quot;[TYPEAGENTROOT].&quot; -LogPath &quot;[LocalAppDataFolder]TypeAgent\logs\msi-install-prereqs.log&quot;" />

<CustomAction Id="InstallPrereqs"
BinaryKey="WixCA"
Expand All @@ -468,6 +472,16 @@
Return="ignore"
Impersonate="yes" />

<!-- Launch the interactive Copilot bootstrap only after the MSI transaction
commits. Quiet/basic-UI installs remain non-interactive and can run the
same setup command later. -->
<CustomAction Id="LaunchCopilotSetup"
Directory="TYPEAGENTROOT"
ExeCommand="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -ExecutionPolicy Bypass -NoProfile -NonInteractive -File &quot;[TYPEAGENTROOT]launch-copilot-setup.ps1&quot; -ServePath &quot;[INSTALLFOLDER]typeagent-serve.mjs&quot; -LogPath &quot;[LocalAppDataFolder]TypeAgent\logs\copilot-setup-launch.log&quot;"
Execute="immediate"
Return="ignore"
Impersonate="yes" />

<!-- ═══════════════════════════════════════════════
Custom action — AISYSTEMS config provisioning
For PROVIDER=AISYSTEMS, download config.local.yaml from Key Vault via
Expand Down Expand Up @@ -679,8 +693,8 @@
<Custom Action="MainProgress5" After="InstallVsCodeShell">NOT REMOVE~="ALL"</Custom>
<Custom Action="ProvisionCopilotConfig" After="MainProgress5">(NOT REMOVE~="ALL") AND (PROVIDER="COPILOT")</Custom>
<Custom Action="ProvisionAiSystemsConfig" After="ProvisionCopilotConfig">(NOT REMOVE~="ALL") AND (PROVIDER="AISYSTEMS")</Custom>
<!-- Prereqs (claude/copilot from the feed) run AFTER provisioning so the
AISYSTEMS az login has established a session the feed token reuses. -->
<!-- Runtime prerequisites run after provisioning so an existing Azure
session can be reused for authenticated TypeAgent feed access. -->
<Custom Action="MainProgress6" After="ProvisionAiSystemsConfig">NOT REMOVE~="ALL"</Custom>
<Custom Action="InstallPrereqs" After="MainProgress6">NOT REMOVE~="ALL"</Custom>
<Custom Action="MainProgress7" After="InstallPrereqs">NOT REMOVE~="ALL"</Custom>
Expand All @@ -695,6 +709,7 @@
<Custom Action="InstallTypeAgentShell" After="ShellProgress2">(NOT REMOVE~="ALL") AND (SHELL="1")</Custom>
<Custom Action="ShellProgress3" After="InstallTypeAgentShell">(NOT REMOVE~="ALL") AND (SHELL="1")</Custom>
<Custom Action="VerifyTypeAgentShell" After="ShellProgress3">(NOT REMOVE~="ALL") AND (SHELL="1")</Custom>
<Custom Action="LaunchCopilotSetup" After="InstallFinalize">(NOT Installed) AND (PROVIDER="COPILOT") AND (UILevel &gt;= 4)</Custom>
<Custom Action="DisableAutostart" Before="UninstallVsCodeShell">REMOVE~="ALL"</Custom>
<Custom Action="UninstallVsCodeShell" Before="UninstallVsCodeChat">REMOVE~="ALL"</Custom>
<Custom Action="UninstallVsCodeChat" Before="UnregisterCopilotPlugin">REMOVE~="ALL"</Custom>
Expand Down
Loading
Loading